cpl 1.2.0 → 1.4.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/check_cpln_links.yml +19 -0
- data/.overcommit.yml +3 -0
- data/CHANGELOG.md +43 -1
- data/Gemfile.lock +9 -5
- data/README.md +43 -7
- data/cpl.gemspec +1 -0
- data/docs/commands.md +30 -23
- data/docs/dns.md +9 -0
- data/docs/tips.md +11 -1
- data/examples/controlplane.yml +22 -1
- data/lib/command/apply_template.rb +58 -10
- data/lib/command/base.rb +79 -2
- data/lib/command/build_image.rb +5 -1
- data/lib/command/cleanup_stale_apps.rb +0 -2
- data/lib/command/copy_image_from_upstream.rb +5 -4
- data/lib/command/deploy_image.rb +20 -2
- data/lib/command/info.rb +11 -26
- data/lib/command/maintenance.rb +8 -4
- data/lib/command/maintenance_off.rb +8 -4
- data/lib/command/maintenance_on.rb +8 -4
- data/lib/command/promote_app_from_upstream.rb +5 -25
- data/lib/command/run.rb +20 -22
- data/lib/command/run_detached.rb +38 -30
- data/lib/command/setup_app.rb +19 -2
- data/lib/core/config.rb +36 -14
- data/lib/core/controlplane.rb +34 -7
- data/lib/core/controlplane_api.rb +12 -0
- data/lib/core/controlplane_api_direct.rb +33 -5
- data/lib/core/helpers.rb +6 -0
- data/lib/cpl/version.rb +1 -1
- data/lib/cpl.rb +6 -1
- data/lib/generator_templates/controlplane.yml +5 -0
- data/lib/generator_templates/templates/gvc.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 +45 -0
- data/templates/daily-task.yml +3 -2
- data/templates/gvc.yml +5 -5
- data/templates/identity.yml +2 -1
- data/templates/rails.yml +3 -2
- data/templates/secrets-policy.yml +4 -0
- data/templates/secrets.yml +3 -0
- data/templates/sidekiq.yml +3 -2
- metadata +21 -2
data/lib/command/base.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "../core/helpers"
|
4
|
+
|
3
5
|
module Command
|
4
6
|
class Base # rubocop:disable Metrics/ClassLength
|
5
7
|
attr_reader :config
|
6
8
|
|
9
|
+
include Helpers
|
10
|
+
|
7
11
|
# Used to call the command (`cpl NAME`)
|
8
12
|
# NAME = ""
|
9
13
|
# Displayed when running `cpl help` or `cpl help NAME` (defaults to `NAME`)
|
@@ -15,6 +19,9 @@ module Command
|
|
15
19
|
DEFAULT_ARGS = [].freeze
|
16
20
|
# Options for the command (use option methods below)
|
17
21
|
OPTIONS = [].freeze
|
22
|
+
# Does not throw error if `true` and extra options
|
23
|
+
# that are not specified in `OPTIONS` are passed to the command
|
24
|
+
ACCEPTS_EXTRA_OPTIONS = false
|
18
25
|
# Displayed when running `cpl help`
|
19
26
|
# DESCRIPTION = ""
|
20
27
|
# Displayed when running `cpl help NAME`
|
@@ -122,6 +129,18 @@ module Command
|
|
122
129
|
}
|
123
130
|
end
|
124
131
|
|
132
|
+
def self.domain_option(required: false)
|
133
|
+
{
|
134
|
+
name: :domain,
|
135
|
+
params: {
|
136
|
+
banner: "DOMAIN_NAME",
|
137
|
+
desc: "Domain name",
|
138
|
+
type: :string,
|
139
|
+
required: required
|
140
|
+
}
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
125
144
|
def self.upstream_token_option(required: false)
|
126
145
|
{
|
127
146
|
name: :upstream_token,
|
@@ -218,6 +237,40 @@ module Command
|
|
218
237
|
}
|
219
238
|
end
|
220
239
|
|
240
|
+
def self.clean_on_failure_option(required: false)
|
241
|
+
{
|
242
|
+
name: :clean_on_failure,
|
243
|
+
params: {
|
244
|
+
desc: "Deletes workload when finished with failure (success always deletes)",
|
245
|
+
type: :boolean,
|
246
|
+
required: required,
|
247
|
+
default: true
|
248
|
+
}
|
249
|
+
}
|
250
|
+
end
|
251
|
+
|
252
|
+
def self.skip_secret_access_binding_option(required: false)
|
253
|
+
{
|
254
|
+
name: :skip_secret_access_binding,
|
255
|
+
params: {
|
256
|
+
desc: "Skips secret access binding",
|
257
|
+
type: :boolean,
|
258
|
+
required: required
|
259
|
+
}
|
260
|
+
}
|
261
|
+
end
|
262
|
+
|
263
|
+
def self.run_release_phase_option(required: false)
|
264
|
+
{
|
265
|
+
name: :run_release_phase,
|
266
|
+
params: {
|
267
|
+
desc: "Runs release phase",
|
268
|
+
type: :boolean,
|
269
|
+
required: required
|
270
|
+
}
|
271
|
+
}
|
272
|
+
end
|
273
|
+
|
221
274
|
def self.all_options
|
222
275
|
methods.grep(/_option$/).map { |method| send(method.to_s) }
|
223
276
|
end
|
@@ -341,8 +394,32 @@ module Command
|
|
341
394
|
@cp ||= Controlplane.new(config)
|
342
395
|
end
|
343
396
|
|
344
|
-
def perform(cmd)
|
345
|
-
system(cmd) || exit(
|
397
|
+
def perform!(cmd)
|
398
|
+
system(cmd) || exit(1)
|
399
|
+
end
|
400
|
+
|
401
|
+
def app_location_link
|
402
|
+
"/org/#{config.org}/location/#{config.location}"
|
403
|
+
end
|
404
|
+
|
405
|
+
def app_image_link
|
406
|
+
"/org/#{config.org}/image/#{latest_image}"
|
407
|
+
end
|
408
|
+
|
409
|
+
def app_identity
|
410
|
+
"#{config.app}-identity"
|
411
|
+
end
|
412
|
+
|
413
|
+
def app_identity_link
|
414
|
+
"/org/#{config.org}/gvc/#{config.app}/identity/#{app_identity}"
|
415
|
+
end
|
416
|
+
|
417
|
+
def app_secrets
|
418
|
+
"#{config.app_prefix}-secrets"
|
419
|
+
end
|
420
|
+
|
421
|
+
def app_secrets_policy
|
422
|
+
"#{app_secrets}-policy"
|
346
423
|
end
|
347
424
|
|
348
425
|
private
|
data/lib/command/build_image.rb
CHANGED
@@ -7,12 +7,14 @@ module Command
|
|
7
7
|
app_option(required: true),
|
8
8
|
commit_option
|
9
9
|
].freeze
|
10
|
+
ACCEPTS_EXTRA_OPTIONS = true
|
10
11
|
DESCRIPTION = "Builds and pushes the image to Control Plane"
|
11
12
|
LONG_DESCRIPTION = <<~DESC
|
12
13
|
- Builds and pushes the image to Control Plane
|
13
14
|
- Automatically assigns image numbers, e.g., `app:1`, `app:2`, etc.
|
14
15
|
- Uses `.controlplane/Dockerfile` or a different Dockerfile specified through `dockerfile` in the `.controlplane/controlplane.yml` file
|
15
16
|
- If a commit is provided through `--commit` or `-c`, it will be set as the runtime env var `GIT_COMMIT`
|
17
|
+
- Accepts extra options that are passed to `docker build`
|
16
18
|
DESC
|
17
19
|
|
18
20
|
def call # rubocop:disable Metrics/MethodLength
|
@@ -32,7 +34,9 @@ module Command
|
|
32
34
|
build_args = []
|
33
35
|
build_args.push("GIT_COMMIT=#{commit}") if commit
|
34
36
|
|
35
|
-
cp.image_build(image_url, dockerfile: dockerfile,
|
37
|
+
cp.image_build(image_url, dockerfile: dockerfile,
|
38
|
+
docker_args: config.args,
|
39
|
+
build_args: build_args)
|
36
40
|
|
37
41
|
progress.puts("\nPushed image to '/org/#{config.org}/image/#{image_name}'.")
|
38
42
|
end
|
@@ -11,7 +11,7 @@ module Command
|
|
11
11
|
DESCRIPTION = "Copies an image (by default the latest) from a source org to the current org"
|
12
12
|
LONG_DESCRIPTION = <<~DESC
|
13
13
|
- Copies an image (by default the latest) from a source org to the current org
|
14
|
-
- The source
|
14
|
+
- The source app must be specified either through the `CPLN_UPSTREAM` env var or `upstream` in the `.controlplane/controlplane.yml` file
|
15
15
|
- Additionally, the token for the source org must be provided through `--upstream-token` or `-t`
|
16
16
|
- A `cpln` profile will be temporarily created to pull the image from the source org
|
17
17
|
DESC
|
@@ -28,8 +28,8 @@ module Command
|
|
28
28
|
def call # rubocop:disable Metrics/MethodLength
|
29
29
|
ensure_docker_running!
|
30
30
|
|
31
|
-
@upstream = config[:upstream]
|
32
|
-
@upstream_org =
|
31
|
+
@upstream = ENV.fetch("CPLN_UPSTREAM", nil) || config[:upstream]
|
32
|
+
@upstream_org = ENV.fetch("CPLN_ORG_UPSTREAM", nil) || config.find_app_config(@upstream)&.dig(:cpln_org)
|
33
33
|
ensure_upstream_org!
|
34
34
|
|
35
35
|
create_upstream_profile
|
@@ -38,6 +38,7 @@ module Command
|
|
38
38
|
pull_image_from_upstream
|
39
39
|
push_image_to_app
|
40
40
|
ensure
|
41
|
+
cp.profile_switch("default")
|
41
42
|
delete_upstream_profile
|
42
43
|
end
|
43
44
|
|
@@ -60,7 +61,7 @@ module Command
|
|
60
61
|
def create_upstream_profile
|
61
62
|
step("Creating upstream profile") do
|
62
63
|
loop do
|
63
|
-
@upstream_profile = "upstream-#{
|
64
|
+
@upstream_profile = "upstream-#{random_four_digits}"
|
64
65
|
break unless cp.profile_exists?(@upstream_profile)
|
65
66
|
end
|
66
67
|
|
data/lib/command/deploy_image.rb
CHANGED
@@ -4,14 +4,19 @@ 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 deploy will fail if the release script exits with a non-zero code or doesn't exist
|
12
15
|
DESC
|
13
16
|
|
14
17
|
def call # rubocop:disable Metrics/MethodLength
|
18
|
+
run_release_script if config.options[:run_release_phase]
|
19
|
+
|
15
20
|
deployed_endpoints = {}
|
16
21
|
|
17
22
|
image = latest_image
|
@@ -34,5 +39,18 @@ module Command
|
|
34
39
|
progress.puts(" - #{workload}: #{endpoint}")
|
35
40
|
end
|
36
41
|
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def run_release_script
|
46
|
+
release_script_name = config[:release_script]
|
47
|
+
release_script_path = Pathname.new("#{config.app_cpln_dir}/#{release_script_name}").expand_path
|
48
|
+
|
49
|
+
raise "Can't find release script in '#{release_script_path}'." unless File.exist?(release_script_path)
|
50
|
+
|
51
|
+
progress.puts("Running release script...\n\n")
|
52
|
+
perform!("bash #{release_script_path}")
|
53
|
+
progress.puts
|
54
|
+
end
|
37
55
|
end
|
38
56
|
end
|
data/lib/command/info.rb
CHANGED
@@ -32,7 +32,7 @@ module Command
|
|
32
32
|
@missing_apps_workloads = {}
|
33
33
|
@missing_apps_starting_with = {}
|
34
34
|
|
35
|
-
if config.app && !config.
|
35
|
+
if config.app && !config.should_app_start_with?(config.app)
|
36
36
|
single_app_info
|
37
37
|
else
|
38
38
|
multiple_apps_info
|
@@ -41,20 +41,8 @@ module Command
|
|
41
41
|
|
42
42
|
private
|
43
43
|
|
44
|
-
def app_matches?(app, app_name, app_options)
|
45
|
-
app == app_name.to_s || (app_options[:match_if_app_name_starts_with] && app.start_with?(app_name.to_s))
|
46
|
-
end
|
47
|
-
|
48
|
-
def find_app_options(app)
|
49
|
-
@app_options ||= {}
|
50
|
-
@app_options[app] ||= config.apps.find do |app_name, app_options|
|
51
|
-
app_matches?(app, app_name, app_options)
|
52
|
-
end&.last
|
53
|
-
end
|
54
|
-
|
55
44
|
def find_workloads(app)
|
56
|
-
app_options =
|
57
|
-
return [] if app_options.nil?
|
45
|
+
app_options = config.find_app_config(app)
|
58
46
|
|
59
47
|
(app_options[:app_workloads] + app_options[:additional_workloads] + [app_options[:one_off_workload]]).uniq
|
60
48
|
end
|
@@ -75,21 +63,19 @@ module Command
|
|
75
63
|
end
|
76
64
|
|
77
65
|
if config.app
|
78
|
-
result.select { |app, _| app_matches?(app, config.app, config.current) }
|
66
|
+
result.select { |app, _| config.app_matches?(app, config.app, config.current) }
|
79
67
|
else
|
80
|
-
result.reject { |app, _|
|
68
|
+
result.reject { |app, _| config.find_app_config(app).nil? }
|
81
69
|
end
|
82
70
|
end
|
83
71
|
|
84
|
-
def orgs
|
72
|
+
def orgs
|
85
73
|
result = []
|
86
74
|
|
87
75
|
if config.org
|
88
76
|
result.push(config.org)
|
89
77
|
else
|
90
|
-
config.apps.each do |
|
91
|
-
next if config.app && !app_matches?(config.app, app_name, app_options)
|
92
|
-
|
78
|
+
config.apps.each do |_, app_options|
|
93
79
|
org = app_options[:cpln_org]
|
94
80
|
result.push(org) if org && !result.include?(org)
|
95
81
|
end
|
@@ -102,7 +88,7 @@ module Command
|
|
102
88
|
result = []
|
103
89
|
|
104
90
|
config.apps.each do |app_name, app_options|
|
105
|
-
next if config.app && !app_matches?(config.app, app_name, app_options)
|
91
|
+
next if config.app && !config.app_matches?(config.app, app_name, app_options)
|
106
92
|
|
107
93
|
app_org = app_options[:cpln_org]
|
108
94
|
result.push(app_name.to_s) if app_org == org
|
@@ -113,7 +99,7 @@ module Command
|
|
113
99
|
end
|
114
100
|
|
115
101
|
def any_app_starts_with?(app)
|
116
|
-
@app_workloads.keys.find { |app_name| app_matches?(app_name, app, config.apps[app.to_sym]) }
|
102
|
+
@app_workloads.keys.find { |app_name| config.app_matches?(app_name, app, config.apps[app.to_sym]) }
|
117
103
|
end
|
118
104
|
|
119
105
|
def check_any_app_starts_with(app)
|
@@ -184,8 +170,7 @@ module Command
|
|
184
170
|
"(replace 'whatever' with whatever suffix you want):"
|
185
171
|
|
186
172
|
@missing_apps_starting_with.each do |app, _workloads|
|
187
|
-
|
188
|
-
puts " - `cpl setup-app -a #{app_with_suffix}`"
|
173
|
+
puts " - `cpl setup-app -a #{app}-whatever`"
|
189
174
|
end
|
190
175
|
end
|
191
176
|
|
@@ -197,7 +182,7 @@ module Command
|
|
197
182
|
@defined_workloads = find_workloads(config.app)
|
198
183
|
@available_workloads = fetch_workloads(config.app)
|
199
184
|
|
200
|
-
workloads = (@defined_workloads + @available_workloads).uniq
|
185
|
+
workloads = (@defined_workloads + @available_workloads).uniq
|
201
186
|
workloads.each do |workload|
|
202
187
|
print_workload(config.app, workload)
|
203
188
|
end
|
@@ -217,7 +202,7 @@ module Command
|
|
217
202
|
@defined_workloads = find_workloads(app)
|
218
203
|
@available_workloads = @app_workloads[app] || []
|
219
204
|
|
220
|
-
workloads = (@defined_workloads + @available_workloads).uniq
|
205
|
+
workloads = (@defined_workloads + @available_workloads).uniq
|
221
206
|
workloads.each do |workload|
|
222
207
|
print_workload(app, workload)
|
223
208
|
end
|
data/lib/command/maintenance.rb
CHANGED
@@ -4,7 +4,8 @@ module Command
|
|
4
4
|
class Maintenance < Base
|
5
5
|
NAME = "maintenance"
|
6
6
|
OPTIONS = [
|
7
|
-
app_option(required: true)
|
7
|
+
app_option(required: true),
|
8
|
+
domain_option
|
8
9
|
].freeze
|
9
10
|
DESCRIPTION = "Checks if maintenance mode is on or off for an app"
|
10
11
|
LONG_DESCRIPTION = <<~DESC
|
@@ -20,15 +21,18 @@ module Command
|
|
20
21
|
one_off_workload = config[:one_off_workload]
|
21
22
|
maintenance_workload = config.current[:maintenance_workload] || "maintenance"
|
22
23
|
|
23
|
-
domain_data =
|
24
|
+
domain_data = if config.domain
|
25
|
+
cp.fetch_domain(config.domain)
|
26
|
+
else
|
27
|
+
cp.find_domain_for([one_off_workload, maintenance_workload])
|
28
|
+
end
|
24
29
|
unless domain_data
|
25
30
|
raise "Can't find domain. " \
|
26
31
|
"Maintenance mode is only supported for domains that use path based routing mode " \
|
27
32
|
"and have a route configured for the prefix '/' on either port 80 or 443."
|
28
33
|
end
|
29
34
|
|
30
|
-
|
31
|
-
if domain_workload == maintenance_workload
|
35
|
+
if cp.domain_workload_matches?(domain_data, maintenance_workload)
|
32
36
|
puts "on"
|
33
37
|
else
|
34
38
|
puts "off"
|
@@ -4,7 +4,8 @@ module Command
|
|
4
4
|
class MaintenanceOff < Base
|
5
5
|
NAME = "maintenance:off"
|
6
6
|
OPTIONS = [
|
7
|
-
app_option(required: true)
|
7
|
+
app_option(required: true),
|
8
|
+
domain_option
|
8
9
|
].freeze
|
9
10
|
DESCRIPTION = "Disables maintenance mode for an app"
|
10
11
|
LONG_DESCRIPTION = <<~DESC
|
@@ -18,7 +19,11 @@ module Command
|
|
18
19
|
one_off_workload = config[:one_off_workload]
|
19
20
|
maintenance_workload = config.current[:maintenance_workload] || "maintenance"
|
20
21
|
|
21
|
-
domain_data =
|
22
|
+
domain_data = if config.domain
|
23
|
+
cp.fetch_domain(config.domain)
|
24
|
+
else
|
25
|
+
cp.find_domain_for([one_off_workload, maintenance_workload])
|
26
|
+
end
|
22
27
|
unless domain_data
|
23
28
|
raise "Can't find domain. " \
|
24
29
|
"Maintenance mode is only supported for domains that use path based routing mode " \
|
@@ -26,8 +31,7 @@ module Command
|
|
26
31
|
end
|
27
32
|
|
28
33
|
domain = domain_data["name"]
|
29
|
-
|
30
|
-
if domain_workload == one_off_workload
|
34
|
+
if cp.domain_workload_matches?(domain_data, one_off_workload)
|
31
35
|
progress.puts("Maintenance mode is already disabled for app '#{config.app}'.")
|
32
36
|
return
|
33
37
|
end
|
@@ -4,7 +4,8 @@ module Command
|
|
4
4
|
class MaintenanceOn < Base
|
5
5
|
NAME = "maintenance:on"
|
6
6
|
OPTIONS = [
|
7
|
-
app_option(required: true)
|
7
|
+
app_option(required: true),
|
8
|
+
domain_option
|
8
9
|
].freeze
|
9
10
|
DESCRIPTION = "Enables maintenance mode for an app"
|
10
11
|
LONG_DESCRIPTION = <<~DESC
|
@@ -18,7 +19,11 @@ module Command
|
|
18
19
|
one_off_workload = config[:one_off_workload]
|
19
20
|
maintenance_workload = config.current[:maintenance_workload] || "maintenance"
|
20
21
|
|
21
|
-
domain_data =
|
22
|
+
domain_data = if config.domain
|
23
|
+
cp.fetch_domain(config.domain)
|
24
|
+
else
|
25
|
+
cp.find_domain_for([one_off_workload, maintenance_workload])
|
26
|
+
end
|
22
27
|
unless domain_data
|
23
28
|
raise "Can't find domain. " \
|
24
29
|
"Maintenance mode is only supported for domains that use path based routing mode " \
|
@@ -26,8 +31,7 @@ module Command
|
|
26
31
|
end
|
27
32
|
|
28
33
|
domain = domain_data["name"]
|
29
|
-
|
30
|
-
if domain_workload == maintenance_workload
|
34
|
+
if cp.domain_workload_matches?(domain_data, maintenance_workload)
|
31
35
|
progress.puts("Maintenance mode is already enabled for app '#{config.app}'.")
|
32
36
|
return
|
33
37
|
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/run.rb
CHANGED
@@ -29,56 +29,53 @@ module Command
|
|
29
29
|
cpl run -a $APP_NAME
|
30
30
|
|
31
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.
|
35
32
|
cpl run -a $APP_NAME -- 'LOG_LEVEL=warn rails db:migrate'
|
36
33
|
|
37
34
|
# Runs command, displays output, and exits shell.
|
38
|
-
cpl run
|
39
|
-
cpl run rails db:migrate:status
|
35
|
+
cpl run -a $APP_NAME -- ls /
|
36
|
+
cpl run -a $APP_NAME -- rails db:migrate:status
|
40
37
|
|
41
38
|
# Runs command and keeps shell open.
|
42
|
-
cpl run
|
39
|
+
cpl run -a $APP_NAME -- rails c
|
43
40
|
|
44
41
|
# Uses a different image (which may not be promoted yet).
|
45
|
-
cpl run
|
46
|
-
cpl run
|
42
|
+
cpl run -a $APP_NAME --image appimage:123 -- rails db:migrate # Exact image name
|
43
|
+
cpl run -a $APP_NAME --image latest -- rails db:migrate # Latest sequential image
|
47
44
|
|
48
45
|
# Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
|
49
|
-
cpl run
|
46
|
+
cpl run -a $APP_NAME -w other-workload -- bash
|
50
47
|
|
51
48
|
# Overrides remote CPLN_TOKEN env variable with local token.
|
52
49
|
# Useful when superuser rights are needed in remote container.
|
53
|
-
cpl run
|
50
|
+
cpl run -a $APP_NAME --use-local-token -- bash
|
54
51
|
```
|
55
52
|
EX
|
56
53
|
|
57
|
-
attr_reader :location, :
|
54
|
+
attr_reader :location, :workload_to_clone, :workload_clone, :container
|
58
55
|
|
59
56
|
def call # rubocop:disable Metrics/MethodLength
|
60
57
|
@location = config.location
|
61
|
-
@
|
62
|
-
@
|
58
|
+
@workload_to_clone = config.options["workload"] || config[:one_off_workload]
|
59
|
+
@workload_clone = "#{workload_to_clone}-run-#{random_four_digits}"
|
63
60
|
|
64
|
-
step("Cloning workload '#{
|
61
|
+
step("Cloning workload '#{workload_to_clone}' on app '#{config.options[:app]}' to '#{workload_clone}'") do
|
65
62
|
clone_workload
|
66
63
|
end
|
67
64
|
|
68
|
-
wait_for_workload(
|
69
|
-
wait_for_replica(
|
65
|
+
wait_for_workload(workload_clone)
|
66
|
+
wait_for_replica(workload_clone, location)
|
70
67
|
run_in_replica
|
71
68
|
ensure
|
72
69
|
progress.puts
|
73
|
-
ensure_workload_deleted(
|
70
|
+
ensure_workload_deleted(workload_clone)
|
74
71
|
end
|
75
72
|
|
76
73
|
private
|
77
74
|
|
78
75
|
def clone_workload # rubocop:disable Metrics/MethodLength
|
79
76
|
# Create a base copy of workload props
|
80
|
-
spec = cp.fetch_workload!(
|
81
|
-
container_spec = spec["containers"].detect { _1["name"] ==
|
77
|
+
spec = cp.fetch_workload!(workload_to_clone).fetch("spec")
|
78
|
+
container_spec = spec["containers"].detect { _1["name"] == workload_to_clone } || spec["containers"].first
|
82
79
|
@container = container_spec["name"]
|
83
80
|
|
84
81
|
# remove other containers if any
|
@@ -107,11 +104,12 @@ module Command
|
|
107
104
|
container_spec["env"] << { "name" => "CONTROLPLANE_RUNNER", "value" => runner_script }
|
108
105
|
|
109
106
|
if config.options["use_local_token"]
|
110
|
-
container_spec["env"] << { "name" => "CONTROLPLANE_TOKEN",
|
107
|
+
container_spec["env"] << { "name" => "CONTROLPLANE_TOKEN",
|
108
|
+
"value" => ControlplaneApiDirect.new.api_token[:token] }
|
111
109
|
end
|
112
110
|
|
113
111
|
# Create workload clone
|
114
|
-
cp.apply_hash("kind" => "workload", "name" =>
|
112
|
+
cp.apply_hash("kind" => "workload", "name" => workload_clone, "spec" => spec)
|
115
113
|
end
|
116
114
|
|
117
115
|
def runner_script # rubocop:disable Metrics/MethodLength
|
@@ -141,7 +139,7 @@ module Command
|
|
141
139
|
def run_in_replica
|
142
140
|
progress.puts("Connecting...\n\n")
|
143
141
|
command = %(bash -c 'eval "$CONTROLPLANE_RUNNER"')
|
144
|
-
cp.workload_exec(
|
142
|
+
cp.workload_exec(workload_clone, location: location, container: container, command: command)
|
145
143
|
end
|
146
144
|
end
|
147
145
|
end
|