Class: Command::Base

Inherits:
Object
  • Object
show all
Includes:
Helpers
Defined in:
lib/command/base.rb

Overview

rubocop:disable Metrics/ClassLength

Constant Summary collapse

USAGE =

Used to call the command (‘cpl NAME`) NAME = “” Displayed when running `cpl help` or `cpl help NAME` (defaults to `NAME`)

""
REQUIRES_ARGS =

Throws error if ‘true` and no arguments are passed to the command or if `false` and arguments are passed to the command

false
DEFAULT_ARGS =

Default arguments if none are passed to the command

[].freeze
OPTIONS =

Options for the command (use option methods below)

[].freeze
ACCEPTS_EXTRA_OPTIONS =

Does not throw error if ‘true` and extra options that are not specified in `OPTIONS` are passed to the command

false
EXAMPLES =

Displayed when running ‘cpl help` DESCRIPTION = “” Displayed when running `cpl help NAME` LONG_DESCRIPTION = “” Displayed along with `LONG_DESCRIPTION` when running `cpl help NAME`

""
HIDE =

If ‘true`, hides the command from `cpl help`

false
WITH_INFO_HEADER =

Whether or not to show key information like ORG and APP name in commands

true
NO_IMAGE_AVAILABLE =
"NO_IMAGE_AVAILABLE"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Helpers

#random_four_digits, #strip_str_and_validate

Constructor Details

#initialize(config) ⇒ Base

Returns a new instance of Base.



38
39
40
# File 'lib/command/base.rb', line 38

def initialize(config)
  @config = config
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



7
8
9
# File 'lib/command/base.rb', line 7

def config
  @config
end

Class Method Details

.all_commandsObject



42
43
44
45
46
47
48
# File 'lib/command/base.rb', line 42

def self.all_commands
  Dir["#{__dir__}/*.rb"].each_with_object({}) do |file, result|
    filename = File.basename(file, ".rb")
    classname = File.read(file).match(/^\s+class (\w+) < Base($| .*$)/)&.captures&.first
    result[filename.to_sym] = Object.const_get("::Command::#{classname}") if classname
  end
end

.all_optionsObject



274
275
276
# File 'lib/command/base.rb', line 274

def self.all_options
  methods.grep(/_option$/).map { |method| send(method.to_s) }
end

.all_options_by_key_nameObject



278
279
280
281
282
283
# File 'lib/command/base.rb', line 278

def self.all_options_by_key_name
  all_options.each_with_object({}) do |option, result|
    option[:params][:aliases]&.each { |current_alias| result[current_alias.to_s] = option }
    result["--#{option[:name]}"] = option
  end
end

.app_option(required: false) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/command/base.rb', line 67

def self.app_option(required: false)
  {
    name: :app,
    params: {
      aliases: ["-a"],
      banner: "APP_NAME",
      desc: "Application name",
      type: :string,
      required: required
    }
  }
end

.clean_on_failure_option(required: false) ⇒ Object



240
241
242
243
244
245
246
247
248
249
250
# File 'lib/command/base.rb', line 240

def self.clean_on_failure_option(required: false)
  {
    name: :clean_on_failure,
    params: {
      desc: "Deletes workload when finished with failure (success always deletes)",
      type: :boolean,
      required: required,
      default: true
    }
  }
end

.commit_option(required: false) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/command/base.rb', line 106

def self.commit_option(required: false)
  {
    name: :commit,
    params: {
      aliases: ["-c"],
      banner: "COMMIT_HASH",
      desc: "Commit hash",
      type: :string,
      required: required
    }
  }
end

.common_optionsObject



50
51
52
# File 'lib/command/base.rb', line 50

def self.common_options
  [org_option, verbose_option, trace_option]
end

.domain_option(required: false) ⇒ Object



132
133
134
135
136
137
138
139
140
141
142
# File 'lib/command/base.rb', line 132

def self.domain_option(required: false)
  {
    name: :domain,
    params: {
      banner: "DOMAIN_NAME",
      desc: "Domain name",
      type: :string,
      required: required
    }
  }
end

.image_option(required: false) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/command/base.rb', line 93

def self.image_option(required: false)
  {
    name: :image,
    params: {
      aliases: ["-i"],
      banner: "IMAGE_NAME",
      desc: "Image name",
      type: :string,
      required: required
    }
  }
end

.location_option(required: false) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/command/base.rb', line 119

def self.location_option(required: false)
  {
    name: :location,
    params: {
      aliases: ["-l"],
      banner: "LOCATION_NAME",
      desc: "Location name",
      type: :string,
      required: required
    }
  }
end

.org_option(required: false) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/command/base.rb', line 54

def self.org_option(required: false)
  {
    name: :org,
    params: {
      aliases: ["-o"],
      banner: "ORG_NAME",
      desc: "Organization name",
      type: :string,
      required: required
    }
  }
end

.run_release_phase_option(required: false) ⇒ Object



263
264
265
266
267
268
269
270
271
272
# File 'lib/command/base.rb', line 263

def self.run_release_phase_option(required: false)
  {
    name: :run_release_phase,
    params: {
      desc: "Runs release phase",
      type: :boolean,
      required: required
    }
  }
end

.skip_confirm_option(required: false) ⇒ Object



157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/command/base.rb', line 157

def self.skip_confirm_option(required: false)
  {
    name: :yes,
    params: {
      aliases: ["-y"],
      banner: "SKIP_CONFIRM",
      desc: "Skip confirmation",
      type: :boolean,
      required: required
    }
  }
end

.skip_secret_access_binding_option(required: false) ⇒ Object



252
253
254
255
256
257
258
259
260
261
# File 'lib/command/base.rb', line 252

def self.skip_secret_access_binding_option(required: false)
  {
    name: :skip_secret_access_binding,
    params: {
      desc: "Skips secret access binding",
      type: :boolean,
      required: required
    }
  }
end

.terminal_size_option(required: false) ⇒ Object



194
195
196
197
198
199
200
201
202
203
204
# File 'lib/command/base.rb', line 194

def self.terminal_size_option(required: false)
  {
    name: :terminal_size,
    params: {
      banner: "ROWS,COLS",
      desc: "Override remote terminal size (e.g. `--terminal-size 10,20`)",
      type: :string,
      required: required
    }
  }
end

.trace_option(required: false) ⇒ Object



229
230
231
232
233
234
235
236
237
238
# File 'lib/command/base.rb', line 229

def self.trace_option(required: false)
  {
    name: :trace,
    params: {
      desc: "Shows trace of API calls. WARNING: may contain sensitive data",
      type: :boolean,
      required: required
    }
  }
end

.upstream_token_option(required: false) ⇒ Object



144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/command/base.rb', line 144

def self.upstream_token_option(required: false)
  {
    name: :upstream_token,
    params: {
      aliases: ["-t"],
      banner: "UPSTREAM_TOKEN",
      desc: "Upstream token",
      type: :string,
      required: required
    }
  }
end

.use_local_token_option(required: false) ⇒ Object



183
184
185
186
187
188
189
190
191
192
# File 'lib/command/base.rb', line 183

def self.use_local_token_option(required: false)
  {
    name: :use_local_token,
    params: {
      desc: "Override remote CPLN_TOKEN with local token",
      type: :boolean,
      required: required
    }
  }
end

.verbose_option(required: false) ⇒ Object



217
218
219
220
221
222
223
224
225
226
227
# File 'lib/command/base.rb', line 217

def self.verbose_option(required: false)
  {
    name: :verbose,
    params: {
      aliases: ["-d"],
      desc: "Shows detailed logs",
      type: :boolean,
      required: required
    }
  }
end

.version_option(required: false) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/command/base.rb', line 170

def self.version_option(required: false)
  {
    name: :version,
    params: {
      aliases: ["-v"],
      banner: "VERSION",
      desc: "Displays the current version of the CLI",
      type: :boolean,
      required: required
    }
  }
end

.wait_option(title = "", required: false) ⇒ Object



206
207
208
209
210
211
212
213
214
215
# File 'lib/command/base.rb', line 206

def self.wait_option(title = "", required: false)
  {
    name: :wait,
    params: {
      desc: "Waits for #{title}",
      type: :boolean,
      required: required
    }
  }
end

.workload_option(required: false) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/command/base.rb', line 80

def self.workload_option(required: false)
  {
    name: :workload,
    params: {
      aliases: ["-w"],
      banner: "WORKLOAD_NAME",
      desc: "Workload name",
      type: :string,
      required: required
    }
  }
end

Instance Method Details

#app_identityObject



409
410
411
# File 'lib/command/base.rb', line 409

def app_identity
  "#{config.app}-identity"
end


413
414
415
# File 'lib/command/base.rb', line 413

def app_identity_link
  "/org/#{config.org}/gvc/#{config.app}/identity/#{app_identity}"
end


405
406
407
# File 'lib/command/base.rb', line 405

def app_image_link
  "/org/#{config.org}/image/#{latest_image}"
end


401
402
403
# File 'lib/command/base.rb', line 401

def app_location_link
  "/org/#{config.org}/location/#{config.location}"
end

#app_secretsObject



417
418
419
# File 'lib/command/base.rb', line 417

def app_secrets
  "#{config.app_prefix}-secrets"
end

#app_secrets_policyObject



421
422
423
# File 'lib/command/base.rb', line 421

def app_secrets_policy
  "#{app_secrets}-policy"
end

#args_join(args) ⇒ Object

NOTE: use simplified variant atm, as shelljoin do different escaping TODO: most probably need better logic for escaping various quotes



344
345
346
# File 'lib/command/base.rb', line 344

def args_join(args)
  args.join(" ")
end

#cpObject



393
394
395
# File 'lib/command/base.rb', line 393

def cp
  @cp ||= Controlplane.new(config)
end

#ensure_workload_deleted(workload) ⇒ Object



297
298
299
300
301
# File 'lib/command/base.rb', line 297

def ensure_workload_deleted(workload)
  step("Deleting workload") do
    cp.delete_workload(workload)
  end
end

#extract_image_commit(image_name) ⇒ Object



338
339
340
# File 'lib/command/base.rb', line 338

def extract_image_commit(image_name)
  image_name.match(/_(\h+)$/)&.captures&.first
end

#latest_image(app = config.app, org = config.org) ⇒ Object



315
316
317
318
319
320
321
322
# File 'lib/command/base.rb', line 315

def latest_image(app = config.app, org = config.org)
  @latest_image ||= {}
  @latest_image[app] ||=
    begin
      items = cp.query_images(app, org)["items"]
      latest_image_from(items, app_name: app)
    end
end

#latest_image_from(items, app_name: config.app, name_only: true) ⇒ Object



303
304
305
306
307
308
309
310
311
312
313
# File 'lib/command/base.rb', line 303

def latest_image_from(items, app_name: config.app, name_only: true)
  matching_items = items.select { |item| item["name"].start_with?("#{app_name}:") }

  # Or special string to indicate no image available
  if matching_items.empty?
    name_only ? "#{app_name}:#{NO_IMAGE_AVAILABLE}" : nil
  else
    latest_item = matching_items.max_by { |item| extract_image_number(item["name"]) }
    name_only ? latest_item["name"] : latest_item
  end
end

#latest_image_next(app = config.app, org = config.org, commit: nil) ⇒ Object



324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/command/base.rb', line 324

def latest_image_next(app = config.app, org = config.org, commit: nil)
  # debugger
  commit ||= config.options[:commit]

  @latest_image_next ||= {}
  @latest_image_next[app] ||= begin
    latest_image_name = latest_image(app, org)
    image = latest_image_name.split(":").first
    image += ":#{extract_image_number(latest_image_name) + 1}"
    image += "_#{commit}" if commit
    image
  end
end

#perform!(cmd) ⇒ Object



397
398
399
# File 'lib/command/base.rb', line 397

def perform!(cmd)
  system(cmd) || exit(1)
end

#progressObject



348
349
350
# File 'lib/command/base.rb', line 348

def progress
  $stderr
end

#step(message, abort_on_error: true, retry_on_failure: false) ⇒ Object

rubocop:disable Metrics/MethodLength



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
# File 'lib/command/base.rb', line 370

def step(message, abort_on_error: true, retry_on_failure: false) # rubocop:disable Metrics/MethodLength
  progress.print("#{message}...")

  Shell.use_tmp_stderr do
    success = false

    begin
      if retry_on_failure
        until (success = yield)
          progress.print(".")
          sleep 1
        end
      else
        success = yield
      end
    rescue RuntimeError => e
      step_error(e, abort_on_error: abort_on_error)
    end

    step_finish(success)
  end
end

#step_error(error, abort_on_error: true) ⇒ Object



352
353
354
355
356
357
358
359
360
# File 'lib/command/base.rb', line 352

def step_error(error, abort_on_error: true)
  message = error.message
  if abort_on_error
    progress.puts(" #{Shell.color('failed!', :red)}\n\n")
    Shell.abort(message)
  else
    Shell.write_to_tmp_stderr(message)
  end
end

#step_finish(success) ⇒ Object



362
363
364
365
366
367
368
# File 'lib/command/base.rb', line 362

def step_finish(success)
  if success
    progress.puts(" #{Shell.color('done!', :green)}")
  else
    progress.puts(" #{Shell.color('failed!', :red)}\n\n#{Shell.read_from_tmp_stderr}\n\n")
  end
end

#wait_for_replica(workload, location) ⇒ Object



291
292
293
294
295
# File 'lib/command/base.rb', line 291

def wait_for_replica(workload, location)
  step("Waiting for replica", retry_on_failure: true) do
    cp.workload_get_replicas_safely(workload, location: location)&.dig("items", 0)
  end
end

#wait_for_workload(workload) ⇒ Object



285
286
287
288
289
# File 'lib/command/base.rb', line 285

def wait_for_workload(workload)
  step("Waiting for workload", retry_on_failure: true) do
    cp.fetch_workload(workload)
  end
end