cpl 0.5.1 → 0.7.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/base.rb CHANGED
@@ -142,13 +142,47 @@ module Command
142
142
  }
143
143
  end
144
144
 
145
+ def self.use_local_token_option(required: false)
146
+ {
147
+ name: :use_local_token,
148
+ params: {
149
+ desc: "Override remote CPLN_TOKEN with local token",
150
+ type: :boolean,
151
+ required: required
152
+ }
153
+ }
154
+ end
155
+
156
+ def self.terminal_size_option(required: false)
157
+ {
158
+ name: :terminal_size,
159
+ params: {
160
+ banner: "ROWS,COLS",
161
+ desc: "Override remote terminal size (e.g. `--terminal-size 10,20`)",
162
+ type: :string,
163
+ required: required
164
+ }
165
+ }
166
+ end
167
+
168
+ def self.wait_option(title = "", required: false)
169
+ {
170
+ name: :wait,
171
+ params: {
172
+ desc: "Waits for #{title}",
173
+ type: :boolean,
174
+ required: required
175
+ }
176
+ }
177
+ end
178
+
145
179
  def self.all_options
146
180
  methods.grep(/_option$/).map { |method| send(method.to_s) }
147
181
  end
148
182
 
149
183
  def self.all_options_by_key_name
150
184
  all_options.each_with_object({}) do |option, result|
151
- option[:params][:aliases].each { |current_alias| result[current_alias.to_s] = option }
185
+ option[:params][:aliases]&.each { |current_alias| result[current_alias.to_s] = option }
152
186
  result["--#{option[:name]}"] = option
153
187
  end
154
188
  end
@@ -168,13 +202,13 @@ module Command
168
202
 
169
203
  def wait_for_replica(workload, location)
170
204
  wait_for("replica") do
171
- cp.workload_get_replicas(workload, location: location)&.dig("items", 0)
205
+ cp.workload_get_replicas_safely(workload, location: location)&.dig("items", 0)
172
206
  end
173
207
  end
174
208
 
175
209
  def ensure_workload_deleted(workload)
176
210
  progress.puts "- Ensure workload is deleted"
177
- cp.workload_delete(workload)
211
+ cp.delete_workload(workload)
178
212
  end
179
213
 
180
214
  def latest_image_from(items, app_name: config.app, name_only: true)
@@ -201,8 +235,9 @@ module Command
201
235
  def latest_image_next(app = config.app, org = config.org)
202
236
  @latest_image_next ||= {}
203
237
  @latest_image_next[app] ||= begin
204
- image = latest_image(app, org).split(":").first
205
- image += ":#{extract_image_number(latest_image) + 1}"
238
+ latest_image_name = latest_image(app, org)
239
+ image = latest_image_name.split(":").first
240
+ image += ":#{extract_image_number(latest_image_name) + 1}"
206
241
  image += "_#{config.options[:commit]}" if config.options[:commit]
207
242
  image
208
243
  end
@@ -218,29 +253,44 @@ module Command
218
253
  $stderr
219
254
  end
220
255
 
221
- def step(message, abort_on_error: true) # rubocop:disable Metrics/MethodLength
256
+ def step_error(error, abort_on_error: true)
257
+ message = error.message
258
+ if abort_on_error
259
+ progress.puts(" #{Shell.color('failed!', :red)}\n\n")
260
+ Shell.abort(message)
261
+ else
262
+ Shell.write_to_tmp_stderr(message)
263
+ end
264
+ end
265
+
266
+ def step_finish(success)
267
+ if success
268
+ progress.puts(" #{Shell.color('done!', :green)}")
269
+ else
270
+ progress.puts(" #{Shell.color('failed!', :red)}\n\n#{Shell.read_from_tmp_stderr}\n\n")
271
+ end
272
+ end
273
+
274
+ def step(message, abort_on_error: true, retry_on_failure: false) # rubocop:disable Metrics/MethodLength
222
275
  progress.print("#{message}...")
223
276
 
224
277
  Shell.use_tmp_stderr do
225
278
  success = false
226
279
 
227
280
  begin
228
- success = yield
229
- rescue RuntimeError => e
230
- message = e.message
231
- if abort_on_error
232
- progress.puts(" #{Shell.color('failed!', :red)}\n\n")
233
- Shell.abort(message)
281
+ if retry_on_failure
282
+ until (success = yield)
283
+ progress.print(".")
284
+ sleep 1
285
+ end
234
286
  else
235
- Shell.write_to_tmp_stderr(message)
287
+ success = yield
236
288
  end
289
+ rescue RuntimeError => e
290
+ step_error(e, abort_on_error: abort_on_error)
237
291
  end
238
292
 
239
- if success
240
- progress.puts(" #{Shell.color('done!', :green)}")
241
- else
242
- progress.puts(" #{Shell.color('failed!', :red)}\n\n#{Shell.read_from_tmp_stderr}\n\n")
243
- end
293
+ step_finish(success)
244
294
  end
245
295
  end
246
296
 
@@ -11,18 +11,30 @@ module Command
11
11
  LONG_DESCRIPTION = <<~DESC
12
12
  - Builds and pushes the image to Control Plane
13
13
  - Automatically assigns image numbers, e.g., `app:1`, `app:2`, etc.
14
- - Uses `.controlplane/Dockerfile`
14
+ - Uses `.controlplane/Dockerfile` or a different Dockerfile specified through `dockerfile` in the `.controlplane/controlplane.yml` file
15
+ - If a commit is provided through `--commit` or `-c`, it will be set as the runtime env var `GIT_COMMIT`
15
16
  DESC
16
17
 
17
- def call
18
+ def call # rubocop:disable Metrics/MethodLength
18
19
  ensure_docker_running!
19
20
 
20
21
  dockerfile = config.current[:dockerfile] || "Dockerfile"
21
22
  dockerfile = "#{config.app_cpln_dir}/#{dockerfile}"
22
23
 
24
+ raise "Can't find Dockerfile at '#{dockerfile}'." unless File.exist?(dockerfile)
25
+
23
26
  progress.puts("Building image from Dockerfile '#{dockerfile}'...\n\n")
24
27
 
25
- cp.image_build(latest_image_next, dockerfile: dockerfile)
28
+ image_name = latest_image_next
29
+ image_url = "#{config.org}.registry.cpln.io/#{image_name}"
30
+
31
+ commit = config.options[:commit]
32
+ build_args = []
33
+ build_args.push("GIT_COMMIT=#{commit}") if commit
34
+
35
+ cp.image_build(image_url, dockerfile: dockerfile, build_args: build_args)
36
+
37
+ progress.puts("\nPushed image to '/org/#{config.org}/image/#{image_name}'.")
26
38
  end
27
39
 
28
40
  private
@@ -29,7 +29,7 @@ module Command
29
29
  ensure_docker_running!
30
30
 
31
31
  @upstream = config[:upstream]
32
- @upstream_org = config.apps[@upstream.to_sym][:cpln_org] || config.apps[@upstream.to_sym][:org]
32
+ @upstream_org = config.apps[@upstream.to_sym][:cpln_org]
33
33
  ensure_upstream_org!
34
34
 
35
35
  create_upstream_profile
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
@@ -8,13 +8,17 @@ module Command
8
8
  DEFAULT_ARGS = ["bash"].freeze
9
9
  OPTIONS = [
10
10
  app_option(required: true),
11
- image_option
11
+ image_option,
12
+ workload_option,
13
+ use_local_token_option,
14
+ terminal_size_option
12
15
  ].freeze
13
16
  DESCRIPTION = "Runs one-off **_interactive_** replicas (analog of `heroku run`)"
14
17
  LONG_DESCRIPTION = <<~DESC
15
18
  - Runs one-off **_interactive_** replicas (analog of `heroku run`)
16
19
  - Uses `Standard` workload type and `cpln exec` as the execution method, with CLI streaming
17
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`)
18
22
 
19
23
  > **IMPORTANT:** Useful for development where it's needed for interaction, and where network connection drops and
20
24
  > task crashing are tolerable. For production tasks, it's better to use `cpl run:detached`.
@@ -24,6 +28,12 @@ module Command
24
28
  # Opens shell (bash by default).
25
29
  cpl run -a $APP_NAME
26
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
+
27
37
  # Runs command, displays output, and exits shell.
28
38
  cpl run ls / -a $APP_NAME
29
39
  cpl run rails db:migrate:status -a $APP_NAME
@@ -34,19 +44,25 @@ module Command
34
44
  # Uses a different image (which may not be promoted yet).
35
45
  cpl run rails db:migrate -a $APP_NAME --image appimage:123 # Exact image name
36
46
  cpl run rails db:migrate -a $APP_NAME --image latest # Latest sequential image
47
+
48
+ # Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
49
+ cpl run bash -a $APP_NAME -w other-workload
50
+
51
+ # Overrides remote CPLN_TOKEN env variable with local token.
52
+ # Useful when superuser rights are needed in remote container.
53
+ cpl run bash -a $APP_NAME --use-local-token
37
54
  ```
38
55
  EX
39
56
 
40
- attr_reader :location, :workload, :one_off
57
+ attr_reader :location, :workload, :one_off, :container
41
58
 
42
59
  def call
43
60
  @location = config[:default_location]
44
- @workload = config[:one_off_workload]
61
+ @workload = config.options["workload"] || config[:one_off_workload]
45
62
  @one_off = "#{workload}-run-#{rand(1000..9999)}"
46
63
 
47
64
  clone_workload
48
65
  wait_for_workload(one_off)
49
- sleep 2 # sometimes replica query lags workload creation, despite ok by prev query
50
66
  wait_for_replica(one_off, location)
51
67
  run_in_replica
52
68
  ensure
@@ -60,39 +76,62 @@ module Command
60
76
 
61
77
  # Create a base copy of workload props
62
78
  spec = cp.fetch_workload!(workload).fetch("spec")
63
- container = spec["containers"].detect { _1["name"] == workload } || spec["containers"].first
79
+ container_spec = spec["containers"].detect { _1["name"] == workload } || spec["containers"].first
80
+ @container = container_spec["name"]
64
81
 
65
82
  # remove other containers if any
66
- spec["containers"] = [container]
83
+ spec["containers"] = [container_spec]
67
84
 
68
85
  # Stub workload command with dummy server that just responds to port
69
86
  # Needed to avoid execution of ENTRYPOINT and CMD of Dockerfile
70
- container["command"] = "ruby"
71
- container["args"] = ["-e", Scripts.http_dummy_server_ruby]
87
+ container_spec["command"] = "ruby"
88
+ container_spec["args"] = ["-e", Scripts.http_dummy_server_ruby]
72
89
 
73
90
  # Ensure one-off workload will be running
74
91
  spec["defaultOptions"]["suspend"] = false
75
92
 
76
93
  # Ensure no scaling
77
94
  spec["defaultOptions"]["autoscaling"]["minScale"] = 1
78
- spec["defaultOptions"]["autoscaling"]["minScale"] = 1
95
+ spec["defaultOptions"]["autoscaling"]["maxScale"] = 1
79
96
  spec["defaultOptions"]["capacityAI"] = false
80
97
 
81
98
  # Override image if specified
82
99
  image = config.options[:image]
83
100
  image = "/org/#{config.org}/image/#{latest_image}" if image == "latest"
84
- container["image"] = image if image
101
+ container_spec["image"] = image if image
85
102
 
86
103
  # Set runner
87
- container["env"] ||= []
88
- container["env"] << { "name" => "CONTROLPLANE_RUNNER", "value" => runner_script }
104
+ container_spec["env"] ||= []
105
+ container_spec["env"] << { "name" => "CONTROLPLANE_RUNNER", "value" => runner_script }
106
+
107
+ if config.options["use_local_token"]
108
+ container_spec["env"] << { "name" => "CONTROLPLANE_TOKEN", "value" => ControlplaneApiDirect.new.api_token }
109
+ end
89
110
 
90
111
  # Create workload clone
91
112
  cp.apply("kind" => "workload", "name" => one_off, "spec" => spec)
92
113
  end
93
114
 
94
- def runner_script
115
+ def runner_script # rubocop:disable Metrics/MethodLength
95
116
  script = Scripts.helpers_cleanup
117
+
118
+ if config.options["use_local_token"]
119
+ script += <<~SHELL
120
+ CPLN_TOKEN=$CONTROLPLANE_TOKEN
121
+ unset CONTROLPLANE_TOKEN
122
+ SHELL
123
+ end
124
+
125
+ # NOTE: fixes terminal size to match local terminal
126
+ if config.current[:fix_terminal_size] || config.options[:terminal_size]
127
+ if config.options[:terminal_size]
128
+ rows, cols = config.options[:terminal_size].split(",")
129
+ else
130
+ rows, cols = `stty -a`.match(/(\d+)\s*rows;\s*(\d+)\s*columns/).captures
131
+ end
132
+ script += "stty rows #{rows}\nstty cols #{cols}\n" if rows && cols
133
+ end
134
+
96
135
  script += args_join(config.args)
97
136
  script
98
137
  end
@@ -100,7 +139,7 @@ module Command
100
139
  def run_in_replica
101
140
  progress.puts "- Connecting"
102
141
  command = %(bash -c 'eval "$CONTROLPLANE_RUNNER"')
103
- cp.workload_exec(one_off, location: location, container: workload, command: command)
142
+ cp.workload_exec(one_off, location: location, container: container, command: command)
104
143
  end
105
144
  end
106
145
  end