cpl 1.3.0 → 2.0.0.rc.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/command_docs.yml +1 -1
- data/.github/workflows/rspec-shared.yml +56 -0
- data/.github/workflows/rspec.yml +19 -31
- data/.github/workflows/rubocop.yml +2 -10
- data/.gitignore +2 -0
- data/.simplecov_spawn.rb +10 -0
- data/CHANGELOG.md +28 -1
- data/CONTRIBUTING.md +32 -2
- data/Gemfile.lock +38 -29
- data/README.md +43 -17
- data/cpl.gemspec +2 -1
- data/docs/commands.md +68 -59
- data/docs/dns.md +6 -0
- data/docs/migrating.md +10 -10
- data/docs/tips.md +15 -3
- data/examples/circleci.yml +3 -3
- data/examples/controlplane.yml +35 -9
- data/lib/command/apply_template.rb +66 -18
- data/lib/command/base.rb +168 -27
- data/lib/command/build_image.rb +4 -9
- data/lib/command/cleanup_stale_apps.rb +1 -3
- data/lib/command/copy_image_from_upstream.rb +0 -7
- data/lib/command/delete.rb +39 -7
- data/lib/command/deploy_image.rb +35 -2
- data/lib/command/exists.rb +1 -1
- data/lib/command/generate.rb +1 -1
- data/lib/command/info.rb +7 -3
- data/lib/command/logs.rb +22 -2
- data/lib/command/maintenance_off.rb +1 -1
- data/lib/command/maintenance_on.rb +1 -1
- data/lib/command/open.rb +2 -2
- data/lib/command/open_console.rb +2 -2
- data/lib/command/promote_app_from_upstream.rb +5 -25
- data/lib/command/ps.rb +1 -1
- data/lib/command/ps_start.rb +2 -1
- data/lib/command/ps_stop.rb +40 -8
- data/lib/command/ps_wait.rb +3 -2
- data/lib/command/run.rb +430 -68
- data/lib/command/setup_app.rb +22 -2
- data/lib/constants/exit_code.rb +7 -0
- data/lib/core/config.rb +11 -3
- data/lib/core/controlplane.rb +126 -47
- data/lib/core/controlplane_api.rb +15 -1
- data/lib/core/controlplane_api_cli.rb +3 -3
- data/lib/core/controlplane_api_direct.rb +33 -5
- data/lib/core/shell.rb +15 -9
- data/lib/cpl/version.rb +1 -1
- data/lib/cpl.rb +50 -9
- data/lib/deprecated_commands.json +2 -1
- data/lib/generator_templates/controlplane.yml +5 -0
- data/lib/generator_templates/templates/{gvc.yml → app.yml} +4 -4
- data/lib/generator_templates/templates/postgres.yml +1 -1
- data/lib/generator_templates/templates/rails.yml +1 -1
- data/script/check_cpln_links +3 -3
- data/templates/app.yml +18 -0
- data/templates/daily-task.yml +3 -2
- data/templates/rails.yml +3 -2
- data/templates/secrets.yml +11 -0
- data/templates/sidekiq.yml +3 -2
- metadata +38 -25
- data/.rspec +0 -1
- data/lib/command/run_cleanup.rb +0 -116
- data/lib/command/run_detached.rb +0 -175
- data/lib/core/scripts.rb +0 -34
- data/templates/gvc.yml +0 -13
- data/templates/identity.yml +0 -2
data/lib/command/base.rb
CHANGED
@@ -51,6 +51,7 @@ module Command
|
|
51
51
|
[org_option, verbose_option, trace_option]
|
52
52
|
end
|
53
53
|
|
54
|
+
# rubocop:disable Metrics/MethodLength
|
54
55
|
def self.org_option(required: false)
|
55
56
|
{
|
56
57
|
name: :org,
|
@@ -90,6 +91,19 @@ module Command
|
|
90
91
|
}
|
91
92
|
end
|
92
93
|
|
94
|
+
def self.replica_option(required: false)
|
95
|
+
{
|
96
|
+
name: :replica,
|
97
|
+
params: {
|
98
|
+
aliases: ["-r"],
|
99
|
+
banner: "REPLICA_NAME",
|
100
|
+
desc: "Replica name",
|
101
|
+
type: :string,
|
102
|
+
required: required
|
103
|
+
}
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
93
107
|
def self.image_option(required: false)
|
94
108
|
{
|
95
109
|
name: :image,
|
@@ -103,6 +117,20 @@ module Command
|
|
103
117
|
}
|
104
118
|
end
|
105
119
|
|
120
|
+
def self.log_method_option(required: false)
|
121
|
+
{
|
122
|
+
name: :log_method,
|
123
|
+
params: {
|
124
|
+
type: :numeric,
|
125
|
+
banner: "LOG_METHOD",
|
126
|
+
desc: "Log method",
|
127
|
+
required: required,
|
128
|
+
valid_values: [1, 2, 3],
|
129
|
+
default: 3
|
130
|
+
}
|
131
|
+
}
|
132
|
+
end
|
133
|
+
|
106
134
|
def self.commit_option(required: false)
|
107
135
|
{
|
108
136
|
name: :commit,
|
@@ -198,7 +226,8 @@ module Command
|
|
198
226
|
banner: "ROWS,COLS",
|
199
227
|
desc: "Override remote terminal size (e.g. `--terminal-size 10,20`)",
|
200
228
|
type: :string,
|
201
|
-
required: required
|
229
|
+
required: required,
|
230
|
+
valid_regex: /^\d+,\d+$/
|
202
231
|
}
|
203
232
|
}
|
204
233
|
end
|
@@ -237,44 +266,128 @@ module Command
|
|
237
266
|
}
|
238
267
|
end
|
239
268
|
|
240
|
-
def self.
|
269
|
+
def self.skip_secret_access_binding_option(required: false)
|
241
270
|
{
|
242
|
-
name: :
|
271
|
+
name: :skip_secret_access_binding,
|
243
272
|
params: {
|
244
|
-
desc: "
|
273
|
+
desc: "Skips secret access binding",
|
245
274
|
type: :boolean,
|
275
|
+
required: required
|
276
|
+
}
|
277
|
+
}
|
278
|
+
end
|
279
|
+
|
280
|
+
def self.run_release_phase_option(required: false)
|
281
|
+
{
|
282
|
+
name: :run_release_phase,
|
283
|
+
params: {
|
284
|
+
desc: "Runs release phase",
|
285
|
+
type: :boolean,
|
286
|
+
required: required
|
287
|
+
}
|
288
|
+
}
|
289
|
+
end
|
290
|
+
|
291
|
+
def self.logs_limit_option(required: false)
|
292
|
+
{
|
293
|
+
name: :limit,
|
294
|
+
params: {
|
295
|
+
banner: "NUMBER",
|
296
|
+
desc: "Limit on number of log entries to show",
|
297
|
+
type: :numeric,
|
246
298
|
required: required,
|
247
|
-
default:
|
299
|
+
default: 200
|
248
300
|
}
|
249
301
|
}
|
250
302
|
end
|
251
303
|
|
252
|
-
def self.
|
253
|
-
|
304
|
+
def self.logs_since_option(required: false)
|
305
|
+
{
|
306
|
+
name: :since,
|
307
|
+
params: {
|
308
|
+
banner: "DURATION",
|
309
|
+
desc: "Loopback window for showing logs " \
|
310
|
+
"(see https://www.npmjs.com/package/parse-duration for the accepted formats, e.g., '1h')",
|
311
|
+
type: :string,
|
312
|
+
required: required,
|
313
|
+
default: "1h"
|
314
|
+
}
|
315
|
+
}
|
254
316
|
end
|
255
317
|
|
256
|
-
def self.
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
318
|
+
def self.interactive_option(required: false)
|
319
|
+
{
|
320
|
+
name: :interactive,
|
321
|
+
params: {
|
322
|
+
desc: "Runs interactive command",
|
323
|
+
type: :boolean,
|
324
|
+
required: required
|
325
|
+
}
|
326
|
+
}
|
261
327
|
end
|
262
328
|
|
263
|
-
def
|
264
|
-
|
265
|
-
|
266
|
-
|
329
|
+
def self.detached_option(required: false)
|
330
|
+
{
|
331
|
+
name: :detached,
|
332
|
+
params: {
|
333
|
+
desc: "Runs non-interactive command, detaches, and prints commands to log and stop the job",
|
334
|
+
type: :boolean,
|
335
|
+
required: required
|
336
|
+
}
|
337
|
+
}
|
267
338
|
end
|
268
339
|
|
269
|
-
def
|
270
|
-
|
271
|
-
|
272
|
-
|
340
|
+
def self.cpu_option(required: false)
|
341
|
+
{
|
342
|
+
name: :cpu,
|
343
|
+
params: {
|
344
|
+
banner: "CPU",
|
345
|
+
desc: "Overrides CPU millicores " \
|
346
|
+
"(e.g., '100m' for 100 millicores, '1' for 1 core)",
|
347
|
+
type: :string,
|
348
|
+
required: required,
|
349
|
+
valid_regex: /^\d+m?$/
|
350
|
+
}
|
351
|
+
}
|
273
352
|
end
|
274
353
|
|
275
|
-
def
|
276
|
-
|
277
|
-
|
354
|
+
def self.memory_option(required: false)
|
355
|
+
{
|
356
|
+
name: :memory,
|
357
|
+
params: {
|
358
|
+
banner: "MEMORY",
|
359
|
+
desc: "Overrides memory size " \
|
360
|
+
"(e.g., '100Mi' for 100 mebibytes, '1Gi' for 1 gibibyte)",
|
361
|
+
type: :string,
|
362
|
+
required: required,
|
363
|
+
valid_regex: /^\d+[MG]i$/
|
364
|
+
}
|
365
|
+
}
|
366
|
+
end
|
367
|
+
|
368
|
+
def self.entrypoint_option(required: false)
|
369
|
+
{
|
370
|
+
name: :entrypoint,
|
371
|
+
params: {
|
372
|
+
banner: "ENTRYPOINT",
|
373
|
+
desc: "Overrides entrypoint " \
|
374
|
+
"(must be a single command or a script path that exists in the container)",
|
375
|
+
type: :string,
|
376
|
+
required: required,
|
377
|
+
valid_regex: /^\S+$/
|
378
|
+
}
|
379
|
+
}
|
380
|
+
end
|
381
|
+
# rubocop:enable Metrics/MethodLength
|
382
|
+
|
383
|
+
def self.all_options
|
384
|
+
methods.grep(/_option$/).map { |method| send(method.to_s) }
|
385
|
+
end
|
386
|
+
|
387
|
+
def self.all_options_by_key_name
|
388
|
+
all_options.each_with_object({}) do |option, result|
|
389
|
+
option[:params][:aliases]&.each { |current_alias| result[current_alias.to_s] = option }
|
390
|
+
result["--#{option[:name]}"] = option
|
278
391
|
end
|
279
392
|
end
|
280
393
|
|
@@ -290,8 +403,9 @@ module Command
|
|
290
403
|
end
|
291
404
|
end
|
292
405
|
|
293
|
-
def latest_image(app = config.app, org = config.org)
|
406
|
+
def latest_image(app = config.app, org = config.org, refresh: false)
|
294
407
|
@latest_image ||= {}
|
408
|
+
@latest_image[app] = nil if refresh
|
295
409
|
@latest_image[app] ||=
|
296
410
|
begin
|
297
411
|
items = cp.query_images(app, org)["items"]
|
@@ -355,7 +469,7 @@ module Command
|
|
355
469
|
if retry_on_failure
|
356
470
|
until (success = yield)
|
357
471
|
progress.print(".")
|
358
|
-
sleep
|
472
|
+
Kernel.sleep(1)
|
359
473
|
end
|
360
474
|
else
|
361
475
|
success = yield
|
@@ -372,8 +486,35 @@ module Command
|
|
372
486
|
@cp ||= Controlplane.new(config)
|
373
487
|
end
|
374
488
|
|
375
|
-
def
|
376
|
-
|
489
|
+
def app_location_link
|
490
|
+
"/org/#{config.org}/location/#{config.location}"
|
491
|
+
end
|
492
|
+
|
493
|
+
def app_image_link
|
494
|
+
"/org/#{config.org}/image/#{latest_image}"
|
495
|
+
end
|
496
|
+
|
497
|
+
def app_identity
|
498
|
+
"#{config.app}-identity"
|
499
|
+
end
|
500
|
+
|
501
|
+
def app_identity_link
|
502
|
+
"/org/#{config.org}/gvc/#{config.app}/identity/#{app_identity}"
|
503
|
+
end
|
504
|
+
|
505
|
+
def app_secrets
|
506
|
+
config.current[:secrets_name] || "#{config.app_prefix}-secrets"
|
507
|
+
end
|
508
|
+
|
509
|
+
def app_secrets_policy
|
510
|
+
config.current[:secrets_policy_name] || "#{app_secrets}-policy"
|
511
|
+
end
|
512
|
+
|
513
|
+
def ensure_docker_running!
|
514
|
+
result = Shell.cmd("docker", "version", capture_stderr: true)
|
515
|
+
return if result[:success]
|
516
|
+
|
517
|
+
raise "Can't run Docker. Please make sure that it's installed and started, then try again."
|
377
518
|
end
|
378
519
|
|
379
520
|
private
|
data/lib/command/build_image.rb
CHANGED
@@ -38,16 +38,11 @@ module Command
|
|
38
38
|
docker_args: config.args,
|
39
39
|
build_args: build_args)
|
40
40
|
|
41
|
-
progress.puts("\nPushed image to '/org/#{config.org}/image/#{image_name}'
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def ensure_docker_running!
|
47
|
-
`docker version > /dev/null 2>&1`
|
48
|
-
return if $CHILD_STATUS.success?
|
41
|
+
progress.puts("\nPushed image to '/org/#{config.org}/image/#{image_name}'.\n\n")
|
49
42
|
|
50
|
-
|
43
|
+
step("Waiting for image to be available", retry_on_failure: true) do
|
44
|
+
image_name == latest_image(refresh: true)
|
45
|
+
end
|
51
46
|
end
|
52
47
|
end
|
53
48
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "date"
|
4
|
-
|
5
3
|
module Command
|
6
4
|
class CleanupStaleApps < Base
|
7
5
|
NAME = "cleanup-stale-apps"
|
@@ -23,7 +21,7 @@ module Command
|
|
23
21
|
|
24
22
|
progress.puts("Stale apps:")
|
25
23
|
stale_apps.each do |app|
|
26
|
-
progress.puts(" #{app[:name]} (#{Shell.color((app[:date]).to_s, :red)})")
|
24
|
+
progress.puts(" - #{app[:name]} (#{Shell.color((app[:date]).to_s, :red)})")
|
27
25
|
end
|
28
26
|
|
29
27
|
return unless confirm_delete
|
@@ -44,13 +44,6 @@ module Command
|
|
44
44
|
|
45
45
|
private
|
46
46
|
|
47
|
-
def ensure_docker_running!
|
48
|
-
`docker version > /dev/null 2>&1`
|
49
|
-
return if $CHILD_STATUS.success?
|
50
|
-
|
51
|
-
raise "Can't run Docker. Please make sure that it's installed and started, then try again."
|
52
|
-
end
|
53
|
-
|
54
47
|
def ensure_upstream_org!
|
55
48
|
return if @upstream_org
|
56
49
|
|
data/lib/command/delete.rb
CHANGED
@@ -5,28 +5,54 @@ module Command
|
|
5
5
|
NAME = "delete"
|
6
6
|
OPTIONS = [
|
7
7
|
app_option(required: true),
|
8
|
+
workload_option,
|
8
9
|
skip_confirm_option
|
9
10
|
].freeze
|
10
|
-
DESCRIPTION = "Deletes the whole app (GVC with all workloads, all volumesets and all images)"
|
11
|
+
DESCRIPTION = "Deletes the whole app (GVC with all workloads, all volumesets and all images) or a specific workload"
|
11
12
|
LONG_DESCRIPTION = <<~DESC
|
12
|
-
- Deletes the whole app (GVC with all workloads, all volumesets and all images)
|
13
|
+
- Deletes the whole app (GVC with all workloads, all volumesets and all images) or a specific workload
|
13
14
|
- Will ask for explicit user confirmation
|
14
15
|
DESC
|
16
|
+
EXAMPLES = <<~EX
|
17
|
+
```sh
|
18
|
+
# Deletes the whole app (GVC with all workloads, all volumesets and all images).
|
19
|
+
cpl delete -a $APP_NAME
|
20
|
+
|
21
|
+
# Deletes a specific workload.
|
22
|
+
cpl delete -a $APP_NAME -w $WORKLOAD_NAME
|
23
|
+
```
|
24
|
+
EX
|
15
25
|
|
16
26
|
def call
|
27
|
+
workload = config.options[:workload]
|
28
|
+
if workload
|
29
|
+
delete_single_workload(workload)
|
30
|
+
else
|
31
|
+
delete_whole_app
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def delete_single_workload(workload)
|
38
|
+
return progress.puts("Workload '#{workload}' does not exist.") if cp.fetch_workload(workload).nil?
|
39
|
+
return unless confirm_delete(workload)
|
40
|
+
|
41
|
+
delete_workload(workload)
|
42
|
+
end
|
43
|
+
|
44
|
+
def delete_whole_app
|
17
45
|
return progress.puts("App '#{config.app}' does not exist.") if cp.fetch_gvc.nil?
|
18
46
|
|
19
47
|
check_volumesets
|
20
48
|
check_images
|
21
|
-
return unless confirm_delete
|
49
|
+
return unless confirm_delete(config.app)
|
22
50
|
|
23
51
|
delete_volumesets
|
24
52
|
delete_gvc
|
25
53
|
delete_images
|
26
54
|
end
|
27
55
|
|
28
|
-
private
|
29
|
-
|
30
56
|
def check_volumesets
|
31
57
|
@volumesets = cp.fetch_volumesets["items"]
|
32
58
|
return progress.puts("No volumesets to delete.") unless @volumesets.any?
|
@@ -46,10 +72,10 @@ module Command
|
|
46
72
|
progress.puts("#{Shell.color(message, :red)}\n#{images_list}\n\n")
|
47
73
|
end
|
48
74
|
|
49
|
-
def confirm_delete
|
75
|
+
def confirm_delete(item)
|
50
76
|
return true if config.options[:yes]
|
51
77
|
|
52
|
-
confirmed = Shell.confirm("Are you sure you want to delete '#{
|
78
|
+
confirmed = Shell.confirm("Are you sure you want to delete '#{item}'?")
|
53
79
|
return false unless confirmed
|
54
80
|
|
55
81
|
progress.puts
|
@@ -62,6 +88,12 @@ module Command
|
|
62
88
|
end
|
63
89
|
end
|
64
90
|
|
91
|
+
def delete_workload(workload)
|
92
|
+
step("Deleting workload '#{workload}'") do
|
93
|
+
cp.delete_workload(workload)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
65
97
|
def delete_volumesets
|
66
98
|
@volumesets.each do |volumeset|
|
67
99
|
step("Deleting volumeset '#{volumeset['name']}'") do
|
data/lib/command/deploy_image.rb
CHANGED
@@ -4,17 +4,28 @@ module Command
|
|
4
4
|
class DeployImage < Base
|
5
5
|
NAME = "deploy-image"
|
6
6
|
OPTIONS = [
|
7
|
-
app_option(required: true)
|
7
|
+
app_option(required: true),
|
8
|
+
run_release_phase_option
|
8
9
|
].freeze
|
9
|
-
DESCRIPTION = "Deploys the latest image to app workloads"
|
10
|
+
DESCRIPTION = "Deploys the latest image to app workloads, and runs a release script (optional)"
|
10
11
|
LONG_DESCRIPTION = <<~DESC
|
11
12
|
- Deploys the latest image to app workloads
|
13
|
+
- Optionally runs a release script before deploying if specified through `release_script` in the `.controlplane/controlplane.yml` file and `--run-release-phase` is provided
|
14
|
+
- The release script is run in the context of `cpl run` with the latest image
|
15
|
+
- The deploy will fail if the release script exits with a non-zero code or doesn't exist
|
12
16
|
DESC
|
13
17
|
|
14
18
|
def call # rubocop:disable Metrics/MethodLength
|
19
|
+
run_release_script if config.options[:run_release_phase]
|
20
|
+
|
15
21
|
deployed_endpoints = {}
|
16
22
|
|
17
23
|
image = latest_image
|
24
|
+
if cp.fetch_image_details(image).nil?
|
25
|
+
raise "Image '#{image}' does not exist in the Docker repository on Control Plane " \
|
26
|
+
"(see https://console.cpln.io/console/org/#{config.org}/repository/#{config.app}). " \
|
27
|
+
"Use `cpl build-image` first."
|
28
|
+
end
|
18
29
|
|
19
30
|
config[:app_workloads].each do |workload|
|
20
31
|
workload_data = cp.fetch_workload!(workload)
|
@@ -34,5 +45,27 @@ module Command
|
|
34
45
|
progress.puts(" - #{workload}: #{endpoint}")
|
35
46
|
end
|
36
47
|
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def run_release_script # rubocop:disable Metrics/MethodLength
|
52
|
+
release_script_name = config[:release_script]
|
53
|
+
release_script_path = Pathname.new("#{config.app_cpln_dir}/#{release_script_name}").expand_path
|
54
|
+
|
55
|
+
raise "Can't find release script in '#{release_script_path}'." unless File.exist?(release_script_path)
|
56
|
+
|
57
|
+
progress.puts("Running release script...\n\n")
|
58
|
+
|
59
|
+
release_script = File.read(release_script_path)
|
60
|
+
begin
|
61
|
+
Cpl::Cli.start(["run", "-a", config.app, "--image", "latest", "--", release_script])
|
62
|
+
rescue SystemExit => e
|
63
|
+
progress.puts
|
64
|
+
|
65
|
+
raise "Failed to run release script." if e.status.nonzero?
|
66
|
+
|
67
|
+
progress.puts("Finished running release script.\n\n")
|
68
|
+
end
|
69
|
+
end
|
37
70
|
end
|
38
71
|
end
|
data/lib/command/exists.rb
CHANGED
data/lib/command/generate.rb
CHANGED
data/lib/command/info.rb
CHANGED
@@ -76,7 +76,7 @@ module Command
|
|
76
76
|
result.push(config.org)
|
77
77
|
else
|
78
78
|
config.apps.each do |_, app_options|
|
79
|
-
org = app_options
|
79
|
+
org = app_org(app_options)
|
80
80
|
result.push(org) if org && !result.include?(org)
|
81
81
|
end
|
82
82
|
end
|
@@ -90,14 +90,18 @@ module Command
|
|
90
90
|
config.apps.each do |app_name, app_options|
|
91
91
|
next if config.app && !config.app_matches?(config.app, app_name, app_options)
|
92
92
|
|
93
|
-
|
94
|
-
result.push(app_name.to_s) if
|
93
|
+
current_app_org = app_org(app_options)
|
94
|
+
result.push(app_name.to_s) if current_app_org == org
|
95
95
|
end
|
96
96
|
|
97
97
|
result += @app_workloads.keys.map(&:to_s)
|
98
98
|
result.uniq.sort
|
99
99
|
end
|
100
100
|
|
101
|
+
def app_org(app_options)
|
102
|
+
app_options[:cpln_org]
|
103
|
+
end
|
104
|
+
|
101
105
|
def any_app_starts_with?(app)
|
102
106
|
@app_workloads.keys.find { |app_name| config.app_matches?(app_name, app, config.apps[app.to_sym]) }
|
103
107
|
end
|
data/lib/command/logs.rb
CHANGED
@@ -5,11 +5,15 @@ module Command
|
|
5
5
|
NAME = "logs"
|
6
6
|
OPTIONS = [
|
7
7
|
app_option(required: true),
|
8
|
-
workload_option
|
8
|
+
workload_option,
|
9
|
+
replica_option,
|
10
|
+
logs_limit_option,
|
11
|
+
logs_since_option
|
9
12
|
].freeze
|
10
13
|
DESCRIPTION = "Light wrapper to display tailed raw logs for app/workload syntax"
|
11
14
|
LONG_DESCRIPTION = <<~DESC
|
12
15
|
- Light wrapper to display tailed raw logs for app/workload syntax
|
16
|
+
- Defaults to showing the last 200 entries from the past 1 hour before tailing
|
13
17
|
DESC
|
14
18
|
EXAMPLES = <<~EX
|
15
19
|
```sh
|
@@ -18,12 +22,28 @@ module Command
|
|
18
22
|
|
19
23
|
# Displays logs for a specific workload.
|
20
24
|
cpl logs -a $APP_NAME -w $WORKLOAD_NAME
|
25
|
+
|
26
|
+
# Displays logs for a specific replica of a workload.
|
27
|
+
cpl logs -a $APP_NAME -w $WORKLOAD_NAME -r $REPLICA_NAME
|
28
|
+
|
29
|
+
# Uses a different limit on number of entries.
|
30
|
+
cpl logs -a $APP_NAME --limit 100
|
31
|
+
|
32
|
+
# Uses a different loopback window.
|
33
|
+
cpl logs -a $APP_NAME --since 30min
|
21
34
|
```
|
22
35
|
EX
|
23
36
|
|
24
37
|
def call
|
25
38
|
workload = config.options[:workload] || config[:one_off_workload]
|
26
|
-
|
39
|
+
replica = config.options[:replica]
|
40
|
+
limit = config.options[:limit]
|
41
|
+
since = config.options[:since]
|
42
|
+
|
43
|
+
message = replica ? "replica '#{replica}'" : "workload '#{workload}'"
|
44
|
+
progress.puts("Fetching logs for #{message}...\n\n")
|
45
|
+
|
46
|
+
cp.logs(workload: workload, replica: replica, limit: limit, since: since)
|
27
47
|
end
|
28
48
|
end
|
29
49
|
end
|
data/lib/command/open.rb
CHANGED
@@ -25,9 +25,9 @@ module Command
|
|
25
25
|
workload = config.options[:workload] || config[:one_off_workload]
|
26
26
|
data = cp.fetch_workload!(workload)
|
27
27
|
url = data["status"]["endpoint"]
|
28
|
-
opener =
|
28
|
+
opener = Shell.cmd("which", "xdg-open", "open")[:output].split("\n").grep_v("not found").first
|
29
29
|
|
30
|
-
exec
|
30
|
+
Kernel.exec(opener, url)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
data/lib/command/open_console.rb
CHANGED
@@ -18,9 +18,9 @@ module Command
|
|
18
18
|
url = "https://console.cpln.io/console/org/#{config.org}/gvc/#{config.app}"
|
19
19
|
url += "/workload/#{workload}" if workload
|
20
20
|
url += "/-info"
|
21
|
-
opener =
|
21
|
+
opener = Shell.cmd("which", "xdg-open", "open")[:output].split("\n").grep_v("not found").first
|
22
22
|
|
23
|
-
exec
|
23
|
+
Kernel.exec(opener, url)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
@@ -12,47 +12,27 @@ module Command
|
|
12
12
|
- Copies the latest image from upstream, runs a release script (optional), and deploys the image
|
13
13
|
- It performs the following steps:
|
14
14
|
- Runs `cpl copy-image-from-upstream` to copy the latest image from upstream
|
15
|
-
- Runs a release script if specified through `release_script` in the `.controlplane/controlplane.yml` file
|
16
15
|
- Runs `cpl deploy-image` to deploy the image
|
16
|
+
- If `.controlplane/controlplane.yml` includes the `release_script`, `cpl deploy-image` will use the `--run-release-phase` option
|
17
|
+
- The deploy will fail if the release script exits with a non-zero code
|
17
18
|
DESC
|
18
19
|
|
19
20
|
def call
|
20
|
-
check_release_script
|
21
21
|
copy_image_from_upstream
|
22
|
-
run_release_script
|
23
22
|
deploy_image
|
24
23
|
end
|
25
24
|
|
26
25
|
private
|
27
26
|
|
28
|
-
def check_release_script
|
29
|
-
release_script_name = config.current[:release_script]
|
30
|
-
unless release_script_name
|
31
|
-
progress.puts("Can't find option 'release_script' for app '#{config.app}' in 'controlplane.yml'. " \
|
32
|
-
"Skipping release script.\n\n")
|
33
|
-
return
|
34
|
-
end
|
35
|
-
|
36
|
-
@release_script_path = Pathname.new("#{config.app_cpln_dir}/#{release_script_name}").expand_path
|
37
|
-
|
38
|
-
raise "Can't find release script in '#{@release_script_path}'." unless File.exist?(@release_script_path)
|
39
|
-
end
|
40
|
-
|
41
27
|
def copy_image_from_upstream
|
42
28
|
Cpl::Cli.start(["copy-image-from-upstream", "-a", config.app, "-t", config.options[:upstream_token]])
|
43
29
|
progress.puts
|
44
30
|
end
|
45
31
|
|
46
|
-
def run_release_script
|
47
|
-
return unless @release_script_path
|
48
|
-
|
49
|
-
progress.puts("Running release script...\n\n")
|
50
|
-
perform("bash #{@release_script_path}")
|
51
|
-
progress.puts
|
52
|
-
end
|
53
|
-
|
54
32
|
def deploy_image
|
55
|
-
|
33
|
+
args = []
|
34
|
+
args.push("--run-release-phase") if config.current[:release_script]
|
35
|
+
Cpl::Cli.start(["deploy-image", "-a", config.app, *args])
|
56
36
|
end
|
57
37
|
end
|
58
38
|
end
|
data/lib/command/ps.rb
CHANGED
@@ -33,7 +33,7 @@ module Command
|
|
33
33
|
workloads.each do |workload|
|
34
34
|
cp.fetch_workload!(workload)
|
35
35
|
|
36
|
-
result = cp.
|
36
|
+
result = cp.fetch_workload_replicas(workload, location: location)
|
37
37
|
result["items"].each { |replica| puts replica }
|
38
38
|
end
|
39
39
|
end
|