cpl 0.6.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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