cpl 1.2.0 → 1.4.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/.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
|