cpl 0.6.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/command/info.rb CHANGED
@@ -90,7 +90,7 @@ module Command
90
90
  config.apps.each do |app_name, app_options|
91
91
  next if config.app && !app_matches?(config.app, app_name, app_options)
92
92
 
93
- org = app_options[:cpln_org] || app_options[:org]
93
+ org = app_options[:cpln_org]
94
94
  result.push(org) unless result.include?(org)
95
95
  end
96
96
  end
@@ -104,7 +104,7 @@ module Command
104
104
  config.apps.each do |app_name, app_options|
105
105
  next if config.app && !app_matches?(config.app, app_name, app_options)
106
106
 
107
- app_org = app_options[:cpln_org] || app_options[:org]
107
+ app_org = app_options[:cpln_org]
108
108
  result.push(app_name.to_s) if app_org == org
109
109
  end
110
110
 
@@ -173,7 +173,11 @@ module Command
173
173
  puts "\nSome apps/workloads are missing. Please create them with:"
174
174
 
175
175
  @missing_apps_workloads.each do |app, workloads|
176
- puts " - `cpl setup #{workloads.join(' ')} -a #{app}`"
176
+ if workloads.include?("gvc")
177
+ puts " - `cpl setup-app -a #{app}`"
178
+ else
179
+ puts " - `cpl apply-template #{workloads.join(' ')} -a #{app}`"
180
+ end
177
181
  end
178
182
  end
179
183
 
@@ -183,9 +187,9 @@ module Command
183
187
  puts "\nThere are no apps starting with some names. If you wish to create any, do so with " \
184
188
  "(replace 'whatever' with whatever suffix you want):"
185
189
 
186
- @missing_apps_starting_with.each do |app, workloads|
190
+ @missing_apps_starting_with.each do |app, _workloads|
187
191
  app_with_suffix = "#{app}#{app.end_with?('-') ? '' : '-'}whatever"
188
- puts " - `cpl setup #{workloads.join(' ')} -a #{app_with_suffix}`"
192
+ puts " - `cpl setup-app -a #{app_with_suffix}`"
189
193
  end
190
194
  end
191
195
 
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class Maintenance < Base
5
+ NAME = "maintenance"
6
+ OPTIONS = [
7
+ app_option(required: true)
8
+ ].freeze
9
+ DESCRIPTION = "Checks if maintenance mode is on or off for an app"
10
+ LONG_DESCRIPTION = <<~DESC
11
+ - Checks if maintenance mode is on or off for an app
12
+ - Outputs 'on' or 'off'
13
+ - Specify the one-off workload through `one_off_workload` in the `.controlplane/controlplane.yml` file
14
+ - Optionally specify the maintenance workload through `maintenance_workload` in the `.controlplane/controlplane.yml` file (defaults to 'maintenance')
15
+ - Maintenance mode is only supported for domains that use path based routing mode and have a route configured for the prefix '/' on either port 80 or 443
16
+ DESC
17
+
18
+ def call # rubocop:disable Metrics/MethodLength
19
+ one_off_workload = config[:one_off_workload]
20
+ maintenance_workload = config.current[:maintenance_workload] || "maintenance"
21
+
22
+ domain_data = cp.find_domain_for([one_off_workload, maintenance_workload])
23
+ unless domain_data
24
+ raise "Can't find domain. " \
25
+ "Maintenance mode is only supported for domains that use path based routing mode " \
26
+ "and have a route configured for the prefix '/' on either port 80 or 443."
27
+ end
28
+
29
+ domain_workload = cp.get_domain_workload(domain_data)
30
+ if domain_workload == maintenance_workload
31
+ puts "on"
32
+ else
33
+ puts "off"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class MaintenanceOff < Base
5
+ NAME = "maintenance:off"
6
+ OPTIONS = [
7
+ app_option(required: true)
8
+ ].freeze
9
+ DESCRIPTION = "Disables maintenance mode for an app"
10
+ LONG_DESCRIPTION = <<~DESC
11
+ - Disables maintenance mode for an app
12
+ - Specify the one-off workload through `one_off_workload` in the `.controlplane/controlplane.yml` file
13
+ - Optionally specify the maintenance workload through `maintenance_workload` in the `.controlplane/controlplane.yml` file (defaults to 'maintenance')
14
+ - Maintenance mode is only supported for domains that use path based routing mode and have a route configured for the prefix '/' on either port 80 or 443
15
+ DESC
16
+
17
+ def call # rubocop:disable Metrics/MethodLength
18
+ one_off_workload = config[:one_off_workload]
19
+ maintenance_workload = config.current[:maintenance_workload] || "maintenance"
20
+
21
+ domain_data = cp.find_domain_for([one_off_workload, maintenance_workload])
22
+ unless domain_data
23
+ raise "Can't find domain. " \
24
+ "Maintenance mode is only supported for domains that use path based routing mode " \
25
+ "and have a route configured for the prefix '/' on either port 80 or 443."
26
+ end
27
+
28
+ domain = domain_data["name"]
29
+ domain_workload = cp.get_domain_workload(domain_data)
30
+ if domain_workload == one_off_workload
31
+ progress.puts("Maintenance mode is already disabled for app '#{config.app}'.")
32
+ return
33
+ end
34
+
35
+ cp.fetch_workload!(maintenance_workload)
36
+
37
+ # Start all other workloads
38
+ Cpl::Cli.start(["ps:start", "-a", config.app, "--wait"])
39
+
40
+ progress.puts
41
+
42
+ # Switch domain workload
43
+ step("Switching workload for domain '#{domain}' to '#{one_off_workload}'") do
44
+ cp.set_domain_workload(domain_data, one_off_workload)
45
+
46
+ # Give it a bit of time for the domain to update
47
+ sleep 30
48
+ end
49
+
50
+ progress.puts
51
+
52
+ # Stop maintenance workload
53
+ Cpl::Cli.start(["ps:stop", "-a", config.app, "-w", maintenance_workload, "--wait"])
54
+
55
+ progress.puts("\nMaintenance mode disabled for app '#{config.app}'.")
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class MaintenanceOn < Base
5
+ NAME = "maintenance:on"
6
+ OPTIONS = [
7
+ app_option(required: true)
8
+ ].freeze
9
+ DESCRIPTION = "Enables maintenance mode for an app"
10
+ LONG_DESCRIPTION = <<~DESC
11
+ - Enables maintenance mode for an app
12
+ - Specify the one-off workload through `one_off_workload` in the `.controlplane/controlplane.yml` file
13
+ - Optionally specify the maintenance workload through `maintenance_workload` in the `.controlplane/controlplane.yml` file (defaults to 'maintenance')
14
+ - Maintenance mode is only supported for domains that use path based routing mode and have a route configured for the prefix '/' on either port 80 or 443
15
+ DESC
16
+
17
+ def call # rubocop:disable Metrics/MethodLength
18
+ one_off_workload = config[:one_off_workload]
19
+ maintenance_workload = config.current[:maintenance_workload] || "maintenance"
20
+
21
+ domain_data = cp.find_domain_for([one_off_workload, maintenance_workload])
22
+ unless domain_data
23
+ raise "Can't find domain. " \
24
+ "Maintenance mode is only supported for domains that use path based routing mode " \
25
+ "and have a route configured for the prefix '/' on either port 80 or 443."
26
+ end
27
+
28
+ domain = domain_data["name"]
29
+ domain_workload = cp.get_domain_workload(domain_data)
30
+ if domain_workload == maintenance_workload
31
+ progress.puts("Maintenance mode is already enabled for app '#{config.app}'.")
32
+ return
33
+ end
34
+
35
+ cp.fetch_workload!(maintenance_workload)
36
+
37
+ # Start maintenance workload
38
+ Cpl::Cli.start(["ps:start", "-a", config.app, "-w", maintenance_workload, "--wait"])
39
+
40
+ progress.puts
41
+
42
+ # Switch domain workload
43
+ step("Switching workload for domain '#{domain}' to '#{maintenance_workload}'") do
44
+ cp.set_domain_workload(domain_data, maintenance_workload)
45
+
46
+ # Give it a bit of time for the domain to update
47
+ sleep 30
48
+ end
49
+
50
+ progress.puts
51
+
52
+ # Stop all other workloads
53
+ Cpl::Cli.start(["ps:stop", "-a", config.app, "--wait"])
54
+
55
+ progress.puts("\nMaintenance mode enabled for app '#{config.app}'.")
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class MaintenanceSetPage < Base
5
+ NAME = "maintenance:set-page"
6
+ USAGE = "maintenance:set-page PAGE_URL"
7
+ REQUIRES_ARGS = true
8
+ OPTIONS = [
9
+ app_option(required: true)
10
+ ].freeze
11
+ DESCRIPTION = "Sets the page for maintenance mode"
12
+ LONG_DESCRIPTION = <<~DESC
13
+ - Sets the page for maintenance mode
14
+ - Only works if the maintenance workload uses the `shakacode/maintenance-mode` image
15
+ - Will set the URL as an env var `PAGE_URL` on the maintenance workload
16
+ - Optionally specify the maintenance workload through `maintenance_workload` in the `.controlplane/controlplane.yml` file (defaults to 'maintenance')
17
+ DESC
18
+
19
+ def call
20
+ maintenance_workload = config.current[:maintenance_workload] || "maintenance"
21
+
22
+ maintenance_workload_data = cp.fetch_workload!(maintenance_workload)
23
+ maintenance_workload_data.dig("spec", "containers").each do |container|
24
+ next unless container["image"].match?(%r{^shakacode/maintenance-mode})
25
+
26
+ container_name = container["name"]
27
+ page_url = config.args.first
28
+ step("Setting '#{page_url}' as the page for maintenance mode") do
29
+ cp.set_workload_env_var(maintenance_workload, container: container_name, name: "PAGE_URL", value: page_url)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -13,7 +13,7 @@ module Command
13
13
  def call
14
14
  return unless config.options[:version]
15
15
 
16
- perform("cpl version")
16
+ Cpl::Cli.start(["version"])
17
17
  end
18
18
  end
19
19
  end
@@ -39,7 +39,7 @@ module Command
39
39
  end
40
40
 
41
41
  def copy_image_from_upstream
42
- perform("cpl copy-image-from-upstream -a #{config.app} -t #{config.options[:upstream_token]}")
42
+ Cpl::Cli.start(["copy-image-from-upstream", "-a", config.app, "-t", config.options[:upstream_token]])
43
43
  progress.puts
44
44
  end
45
45
 
@@ -52,7 +52,7 @@ module Command
52
52
  end
53
53
 
54
54
  def deploy_image
55
- perform("cpl deploy-image -a #{config.app}")
55
+ Cpl::Cli.start(["deploy-image", "-a", config.app])
56
56
  end
57
57
  end
58
58
  end
@@ -5,7 +5,8 @@ module Command
5
5
  NAME = "ps:start"
6
6
  OPTIONS = [
7
7
  app_option(required: true),
8
- workload_option
8
+ workload_option,
9
+ wait_option("workload to be ready")
9
10
  ].freeze
10
11
  DESCRIPTION = "Starts workloads in app"
11
12
  LONG_DESCRIPTION = <<~DESC
@@ -22,12 +23,28 @@ module Command
22
23
  EX
23
24
 
24
25
  def call
25
- workloads = [config.options[:workload]] if config.options[:workload]
26
- workloads ||= config[:app_workloads] + config[:additional_workloads]
26
+ @workloads = [config.options[:workload]] if config.options[:workload]
27
+ @workloads ||= config[:app_workloads] + config[:additional_workloads]
27
28
 
28
- workloads.reverse_each do |workload|
29
+ @workloads.reverse_each do |workload|
29
30
  step("Starting workload '#{workload}'") do
30
- cp.workload_set_suspend(workload, false)
31
+ cp.set_workload_suspend(workload, false)
32
+ end
33
+ end
34
+
35
+ wait_for_ready if config.options[:wait]
36
+ end
37
+
38
+ private
39
+
40
+ def wait_for_ready
41
+ progress.puts
42
+
43
+ @workloads.reverse_each do |workload|
44
+ step("Waiting for workload '#{workload}' to be ready", retry_on_failure: true) do
45
+ cp.fetch_workload_deployments(workload)&.dig("items")&.any? do |item|
46
+ item.dig("status", "ready")
47
+ end
31
48
  end
32
49
  end
33
50
  end
@@ -5,7 +5,8 @@ module Command
5
5
  NAME = "ps:stop"
6
6
  OPTIONS = [
7
7
  app_option(required: true),
8
- workload_option
8
+ workload_option,
9
+ wait_option("workload to be not ready")
9
10
  ].freeze
10
11
  DESCRIPTION = "Stops workloads in app"
11
12
  LONG_DESCRIPTION = <<~DESC
@@ -22,12 +23,28 @@ module Command
22
23
  EX
23
24
 
24
25
  def call
25
- workloads = [config.options[:workload]] if config.options[:workload]
26
- workloads ||= config[:app_workloads] + config[:additional_workloads]
26
+ @workloads = [config.options[:workload]] if config.options[:workload]
27
+ @workloads ||= config[:app_workloads] + config[:additional_workloads]
27
28
 
28
- workloads.each do |workload|
29
+ @workloads.each do |workload|
29
30
  step("Stopping workload '#{workload}'") do
30
- cp.workload_set_suspend(workload, true)
31
+ cp.set_workload_suspend(workload, true)
32
+ end
33
+ end
34
+
35
+ wait_for_not_ready if config.options[:wait]
36
+ end
37
+
38
+ private
39
+
40
+ def wait_for_not_ready
41
+ progress.puts
42
+
43
+ @workloads.each do |workload|
44
+ step("Waiting for workload '#{workload}' to be not ready", retry_on_failure: true) do
45
+ cp.fetch_workload_deployments(workload)&.dig("items")&.all? do |item|
46
+ !item.dig("status", "ready")
47
+ end
31
48
  end
32
49
  end
33
50
  end
data/lib/command/run.rb CHANGED
@@ -10,13 +10,15 @@ module Command
10
10
  app_option(required: true),
11
11
  image_option,
12
12
  workload_option,
13
- use_local_token_option
13
+ use_local_token_option,
14
+ terminal_size_option
14
15
  ].freeze
15
16
  DESCRIPTION = "Runs one-off **_interactive_** replicas (analog of `heroku run`)"
16
17
  LONG_DESCRIPTION = <<~DESC
17
18
  - Runs one-off **_interactive_** replicas (analog of `heroku run`)
18
19
  - Uses `Standard` workload type and `cpln exec` as the execution method, with CLI streaming
19
20
  - May not work correctly with tasks that last over 5 minutes (there's a Control Plane scaling bug at the moment)
21
+ - If `fix_terminal_size` is `true` in the `.controlplane/controlplane.yml` file, the remote terminal size will be fixed to match the local terminal size (may also be overriden through `--terminal-size`)
20
22
 
21
23
  > **IMPORTANT:** Useful for development where it's needed for interaction, and where network connection drops and
22
24
  > task crashing are tolerable. For production tasks, it's better to use `cpl run:detached`.
@@ -26,6 +28,12 @@ module Command
26
28
  # Opens shell (bash by default).
27
29
  cpl run -a $APP_NAME
28
30
 
31
+ # Need to quote COMMAND if setting ENV value or passing args.
32
+ cpl run 'LOG_LEVEL=warn rails db:migrate' -a $APP_NAME
33
+
34
+ # COMMAND may also be passed at the end (in this case, no need to quote).
35
+ cpl run -a $APP_NAME -- rails db:migrate
36
+
29
37
  # Runs command, displays output, and exits shell.
30
38
  cpl run ls / -a $APP_NAME
31
39
  cpl run rails db:migrate:status -a $APP_NAME
@@ -37,35 +45,37 @@ module Command
37
45
  cpl run rails db:migrate -a $APP_NAME --image appimage:123 # Exact image name
38
46
  cpl run rails db:migrate -a $APP_NAME --image latest # Latest sequential image
39
47
 
40
- # Uses a different workload
48
+ # Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
41
49
  cpl run bash -a $APP_NAME -w other-workload
42
50
 
43
51
  # Overrides remote CPLN_TOKEN env variable with local token.
44
- # Useful when need superuser rights in remote container
52
+ # Useful when superuser rights are needed in remote container.
45
53
  cpl run bash -a $APP_NAME --use-local-token
46
54
  ```
47
55
  EX
48
56
 
49
57
  attr_reader :location, :workload, :one_off, :container
50
58
 
51
- def call
59
+ def call # rubocop:disable Metrics/MethodLength
52
60
  @location = config[:default_location]
53
61
  @workload = config.options["workload"] || config[:one_off_workload]
54
62
  @one_off = "#{workload}-run-#{rand(1000..9999)}"
55
63
 
56
- clone_workload
64
+ step("Cloning workload '#{workload}' on app '#{config.options[:app]}' to '#{one_off}'") do
65
+ clone_workload
66
+ end
67
+
57
68
  wait_for_workload(one_off)
58
69
  wait_for_replica(one_off, location)
59
70
  run_in_replica
60
71
  ensure
72
+ progress.puts
61
73
  ensure_workload_deleted(one_off)
62
74
  end
63
75
 
64
76
  private
65
77
 
66
78
  def clone_workload # rubocop:disable Metrics/MethodLength
67
- progress.puts "- Cloning workload '#{workload}' on '#{config.options[:app]}' to '#{one_off}'"
68
-
69
79
  # Create a base copy of workload props
70
80
  spec = cp.fetch_workload!(workload).fetch("spec")
71
81
  container_spec = spec["containers"].detect { _1["name"] == workload } || spec["containers"].first
@@ -84,7 +94,7 @@ module Command
84
94
 
85
95
  # Ensure no scaling
86
96
  spec["defaultOptions"]["autoscaling"]["minScale"] = 1
87
- spec["defaultOptions"]["autoscaling"]["minScale"] = 1
97
+ spec["defaultOptions"]["autoscaling"]["maxScale"] = 1
88
98
  spec["defaultOptions"]["capacityAI"] = false
89
99
 
90
100
  # Override image if specified
@@ -104,7 +114,7 @@ module Command
104
114
  cp.apply("kind" => "workload", "name" => one_off, "spec" => spec)
105
115
  end
106
116
 
107
- def runner_script
117
+ def runner_script # rubocop:disable Metrics/MethodLength
108
118
  script = Scripts.helpers_cleanup
109
119
 
110
120
  if config.options["use_local_token"]
@@ -114,12 +124,22 @@ module Command
114
124
  SHELL
115
125
  end
116
126
 
127
+ # NOTE: fixes terminal size to match local terminal
128
+ if config.current[:fix_terminal_size] || config.options[:terminal_size]
129
+ if config.options[:terminal_size]
130
+ rows, cols = config.options[:terminal_size].split(",")
131
+ else
132
+ rows, cols = `stty size`.split(/\s+/)
133
+ end
134
+ script += "stty rows #{rows}\nstty cols #{cols}\n" if rows && cols
135
+ end
136
+
117
137
  script += args_join(config.args)
118
138
  script
119
139
  end
120
140
 
121
141
  def run_in_replica
122
- progress.puts "- Connecting"
142
+ progress.puts("Connecting...\n\n")
123
143
  command = %(bash -c 'eval "$CONTROLPLANE_RUNNER"')
124
144
  cp.workload_exec(one_off, location: location, container: container, command: command)
125
145
  end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class RunCleanup < Base
5
+ NAME = "run:cleanup"
6
+ OPTIONS = [
7
+ app_option(required: true),
8
+ skip_confirm_option
9
+ ].freeze
10
+ DESCRIPTION = "Deletes stale run workloads for an app"
11
+ LONG_DESCRIPTION = <<~DESC
12
+ - Deletes stale run workloads for an app
13
+ - Workloads are considered stale based on how many days since created
14
+ - `stale_run_workload_created_days` in the `.controlplane/controlplane.yml` file specifies the number of days after created that the workload is considered stale
15
+ - Will ask for explicit user confirmation of deletion
16
+ DESC
17
+
18
+ def call # rubocop:disable Metrics/MethodLength
19
+ return progress.puts("No stale run workloads found.") if stale_run_workloads.empty?
20
+
21
+ progress.puts("Stale run workloads:")
22
+ stale_run_workloads.each do |workload|
23
+ progress.puts(" #{workload[:name]} " \
24
+ "(#{Shell.color("#{workload[:date]} - #{workload[:days]} days ago", :red)})")
25
+ end
26
+
27
+ return unless confirm_delete
28
+
29
+ progress.puts
30
+ stale_run_workloads.each do |workload|
31
+ delete_workload(workload)
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def app_matches?(app, app_name, app_options)
38
+ app == app_name.to_s || (app_options[:match_if_app_name_starts_with] && app.start_with?(app_name.to_s))
39
+ end
40
+
41
+ def find_app_options(app)
42
+ @app_options ||= {}
43
+ @app_options[app] ||= config.apps.find do |app_name, app_options|
44
+ app_matches?(app, app_name, app_options)
45
+ end&.last
46
+ end
47
+
48
+ def find_workloads(app)
49
+ app_options = find_app_options(app)
50
+ return [] if app_options.nil?
51
+
52
+ (app_options[:app_workloads] + app_options[:additional_workloads] + [app_options[:one_off_workload]]).uniq
53
+ end
54
+
55
+ def stale_run_workloads # rubocop:disable Metrics/MethodLength
56
+ @stale_run_workloads ||=
57
+ begin
58
+ defined_workloads = find_workloads(config.app)
59
+
60
+ run_workloads = []
61
+
62
+ now = DateTime.now
63
+ stale_run_workload_created_days = config[:stale_run_workload_created_days]
64
+
65
+ workloads = cp.query_workloads("-run-", partial_match: true)["items"]
66
+ workloads.each do |workload|
67
+ workload_name = workload["name"]
68
+
69
+ original_workload_name, workload_number = workload_name.split("-run-")
70
+ next unless defined_workloads.include?(original_workload_name) && workload_number.match?(/^\d{4}$/)
71
+
72
+ created_date = DateTime.parse(workload["created"])
73
+ diff_in_days = (now - created_date).to_i
74
+ next unless diff_in_days >= stale_run_workload_created_days
75
+
76
+ run_workloads.push({
77
+ name: workload_name,
78
+ date: created_date,
79
+ days: diff_in_days
80
+ })
81
+ end
82
+
83
+ run_workloads
84
+ end
85
+ end
86
+
87
+ def confirm_delete
88
+ return true if config.options[:yes]
89
+
90
+ Shell.confirm("\nAre you sure you want to delete these #{stale_run_workloads.length} run workloads?")
91
+ end
92
+
93
+ def delete_workload(workload)
94
+ step("Deleting run workload '#{workload[:name]}'") do
95
+ cp.delete_workload(workload[:name])
96
+ end
97
+ end
98
+ end
99
+ end
@@ -22,9 +22,12 @@ module Command
22
22
  ```sh
23
23
  cpl run:detached rails db:prepare -a $APP_NAME
24
24
 
25
- # Need to quote COMMAND if setting ENV value or passing args to command to run
25
+ # Need to quote COMMAND if setting ENV value or passing args.
26
26
  cpl run:detached 'LOG_LEVEL=warn rails db:migrate' -a $APP_NAME
27
27
 
28
+ # COMMAND may also be passed at the end (in this case, no need to quote).
29
+ cpl run:detached -a $APP_NAME -- rails db:migrate
30
+
28
31
  # Uses some other image.
29
32
  cpl run:detached rails db:migrate -a $APP_NAME --image /some/full/image/path
30
33
 
@@ -35,7 +38,7 @@ module Command
35
38
  cpl run:detached rails db:migrate -a $APP_NAME --image appimage:123 # Exact image name
36
39
  cpl run:detached rails db:migrate -a $APP_NAME --image latest # Latest sequential image
37
40
 
38
- # Uses a different workload
41
+ # Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
39
42
  cpl run:detached rails db:migrate:status -a $APP_NAME -w other-workload
40
43
  ```
41
44
  EX
@@ -44,24 +47,28 @@ module Command
44
47
 
45
48
  attr_reader :location, :workload, :one_off, :container
46
49
 
47
- def call
50
+ def call # rubocop:disable Metrics/MethodLength
48
51
  @location = config[:default_location]
49
52
  @workload = config.options["workload"] || config[:one_off_workload]
50
53
  @one_off = "#{workload}-runner-#{rand(1000..9999)}"
51
54
 
52
- clone_workload
55
+ step("Cloning workload '#{workload}' on app '#{config.options[:app]}' to '#{one_off}'") do
56
+ clone_workload
57
+ end
58
+
53
59
  wait_for_workload(one_off)
54
60
  show_logs_waiting
55
61
  ensure
56
- ensure_workload_deleted(one_off)
62
+ if cp.fetch_workload(one_off)
63
+ progress.puts
64
+ ensure_workload_deleted(one_off)
65
+ end
57
66
  exit(1) if @crashed
58
67
  end
59
68
 
60
69
  private
61
70
 
62
71
  def clone_workload # rubocop:disable Metrics/MethodLength
63
- progress.puts "- Cloning workload '#{workload}' on '#{config.options[:app]}' to '#{one_off}'"
64
-
65
72
  # Get base specs of workload
66
73
  spec = cp.fetch_workload!(workload).fetch("spec")
67
74
  container_spec = spec["containers"].detect { _1["name"] == workload } || spec["containers"].first
@@ -79,7 +86,7 @@ module Command
79
86
 
80
87
  # Ensure no scaling
81
88
  spec["defaultOptions"]["autoscaling"]["minScale"] = 1
82
- spec["defaultOptions"]["autoscaling"]["minScale"] = 1
89
+ spec["defaultOptions"]["autoscaling"]["maxScale"] = 1
83
90
  spec["defaultOptions"]["capacityAI"] = false
84
91
 
85
92
  # Override image if specified
@@ -118,17 +125,17 @@ module Command
118
125
  end
119
126
 
120
127
  def show_logs_waiting # rubocop:disable Metrics/MethodLength
121
- progress.puts "- Scheduled, fetching logs (it is cron job, so it may take up to a minute to start)"
128
+ progress.puts("Scheduled, fetching logs (it's a cron job, so it may take up to a minute to start)...\n\n")
122
129
  begin
123
130
  while cp.fetch_workload(one_off)
124
131
  sleep(WORKLOAD_SLEEP_CHECK)
125
132
  print_uniq_logs
126
133
  end
127
134
  rescue RuntimeError => e
128
- progress.puts "ERROR: #{e}"
135
+ progress.puts(Shell.color("ERROR: #{e}", :red))
129
136
  retry
130
137
  end
131
- progress.puts "- Finished workload and logger"
138
+ progress.puts("\nFinished workload and logger.")
132
139
  end
133
140
 
134
141
  def print_uniq_logs
@@ -14,16 +14,16 @@ module Command
14
14
  DESC
15
15
 
16
16
  def call
17
- templates = config[:setup].join(" ")
17
+ templates = config[:setup]
18
18
 
19
19
  app = cp.fetch_gvc
20
20
  if app
21
21
  raise "App '#{config.app}' already exists. If you want to update this app, " \
22
22
  "either run 'cpl delete -a #{config.app}' and then re-run this command, " \
23
- "or run 'cpl apply-template #{templates} -a #{config.app}'."
23
+ "or run 'cpl apply-template #{templates.join(' ')} -a #{config.app}'."
24
24
  end
25
25
 
26
- perform("cpl apply-template #{templates} -a #{config.app}")
26
+ Cpl::Cli.start(["apply-template", *templates, "-a", config.app])
27
27
  end
28
28
  end
29
29
  end