cpl 2.0.2 → 2.1.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +22 -1
- data/Gemfile.lock +2 -2
- data/README.md +12 -3
- data/docs/commands.md +30 -6
- data/docs/secrets-and-env-values.md +42 -0
- data/docs/tips.md +1 -40
- data/examples/controlplane.yml +12 -3
- data/lib/command/apply_template.rb +70 -80
- data/lib/command/base.rb +82 -71
- data/lib/command/build_image.rb +2 -2
- data/lib/command/cleanup_images.rb +1 -1
- data/lib/command/cleanup_stale_apps.rb +1 -1
- data/lib/command/copy_image_from_upstream.rb +3 -3
- data/lib/command/delete.rb +17 -5
- data/lib/command/deploy_image.rb +6 -21
- data/lib/command/doctor.rb +47 -0
- data/lib/command/latest_image.rb +1 -1
- data/lib/command/no_command.rb +1 -0
- data/lib/command/promote_app_from_upstream.rb +1 -1
- data/lib/command/run.rb +1 -1
- data/lib/command/setup_app.rb +80 -16
- data/lib/command/test.rb +1 -0
- data/lib/command/version.rb +1 -0
- data/lib/core/config.rb +40 -12
- data/lib/core/controlplane.rb +53 -0
- data/lib/core/controlplane_api.rb +13 -7
- data/lib/core/controlplane_api_direct.rb +1 -1
- data/lib/core/doctor_service.rb +104 -0
- data/lib/core/helpers.rb +10 -0
- data/lib/core/template_parser.rb +76 -0
- data/lib/cpl/version.rb +1 -1
- data/lib/cpl.rb +24 -6
- data/templates/app.yml +0 -5
- metadata +6 -4
- data/lib/core/controlplane_api_cli.rb +0 -10
- data/templates/secrets.yml +0 -11
data/lib/command/base.rb
CHANGED
@@ -8,6 +8,10 @@ module Command
|
|
8
8
|
|
9
9
|
include Helpers
|
10
10
|
|
11
|
+
VALIDATIONS_WITHOUT_ADDITIONAL_OPTIONS = %w[config].freeze
|
12
|
+
VALIDATIONS_WITH_ADDITIONAL_OPTIONS = %w[templates].freeze
|
13
|
+
ALL_VALIDATIONS = VALIDATIONS_WITHOUT_ADDITIONAL_OPTIONS + VALIDATIONS_WITH_ADDITIONAL_OPTIONS
|
14
|
+
|
11
15
|
# Used to call the command (`cpl NAME`)
|
12
16
|
# NAME = ""
|
13
17
|
# Displayed when running `cpl help` or `cpl help NAME` (defaults to `NAME`)
|
@@ -32,8 +36,8 @@ module Command
|
|
32
36
|
HIDE = false
|
33
37
|
# Whether or not to show key information like ORG and APP name in commands
|
34
38
|
WITH_INFO_HEADER = true
|
35
|
-
|
36
|
-
|
39
|
+
# Which validations to run before the command
|
40
|
+
VALIDATIONS = %w[config].freeze
|
37
41
|
|
38
42
|
def initialize(config)
|
39
43
|
@config = config
|
@@ -269,6 +273,7 @@ module Command
|
|
269
273
|
def self.skip_secret_access_binding_option(required: false)
|
270
274
|
{
|
271
275
|
name: :skip_secret_access_binding,
|
276
|
+
new_name: :skip_secrets_setup,
|
272
277
|
params: {
|
273
278
|
desc: "Skips secret access binding",
|
274
279
|
type: :boolean,
|
@@ -277,6 +282,17 @@ module Command
|
|
277
282
|
}
|
278
283
|
end
|
279
284
|
|
285
|
+
def self.skip_secrets_setup_option(required: false)
|
286
|
+
{
|
287
|
+
name: :skip_secrets_setup,
|
288
|
+
params: {
|
289
|
+
desc: "Skips secrets setup",
|
290
|
+
type: :boolean,
|
291
|
+
required: required
|
292
|
+
}
|
293
|
+
}
|
294
|
+
end
|
295
|
+
|
280
296
|
def self.run_release_phase_option(required: false)
|
281
297
|
{
|
282
298
|
name: :run_release_phase,
|
@@ -378,57 +394,65 @@ module Command
|
|
378
394
|
}
|
379
395
|
}
|
380
396
|
end
|
381
|
-
# rubocop:enable Metrics/MethodLength
|
382
397
|
|
383
|
-
def self.
|
384
|
-
|
398
|
+
def self.validations_option(required: false)
|
399
|
+
{
|
400
|
+
name: :validations,
|
401
|
+
params: {
|
402
|
+
banner: "VALIDATION_1,VALIDATION_2,...",
|
403
|
+
desc: "Which validations to run " \
|
404
|
+
"(must be separated by a comma)",
|
405
|
+
type: :string,
|
406
|
+
required: required,
|
407
|
+
default: VALIDATIONS_WITHOUT_ADDITIONAL_OPTIONS.join(","),
|
408
|
+
valid_regex: /^(#{ALL_VALIDATIONS.join("|")})(,(#{ALL_VALIDATIONS.join("|")}))*$/
|
409
|
+
}
|
410
|
+
}
|
385
411
|
end
|
386
412
|
|
387
|
-
def self.
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
413
|
+
def self.skip_post_creation_hook_option(required: false)
|
414
|
+
{
|
415
|
+
name: :skip_post_creation_hook,
|
416
|
+
params: {
|
417
|
+
desc: "Skips post-creation hook",
|
418
|
+
type: :boolean,
|
419
|
+
required: required
|
420
|
+
}
|
421
|
+
}
|
392
422
|
end
|
393
423
|
|
394
|
-
def
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
end
|
424
|
+
def self.skip_pre_deletion_hook_option(required: false)
|
425
|
+
{
|
426
|
+
name: :skip_pre_deletion_hook,
|
427
|
+
params: {
|
428
|
+
desc: "Skips pre-deletion hook",
|
429
|
+
type: :boolean,
|
430
|
+
required: required
|
431
|
+
}
|
432
|
+
}
|
404
433
|
end
|
405
434
|
|
406
|
-
def
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
435
|
+
def self.add_app_identity_option(required: false)
|
436
|
+
{
|
437
|
+
name: :add_app_identity,
|
438
|
+
params: {
|
439
|
+
desc: "Adds app identity template if it does not exist",
|
440
|
+
type: :boolean,
|
441
|
+
required: required
|
442
|
+
}
|
443
|
+
}
|
414
444
|
end
|
445
|
+
# rubocop:enable Metrics/MethodLength
|
415
446
|
|
416
|
-
def
|
417
|
-
|
418
|
-
commit ||= config.options[:commit]
|
419
|
-
|
420
|
-
@latest_image_next ||= {}
|
421
|
-
@latest_image_next[app] ||= begin
|
422
|
-
latest_image_name = latest_image(app, org)
|
423
|
-
image = latest_image_name.split(":").first
|
424
|
-
image += ":#{extract_image_number(latest_image_name) + 1}"
|
425
|
-
image += "_#{commit}" if commit
|
426
|
-
image
|
427
|
-
end
|
447
|
+
def self.all_options
|
448
|
+
methods.grep(/_option$/).map { |method| send(method.to_s) }
|
428
449
|
end
|
429
450
|
|
430
|
-
def
|
431
|
-
|
451
|
+
def self.all_options_by_key_name
|
452
|
+
all_options.each_with_object({}) do |option, result|
|
453
|
+
option[:params][:aliases]&.each { |current_alias| result[current_alias.to_s] = option }
|
454
|
+
result["--#{option[:name]}"] = option
|
455
|
+
end
|
432
456
|
end
|
433
457
|
|
434
458
|
# NOTE: use simplified variant atm, as shelljoin do different escaping
|
@@ -486,30 +510,6 @@ module Command
|
|
486
510
|
@cp ||= Controlplane.new(config)
|
487
511
|
end
|
488
512
|
|
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
513
|
def ensure_docker_running!
|
514
514
|
result = Shell.cmd("docker", "version", capture_stderr: true)
|
515
515
|
return if result[:success]
|
@@ -517,13 +517,24 @@ module Command
|
|
517
517
|
raise "Can't run Docker. Please make sure that it's installed and started, then try again."
|
518
518
|
end
|
519
519
|
|
520
|
-
|
520
|
+
def run_command_in_latest_image(command, title:)
|
521
|
+
# Need to prefix the command with '.controlplane/'
|
522
|
+
# if it's a file in the '.controlplane' directory,
|
523
|
+
# for backwards compatibility
|
524
|
+
path = Pathname.new("#{config.app_cpln_dir}/#{command}").expand_path
|
525
|
+
command = ".controlplane/#{command}" if File.exist?(path)
|
526
|
+
|
527
|
+
progress.puts("Running #{title}...\n\n")
|
521
528
|
|
522
|
-
|
523
|
-
|
524
|
-
|
529
|
+
begin
|
530
|
+
Cpl::Cli.start(["run", "-a", config.app, "--image", "latest", "--", command])
|
531
|
+
rescue SystemExit => e
|
532
|
+
progress.puts
|
525
533
|
|
526
|
-
|
534
|
+
raise "Failed to run #{title}." if e.status.nonzero?
|
535
|
+
|
536
|
+
progress.puts("Finished running #{title}.\n\n")
|
537
|
+
end
|
527
538
|
end
|
528
539
|
end
|
529
540
|
end
|
data/lib/command/build_image.rb
CHANGED
@@ -27,7 +27,7 @@ module Command
|
|
27
27
|
|
28
28
|
progress.puts("Building image from Dockerfile '#{dockerfile}'...\n\n")
|
29
29
|
|
30
|
-
image_name = latest_image_next
|
30
|
+
image_name = cp.latest_image_next
|
31
31
|
image_url = "#{config.org}.registry.cpln.io/#{image_name}"
|
32
32
|
|
33
33
|
commit = config.options[:commit]
|
@@ -41,7 +41,7 @@ module Command
|
|
41
41
|
progress.puts("\nPushed image to '/org/#{config.org}/image/#{image_name}'.\n\n")
|
42
42
|
|
43
43
|
step("Waiting for image to be available", retry_on_failure: true) do
|
44
|
-
image_name == latest_image(refresh: true)
|
44
|
+
image_name == cp.latest_image(refresh: true)
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
@@ -56,7 +56,7 @@ module Command
|
|
56
56
|
return images unless cp.fetch_gvc(app)
|
57
57
|
|
58
58
|
# If app exists, remove latest image, because we don't want to delete the image that is currently deployed
|
59
|
-
latest_image_name = latest_image_from(images, app_name: app)
|
59
|
+
latest_image_name = cp.latest_image_from(images, app_name: app)
|
60
60
|
images.reject { |image| image["name"] == latest_image_name }
|
61
61
|
end
|
62
62
|
|
@@ -49,7 +49,7 @@ module Command
|
|
49
49
|
app_name = gvc["name"]
|
50
50
|
|
51
51
|
images = cp.query_images(app_name)["items"].select { |item| item["name"].start_with?("#{app_name}:") }
|
52
|
-
image = latest_image_from(images, app_name: app_name, name_only: false)
|
52
|
+
image = cp.latest_image_from(images, app_name: app_name, name_only: false)
|
53
53
|
next unless image
|
54
54
|
|
55
55
|
created_date = DateTime.parse(image["created"])
|
@@ -66,8 +66,8 @@ module Command
|
|
66
66
|
step("Fetching upstream image URL") do
|
67
67
|
cp.profile_switch(@upstream_profile)
|
68
68
|
upstream_image = config.options[:image]
|
69
|
-
upstream_image = latest_image(@upstream, @upstream_org) if !upstream_image || upstream_image == "latest"
|
70
|
-
@commit = extract_image_commit(upstream_image)
|
69
|
+
upstream_image = cp.latest_image(@upstream, @upstream_org) if !upstream_image || upstream_image == "latest"
|
70
|
+
@commit = cp.extract_image_commit(upstream_image)
|
71
71
|
@upstream_image_url = "#{@upstream_org}.registry.cpln.io/#{upstream_image}"
|
72
72
|
end
|
73
73
|
end
|
@@ -75,7 +75,7 @@ module Command
|
|
75
75
|
def fetch_app_image_url
|
76
76
|
step("Fetching app image URL") do
|
77
77
|
cp.profile_switch("default")
|
78
|
-
app_image = latest_image_next(config.app, config.org, commit: @commit)
|
78
|
+
app_image = cp.latest_image_next(config.app, config.org, commit: @commit)
|
79
79
|
@app_image_url = "#{config.org}.registry.cpln.io/#{app_image}"
|
80
80
|
end
|
81
81
|
end
|
data/lib/command/delete.rb
CHANGED
@@ -6,13 +6,17 @@ module Command
|
|
6
6
|
OPTIONS = [
|
7
7
|
app_option(required: true),
|
8
8
|
workload_option,
|
9
|
-
skip_confirm_option
|
9
|
+
skip_confirm_option,
|
10
|
+
skip_pre_deletion_hook_option
|
10
11
|
].freeze
|
11
12
|
DESCRIPTION = "Deletes the whole app (GVC with all workloads, all volumesets and all images) or a specific workload"
|
12
13
|
LONG_DESCRIPTION = <<~DESC
|
13
14
|
- Deletes the whole app (GVC with all workloads, all volumesets and all images) or a specific workload
|
14
15
|
- Also unbinds the app from the secrets policy, as long as both the identity and the policy exist (and are bound)
|
15
16
|
- Will ask for explicit user confirmation
|
17
|
+
- Runs a pre-deletion hook before the app is deleted if `hooks.pre_deletion` is specified in the `.controlplane/controlplane.yml` file
|
18
|
+
- If the hook exits with a non-zero code, the command will stop executing and also exit with a non-zero code
|
19
|
+
- Use `--skip-pre-deletion-hook` to skip the hook if specified in `controlplane.yml`
|
16
20
|
DESC
|
17
21
|
EXAMPLES = <<~EX
|
18
22
|
```sh
|
@@ -51,6 +55,7 @@ module Command
|
|
51
55
|
check_images
|
52
56
|
return unless confirm_delete(config.app)
|
53
57
|
|
58
|
+
run_pre_deletion_hook unless config.options[:skip_pre_deletion_hook]
|
54
59
|
unbind_identity_from_policy
|
55
60
|
delete_volumesets
|
56
61
|
delete_gvc
|
@@ -119,19 +124,26 @@ module Command
|
|
119
124
|
end
|
120
125
|
|
121
126
|
def unbind_identity_from_policy
|
122
|
-
return if cp.fetch_identity(
|
127
|
+
return if cp.fetch_identity(config.identity).nil?
|
123
128
|
|
124
|
-
policy = cp.fetch_policy(
|
129
|
+
policy = cp.fetch_policy(config.secrets_policy)
|
125
130
|
return if policy.nil?
|
126
131
|
|
127
132
|
is_bound = policy["bindings"].any? do |binding|
|
128
|
-
binding["principalLinks"].any? { |link| link ==
|
133
|
+
binding["principalLinks"].any? { |link| link == config.identity_link }
|
129
134
|
end
|
130
135
|
return unless is_bound
|
131
136
|
|
132
137
|
step("Unbinding identity from policy for app '#{config.app}'") do
|
133
|
-
cp.unbind_identity_from_policy(
|
138
|
+
cp.unbind_identity_from_policy(config.identity_link, config.secrets_policy)
|
134
139
|
end
|
135
140
|
end
|
141
|
+
|
142
|
+
def run_pre_deletion_hook
|
143
|
+
pre_deletion_hook = config.current.dig(:hooks, :pre_deletion)
|
144
|
+
return unless pre_deletion_hook
|
145
|
+
|
146
|
+
run_command_in_latest_image(pre_deletion_hook, title: "pre-deletion hook")
|
147
|
+
end
|
136
148
|
end
|
137
149
|
end
|
data/lib/command/deploy_image.rb
CHANGED
@@ -10,9 +10,9 @@ module Command
|
|
10
10
|
DESCRIPTION = "Deploys the latest image to app workloads, and runs a release script (optional)"
|
11
11
|
LONG_DESCRIPTION = <<~DESC
|
12
12
|
- Deploys the latest image to app workloads
|
13
|
-
-
|
13
|
+
- Runs a release script before deploying if `release_script` is specified in the `.controlplane/controlplane.yml` file and `--run-release-phase` is provided
|
14
14
|
- The release script is run in the context of `cpl run` with the latest image
|
15
|
-
-
|
15
|
+
- If the release script exits with a non-zero code, the command will stop executing and also exit with a non-zero code
|
16
16
|
DESC
|
17
17
|
|
18
18
|
def call # rubocop:disable Metrics/MethodLength
|
@@ -20,7 +20,7 @@ module Command
|
|
20
20
|
|
21
21
|
deployed_endpoints = {}
|
22
22
|
|
23
|
-
image = latest_image
|
23
|
+
image = cp.latest_image
|
24
24
|
if cp.fetch_image_details(image).nil?
|
25
25
|
raise "Image '#{image}' does not exist in the Docker repository on Control Plane " \
|
26
26
|
"(see https://console.cpln.io/console/org/#{config.org}/repository/#{config.app}). " \
|
@@ -48,24 +48,9 @@ module Command
|
|
48
48
|
|
49
49
|
private
|
50
50
|
|
51
|
-
def run_release_script
|
52
|
-
|
53
|
-
|
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
|
51
|
+
def run_release_script
|
52
|
+
release_script = config[:release_script]
|
53
|
+
run_command_in_latest_image(release_script, title: "release script")
|
69
54
|
end
|
70
55
|
end
|
71
56
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Command
|
4
|
+
class Doctor < Base
|
5
|
+
NAME = "doctor"
|
6
|
+
OPTIONS = [
|
7
|
+
validations_option,
|
8
|
+
app_option
|
9
|
+
].freeze
|
10
|
+
DESCRIPTION = "Runs validations"
|
11
|
+
LONG_DESCRIPTION = <<~DESC
|
12
|
+
- Runs validations
|
13
|
+
DESC
|
14
|
+
EXAMPLES = <<~EX
|
15
|
+
```sh
|
16
|
+
# Runs all validations that don't require additional options by default.
|
17
|
+
cpl doctor
|
18
|
+
|
19
|
+
# Runs config validation.
|
20
|
+
cpl doctor --validations config
|
21
|
+
|
22
|
+
# Runs templates validation (requires app).
|
23
|
+
cpl doctor --validations templates -a $APP_NAME
|
24
|
+
```
|
25
|
+
EX
|
26
|
+
VALIDATIONS = [].freeze
|
27
|
+
|
28
|
+
def call
|
29
|
+
validations = config.options[:validations].split(",")
|
30
|
+
ensure_required_options!(validations)
|
31
|
+
|
32
|
+
doctor_service = DoctorService.new(config)
|
33
|
+
doctor_service.run_validations(validations)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def ensure_required_options!(validations)
|
39
|
+
validations.each do |validation|
|
40
|
+
case validation
|
41
|
+
when "templates"
|
42
|
+
raise "App is required for templates validation." unless config.app
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/command/latest_image.rb
CHANGED
data/lib/command/no_command.rb
CHANGED
@@ -14,7 +14,7 @@ module Command
|
|
14
14
|
- Runs `cpl copy-image-from-upstream` to copy the latest image from upstream
|
15
15
|
- Runs `cpl deploy-image` to deploy the image
|
16
16
|
- If `.controlplane/controlplane.yml` includes the `release_script`, `cpl deploy-image` will use the `--run-release-phase` option
|
17
|
-
-
|
17
|
+
- If the release script exits with a non-zero code, the command will stop executing and also exit with a non-zero code
|
18
18
|
DESC
|
19
19
|
|
20
20
|
def call
|
data/lib/command/run.rb
CHANGED
@@ -183,7 +183,7 @@ module Command
|
|
183
183
|
# Override image if specified
|
184
184
|
image = config.options[:image]
|
185
185
|
image_link = if image
|
186
|
-
image = latest_image if image == "latest"
|
186
|
+
image = cp.latest_image if image == "latest"
|
187
187
|
"/org/#{config.org}/image/#{image}"
|
188
188
|
else
|
189
189
|
original_container_spec["image"]
|
data/lib/command/setup_app.rb
CHANGED
@@ -5,18 +5,27 @@ module Command
|
|
5
5
|
NAME = "setup-app"
|
6
6
|
OPTIONS = [
|
7
7
|
app_option(required: true),
|
8
|
-
skip_secret_access_binding_option
|
8
|
+
skip_secret_access_binding_option,
|
9
|
+
skip_secrets_setup_option,
|
10
|
+
skip_post_creation_hook_option
|
9
11
|
].freeze
|
10
12
|
DESCRIPTION = "Creates an app and all its workloads"
|
11
13
|
LONG_DESCRIPTION = <<~DESC
|
12
14
|
- Creates an app and all its workloads
|
13
15
|
- Specify the templates for the app and workloads through `setup_app_templates` in the `.controlplane/controlplane.yml` file
|
14
|
-
- This should only be used for temporary apps like review apps, never for persistent apps like production (to update workloads for those, use 'cpl apply-template' instead)
|
15
|
-
-
|
16
|
-
|
16
|
+
- This should only be used for temporary apps like review apps, never for persistent apps like production or staging (to update workloads for those, use 'cpl apply-template' instead)
|
17
|
+
- Configures app to have org-level secrets with default name "{APP_PREFIX}-secrets"
|
18
|
+
using org-level policy with default name "{APP_PREFIX}-secrets-policy" (names can be customized, see docs)
|
19
|
+
- Creates identity for secrets if it does not exist
|
20
|
+
- Use `--skip-secrets-setup` to prevent the automatic setup of secrets,
|
21
|
+
or set it through `skip_secrets_setup` in the `.controlplane/controlplane.yml` file
|
22
|
+
- Runs a post-creation hook after the app is created if `hooks.post_creation` is specified in the `.controlplane/controlplane.yml` file
|
23
|
+
- If the hook exits with a non-zero code, the command will stop executing and also exit with a non-zero code
|
24
|
+
- Use `--skip-post-creation-hook` to skip the hook if specified in `controlplane.yml`
|
17
25
|
DESC
|
26
|
+
VALIDATIONS = %w[config templates].freeze
|
18
27
|
|
19
|
-
def call # rubocop:disable Metrics/MethodLength
|
28
|
+
def call # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
20
29
|
templates = config[:setup_app_templates]
|
21
30
|
|
22
31
|
app = cp.fetch_gvc
|
@@ -26,24 +35,79 @@ module Command
|
|
26
35
|
"or run 'cpl apply-template #{templates.join(' ')} -a #{config.app}'."
|
27
36
|
end
|
28
37
|
|
29
|
-
|
38
|
+
skip_secrets_setup = config.options[:skip_secret_access_binding] ||
|
39
|
+
config.options[:skip_secrets_setup] || config.current[:skip_secrets_setup]
|
30
40
|
|
31
|
-
|
41
|
+
create_secret_and_policy_if_not_exist unless skip_secrets_setup
|
42
|
+
|
43
|
+
args = []
|
44
|
+
args.push("--add-app-identity") unless skip_secrets_setup
|
45
|
+
Cpl::Cli.start(["apply-template", *templates, "-a", config.app, *args])
|
46
|
+
|
47
|
+
bind_identity_to_policy unless skip_secrets_setup
|
48
|
+
run_post_creation_hook unless config.options[:skip_post_creation_hook]
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def create_secret_and_policy_if_not_exist
|
54
|
+
create_secret_if_not_exists
|
55
|
+
create_policy_if_not_exists
|
32
56
|
|
33
57
|
progress.puts
|
58
|
+
end
|
34
59
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
60
|
+
def create_secret_if_not_exists
|
61
|
+
if cp.fetch_secret(config.secrets)
|
62
|
+
progress.puts("Secret '#{config.secrets}' already exists. Skipping creation...")
|
63
|
+
else
|
64
|
+
step("Creating secret '#{config.secrets}'") do
|
65
|
+
cp.apply_hash(build_secret_hash)
|
66
|
+
end
|
42
67
|
end
|
68
|
+
end
|
43
69
|
|
44
|
-
|
45
|
-
|
70
|
+
def create_policy_if_not_exists
|
71
|
+
if cp.fetch_policy(config.secrets_policy)
|
72
|
+
progress.puts("Policy '#{config.secrets_policy}' already exists. Skipping creation...")
|
73
|
+
else
|
74
|
+
step("Creating policy '#{config.secrets_policy}'") do
|
75
|
+
cp.apply_hash(build_policy_hash)
|
76
|
+
end
|
46
77
|
end
|
47
78
|
end
|
79
|
+
|
80
|
+
def build_secret_hash
|
81
|
+
{
|
82
|
+
"kind" => "secret",
|
83
|
+
"name" => config.secrets,
|
84
|
+
"type" => "dictionary",
|
85
|
+
"data" => {}
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def build_policy_hash
|
90
|
+
{
|
91
|
+
"kind" => "policy",
|
92
|
+
"name" => config.secrets_policy,
|
93
|
+
"targetKind" => "secret",
|
94
|
+
"targetLinks" => ["//secret/#{config.secrets}"]
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
def bind_identity_to_policy
|
99
|
+
progress.puts
|
100
|
+
|
101
|
+
step("Binding identity '#{config.identity}' to policy '#{config.secrets_policy}'") do
|
102
|
+
cp.bind_identity_to_policy(config.identity_link, config.secrets_policy)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def run_post_creation_hook
|
107
|
+
post_creation_hook = config.current.dig(:hooks, :post_creation)
|
108
|
+
return unless post_creation_hook
|
109
|
+
|
110
|
+
run_command_in_latest_image(post_creation_hook, title: "post-creation hook")
|
111
|
+
end
|
48
112
|
end
|
49
113
|
end
|
data/lib/command/test.rb
CHANGED