cpl 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -0
- data/Gemfile.lock +17 -2
- data/README.md +67 -20
- data/Rakefile +2 -2
- data/cpl.gemspec +3 -0
- data/docs/commands.md +72 -5
- data/examples/circleci.yml +1 -1
- data/examples/controlplane.yml +3 -0
- data/lib/command/base.rb +55 -16
- data/lib/command/build_image.rb +15 -3
- data/lib/command/copy_image_from_upstream.rb +1 -1
- data/lib/command/info.rb +9 -5
- data/lib/command/maintenance.rb +37 -0
- data/lib/command/maintenance_off.rb +58 -0
- data/lib/command/maintenance_on.rb +58 -0
- data/lib/command/maintenance_set_page.rb +34 -0
- data/lib/command/no_command.rb +1 -1
- data/lib/command/promote_app_from_upstream.rb +2 -2
- data/lib/command/ps_start.rb +22 -5
- data/lib/command/ps_stop.rb +22 -5
- data/lib/command/run.rb +23 -5
- data/lib/command/run_cleanup.rb +99 -0
- data/lib/command/run_detached.rb +6 -3
- data/lib/command/setup_app.rb +3 -3
- data/lib/core/config.rb +39 -32
- data/lib/core/controlplane.rb +72 -16
- data/lib/core/controlplane_api.rb +39 -0
- data/lib/core/controlplane_api_direct.rb +13 -3
- data/lib/cpl/version.rb +1 -1
- data/templates/daily-task.yml +30 -0
- data/templates/maintenance.yml +24 -0
- metadata +51 -2
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Command
|
4
|
+
class MaintenanceOff < Base
|
5
|
+
NAME = "maintenance:off"
|
6
|
+
OPTIONS = [
|
7
|
+
app_option(required: true)
|
8
|
+
].freeze
|
9
|
+
DESCRIPTION = "Disables maintenance mode for an app"
|
10
|
+
LONG_DESCRIPTION = <<~DESC
|
11
|
+
- Disables maintenance mode for an app
|
12
|
+
- Specify the one-off workload through `one_off_workload` in the `.controlplane/controlplane.yml` file
|
13
|
+
- Optionally specify the maintenance workload through `maintenance_workload` in the `.controlplane/controlplane.yml` file (defaults to 'maintenance')
|
14
|
+
- Maintenance mode is only supported for domains that use path based routing mode and have a route configured for the prefix '/' on either port 80 or 443
|
15
|
+
DESC
|
16
|
+
|
17
|
+
def call # rubocop:disable Metrics/MethodLength
|
18
|
+
one_off_workload = config[:one_off_workload]
|
19
|
+
maintenance_workload = config.current[:maintenance_workload] || "maintenance"
|
20
|
+
|
21
|
+
domain_data = cp.find_domain_for([one_off_workload, maintenance_workload])
|
22
|
+
unless domain_data
|
23
|
+
raise "Can't find domain. " \
|
24
|
+
"Maintenance mode is only supported for domains that use path based routing mode " \
|
25
|
+
"and have a route configured for the prefix '/' on either port 80 or 443."
|
26
|
+
end
|
27
|
+
|
28
|
+
domain = domain_data["name"]
|
29
|
+
domain_workload = cp.get_domain_workload(domain_data)
|
30
|
+
if domain_workload == one_off_workload
|
31
|
+
progress.puts("Maintenance mode is already disabled for app '#{config.app}'.")
|
32
|
+
return
|
33
|
+
end
|
34
|
+
|
35
|
+
cp.fetch_workload!(maintenance_workload)
|
36
|
+
|
37
|
+
# Start all other workloads
|
38
|
+
Cpl::Cli.start(["ps:start", "-a", config.app, "--wait"])
|
39
|
+
|
40
|
+
progress.puts
|
41
|
+
|
42
|
+
# Switch domain workload
|
43
|
+
step("Switching workload for domain '#{domain}' to '#{one_off_workload}'") do
|
44
|
+
cp.set_domain_workload(domain_data, one_off_workload)
|
45
|
+
|
46
|
+
# Give it a bit of time for the domain to update
|
47
|
+
sleep 30
|
48
|
+
end
|
49
|
+
|
50
|
+
progress.puts
|
51
|
+
|
52
|
+
# Stop maintenance workload
|
53
|
+
Cpl::Cli.start(["ps:stop", "-a", config.app, "-w", maintenance_workload, "--wait"])
|
54
|
+
|
55
|
+
progress.puts("\nMaintenance mode disabled for app '#{config.app}'.")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Command
|
4
|
+
class MaintenanceOn < Base
|
5
|
+
NAME = "maintenance:on"
|
6
|
+
OPTIONS = [
|
7
|
+
app_option(required: true)
|
8
|
+
].freeze
|
9
|
+
DESCRIPTION = "Enables maintenance mode for an app"
|
10
|
+
LONG_DESCRIPTION = <<~DESC
|
11
|
+
- Enables maintenance mode for an app
|
12
|
+
- Specify the one-off workload through `one_off_workload` in the `.controlplane/controlplane.yml` file
|
13
|
+
- Optionally specify the maintenance workload through `maintenance_workload` in the `.controlplane/controlplane.yml` file (defaults to 'maintenance')
|
14
|
+
- Maintenance mode is only supported for domains that use path based routing mode and have a route configured for the prefix '/' on either port 80 or 443
|
15
|
+
DESC
|
16
|
+
|
17
|
+
def call # rubocop:disable Metrics/MethodLength
|
18
|
+
one_off_workload = config[:one_off_workload]
|
19
|
+
maintenance_workload = config.current[:maintenance_workload] || "maintenance"
|
20
|
+
|
21
|
+
domain_data = cp.find_domain_for([one_off_workload, maintenance_workload])
|
22
|
+
unless domain_data
|
23
|
+
raise "Can't find domain. " \
|
24
|
+
"Maintenance mode is only supported for domains that use path based routing mode " \
|
25
|
+
"and have a route configured for the prefix '/' on either port 80 or 443."
|
26
|
+
end
|
27
|
+
|
28
|
+
domain = domain_data["name"]
|
29
|
+
domain_workload = cp.get_domain_workload(domain_data)
|
30
|
+
if domain_workload == maintenance_workload
|
31
|
+
progress.puts("Maintenance mode is already enabled for app '#{config.app}'.")
|
32
|
+
return
|
33
|
+
end
|
34
|
+
|
35
|
+
cp.fetch_workload!(maintenance_workload)
|
36
|
+
|
37
|
+
# Start maintenance workload
|
38
|
+
Cpl::Cli.start(["ps:start", "-a", config.app, "-w", maintenance_workload, "--wait"])
|
39
|
+
|
40
|
+
progress.puts
|
41
|
+
|
42
|
+
# Switch domain workload
|
43
|
+
step("Switching workload for domain '#{domain}' to '#{maintenance_workload}'") do
|
44
|
+
cp.set_domain_workload(domain_data, maintenance_workload)
|
45
|
+
|
46
|
+
# Give it a bit of time for the domain to update
|
47
|
+
sleep 30
|
48
|
+
end
|
49
|
+
|
50
|
+
progress.puts
|
51
|
+
|
52
|
+
# Stop all other workloads
|
53
|
+
Cpl::Cli.start(["ps:stop", "-a", config.app, "--wait"])
|
54
|
+
|
55
|
+
progress.puts("\nMaintenance mode enabled for app '#{config.app}'.")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Command
|
4
|
+
class MaintenanceSetPage < Base
|
5
|
+
NAME = "maintenance:set-page"
|
6
|
+
USAGE = "maintenance:set-page PAGE_URL"
|
7
|
+
REQUIRES_ARGS = true
|
8
|
+
OPTIONS = [
|
9
|
+
app_option(required: true)
|
10
|
+
].freeze
|
11
|
+
DESCRIPTION = "Sets the page for maintenance mode"
|
12
|
+
LONG_DESCRIPTION = <<~DESC
|
13
|
+
- Sets the page for maintenance mode
|
14
|
+
- Only works if the maintenance workload uses the `shakacode/maintenance-mode` image
|
15
|
+
- Will set the URL as an env var `PAGE_URL` on the maintenance workload
|
16
|
+
- Optionally specify the maintenance workload through `maintenance_workload` in the `.controlplane/controlplane.yml` file (defaults to 'maintenance')
|
17
|
+
DESC
|
18
|
+
|
19
|
+
def call
|
20
|
+
maintenance_workload = config.current[:maintenance_workload] || "maintenance"
|
21
|
+
|
22
|
+
maintenance_workload_data = cp.fetch_workload!(maintenance_workload)
|
23
|
+
maintenance_workload_data.dig("spec", "containers").each do |container|
|
24
|
+
next unless container["image"].match?(%r{^shakacode/maintenance-mode})
|
25
|
+
|
26
|
+
container_name = container["name"]
|
27
|
+
page_url = config.args.first
|
28
|
+
step("Setting '#{page_url}' as the page for maintenance mode") do
|
29
|
+
cp.set_workload_env_var(maintenance_workload, container: container_name, name: "PAGE_URL", value: page_url)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/command/no_command.rb
CHANGED
@@ -39,7 +39,7 @@ module Command
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def copy_image_from_upstream
|
42
|
-
|
42
|
+
Cpl::Cli.start(["copy-image-from-upstream", "-a", config.app, "-t", config.options[:upstream_token]])
|
43
43
|
progress.puts
|
44
44
|
end
|
45
45
|
|
@@ -52,7 +52,7 @@ module Command
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def deploy_image
|
55
|
-
|
55
|
+
Cpl::Cli.start(["deploy-image", "-a", config.app])
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
data/lib/command/ps_start.rb
CHANGED
@@ -5,7 +5,8 @@ module Command
|
|
5
5
|
NAME = "ps:start"
|
6
6
|
OPTIONS = [
|
7
7
|
app_option(required: true),
|
8
|
-
workload_option
|
8
|
+
workload_option,
|
9
|
+
wait_option("workload to be ready")
|
9
10
|
].freeze
|
10
11
|
DESCRIPTION = "Starts workloads in app"
|
11
12
|
LONG_DESCRIPTION = <<~DESC
|
@@ -22,12 +23,28 @@ module Command
|
|
22
23
|
EX
|
23
24
|
|
24
25
|
def call
|
25
|
-
workloads = [config.options[:workload]] if config.options[:workload]
|
26
|
-
workloads ||= config[:app_workloads] + config[:additional_workloads]
|
26
|
+
@workloads = [config.options[:workload]] if config.options[:workload]
|
27
|
+
@workloads ||= config[:app_workloads] + config[:additional_workloads]
|
27
28
|
|
28
|
-
workloads.reverse_each do |workload|
|
29
|
+
@workloads.reverse_each do |workload|
|
29
30
|
step("Starting workload '#{workload}'") do
|
30
|
-
cp.
|
31
|
+
cp.set_workload_suspend(workload, false)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
wait_for_ready if config.options[:wait]
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def wait_for_ready
|
41
|
+
progress.puts
|
42
|
+
|
43
|
+
@workloads.reverse_each do |workload|
|
44
|
+
step("Waiting for workload '#{workload}' to be ready", retry_on_failure: true) do
|
45
|
+
cp.fetch_workload_deployments(workload)&.dig("items")&.any? do |item|
|
46
|
+
item.dig("status", "ready")
|
47
|
+
end
|
31
48
|
end
|
32
49
|
end
|
33
50
|
end
|
data/lib/command/ps_stop.rb
CHANGED
@@ -5,7 +5,8 @@ module Command
|
|
5
5
|
NAME = "ps:stop"
|
6
6
|
OPTIONS = [
|
7
7
|
app_option(required: true),
|
8
|
-
workload_option
|
8
|
+
workload_option,
|
9
|
+
wait_option("workload to be not ready")
|
9
10
|
].freeze
|
10
11
|
DESCRIPTION = "Stops workloads in app"
|
11
12
|
LONG_DESCRIPTION = <<~DESC
|
@@ -22,12 +23,28 @@ module Command
|
|
22
23
|
EX
|
23
24
|
|
24
25
|
def call
|
25
|
-
workloads = [config.options[:workload]] if config.options[:workload]
|
26
|
-
workloads ||= config[:app_workloads] + config[:additional_workloads]
|
26
|
+
@workloads = [config.options[:workload]] if config.options[:workload]
|
27
|
+
@workloads ||= config[:app_workloads] + config[:additional_workloads]
|
27
28
|
|
28
|
-
workloads.each do |workload|
|
29
|
+
@workloads.each do |workload|
|
29
30
|
step("Stopping workload '#{workload}'") do
|
30
|
-
cp.
|
31
|
+
cp.set_workload_suspend(workload, true)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
wait_for_not_ready if config.options[:wait]
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def wait_for_not_ready
|
41
|
+
progress.puts
|
42
|
+
|
43
|
+
@workloads.each do |workload|
|
44
|
+
step("Waiting for workload '#{workload}' to be not ready", retry_on_failure: true) do
|
45
|
+
cp.fetch_workload_deployments(workload)&.dig("items")&.all? do |item|
|
46
|
+
!item.dig("status", "ready")
|
47
|
+
end
|
31
48
|
end
|
32
49
|
end
|
33
50
|
end
|
data/lib/command/run.rb
CHANGED
@@ -10,13 +10,15 @@ module Command
|
|
10
10
|
app_option(required: true),
|
11
11
|
image_option,
|
12
12
|
workload_option,
|
13
|
-
use_local_token_option
|
13
|
+
use_local_token_option,
|
14
|
+
terminal_size_option
|
14
15
|
].freeze
|
15
16
|
DESCRIPTION = "Runs one-off **_interactive_** replicas (analog of `heroku run`)"
|
16
17
|
LONG_DESCRIPTION = <<~DESC
|
17
18
|
- Runs one-off **_interactive_** replicas (analog of `heroku run`)
|
18
19
|
- Uses `Standard` workload type and `cpln exec` as the execution method, with CLI streaming
|
19
20
|
- May not work correctly with tasks that last over 5 minutes (there's a Control Plane scaling bug at the moment)
|
21
|
+
- If `fix_terminal_size` is `true` in the `.controlplane/controlplane.yml` file, the remote terminal size will be fixed to match the local terminal size (may also be overriden through `--terminal-size`)
|
20
22
|
|
21
23
|
> **IMPORTANT:** Useful for development where it's needed for interaction, and where network connection drops and
|
22
24
|
> task crashing are tolerable. For production tasks, it's better to use `cpl run:detached`.
|
@@ -26,6 +28,12 @@ module Command
|
|
26
28
|
# Opens shell (bash by default).
|
27
29
|
cpl run -a $APP_NAME
|
28
30
|
|
31
|
+
# Need to quote COMMAND if setting ENV value or passing args.
|
32
|
+
cpl run 'LOG_LEVEL=warn rails db:migrate' -a $APP_NAME
|
33
|
+
|
34
|
+
# COMMAND may also be passed at the end (in this case, no need to quote).
|
35
|
+
cpl run -a $APP_NAME -- rails db:migrate
|
36
|
+
|
29
37
|
# Runs command, displays output, and exits shell.
|
30
38
|
cpl run ls / -a $APP_NAME
|
31
39
|
cpl run rails db:migrate:status -a $APP_NAME
|
@@ -37,11 +45,11 @@ module Command
|
|
37
45
|
cpl run rails db:migrate -a $APP_NAME --image appimage:123 # Exact image name
|
38
46
|
cpl run rails db:migrate -a $APP_NAME --image latest # Latest sequential image
|
39
47
|
|
40
|
-
# Uses a different workload
|
48
|
+
# Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
|
41
49
|
cpl run bash -a $APP_NAME -w other-workload
|
42
50
|
|
43
51
|
# Overrides remote CPLN_TOKEN env variable with local token.
|
44
|
-
# Useful when
|
52
|
+
# Useful when superuser rights are needed in remote container.
|
45
53
|
cpl run bash -a $APP_NAME --use-local-token
|
46
54
|
```
|
47
55
|
EX
|
@@ -84,7 +92,7 @@ module Command
|
|
84
92
|
|
85
93
|
# Ensure no scaling
|
86
94
|
spec["defaultOptions"]["autoscaling"]["minScale"] = 1
|
87
|
-
spec["defaultOptions"]["autoscaling"]["
|
95
|
+
spec["defaultOptions"]["autoscaling"]["maxScale"] = 1
|
88
96
|
spec["defaultOptions"]["capacityAI"] = false
|
89
97
|
|
90
98
|
# Override image if specified
|
@@ -104,7 +112,7 @@ module Command
|
|
104
112
|
cp.apply("kind" => "workload", "name" => one_off, "spec" => spec)
|
105
113
|
end
|
106
114
|
|
107
|
-
def runner_script
|
115
|
+
def runner_script # rubocop:disable Metrics/MethodLength
|
108
116
|
script = Scripts.helpers_cleanup
|
109
117
|
|
110
118
|
if config.options["use_local_token"]
|
@@ -114,6 +122,16 @@ module Command
|
|
114
122
|
SHELL
|
115
123
|
end
|
116
124
|
|
125
|
+
# NOTE: fixes terminal size to match local terminal
|
126
|
+
if config.current[:fix_terminal_size] || config.options[:terminal_size]
|
127
|
+
if config.options[:terminal_size]
|
128
|
+
rows, cols = config.options[:terminal_size].split(",")
|
129
|
+
else
|
130
|
+
rows, cols = `stty -a`.match(/(\d+)\s*rows;\s*(\d+)\s*columns/).captures
|
131
|
+
end
|
132
|
+
script += "stty rows #{rows}\nstty cols #{cols}\n" if rows && cols
|
133
|
+
end
|
134
|
+
|
117
135
|
script += args_join(config.args)
|
118
136
|
script
|
119
137
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Command
|
4
|
+
class RunCleanup < Base
|
5
|
+
NAME = "run:cleanup"
|
6
|
+
OPTIONS = [
|
7
|
+
app_option(required: true),
|
8
|
+
skip_confirm_option
|
9
|
+
].freeze
|
10
|
+
DESCRIPTION = "Deletes stale run workloads for an app"
|
11
|
+
LONG_DESCRIPTION = <<~DESC
|
12
|
+
- Deletes stale run workloads for an app
|
13
|
+
- Workloads are considered stale based on how many days since created
|
14
|
+
- `stale_run_workload_created_days` in the `.controlplane/controlplane.yml` file specifies the number of days after created that the workload is considered stale
|
15
|
+
- Will ask for explicit user confirmation of deletion
|
16
|
+
DESC
|
17
|
+
|
18
|
+
def call # rubocop:disable Metrics/MethodLength
|
19
|
+
return progress.puts("No stale run workloads found.") if stale_run_workloads.empty?
|
20
|
+
|
21
|
+
progress.puts("Stale run workloads:")
|
22
|
+
stale_run_workloads.each do |workload|
|
23
|
+
progress.puts(" #{workload[:name]} " \
|
24
|
+
"(#{Shell.color("#{workload[:date]} - #{workload[:days]} days ago", :red)})")
|
25
|
+
end
|
26
|
+
|
27
|
+
return unless confirm_delete
|
28
|
+
|
29
|
+
progress.puts
|
30
|
+
stale_run_workloads.each do |workload|
|
31
|
+
delete_workload(workload)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def app_matches?(app, app_name, app_options)
|
38
|
+
app == app_name.to_s || (app_options[:match_if_app_name_starts_with] && app.start_with?(app_name.to_s))
|
39
|
+
end
|
40
|
+
|
41
|
+
def find_app_options(app)
|
42
|
+
@app_options ||= {}
|
43
|
+
@app_options[app] ||= config.apps.find do |app_name, app_options|
|
44
|
+
app_matches?(app, app_name, app_options)
|
45
|
+
end&.last
|
46
|
+
end
|
47
|
+
|
48
|
+
def find_workloads(app)
|
49
|
+
app_options = find_app_options(app)
|
50
|
+
return [] if app_options.nil?
|
51
|
+
|
52
|
+
(app_options[:app_workloads] + app_options[:additional_workloads] + [app_options[:one_off_workload]]).uniq
|
53
|
+
end
|
54
|
+
|
55
|
+
def stale_run_workloads # rubocop:disable Metrics/MethodLength
|
56
|
+
@stale_run_workloads ||=
|
57
|
+
begin
|
58
|
+
defined_workloads = find_workloads(config.app)
|
59
|
+
|
60
|
+
run_workloads = []
|
61
|
+
|
62
|
+
now = DateTime.now
|
63
|
+
stale_run_workload_created_days = config[:stale_run_workload_created_days]
|
64
|
+
|
65
|
+
workloads = cp.query_workloads("-run-", partial_match: true)["items"]
|
66
|
+
workloads.each do |workload|
|
67
|
+
workload_name = workload["name"]
|
68
|
+
|
69
|
+
original_workload_name, workload_number = workload_name.split("-run-")
|
70
|
+
next unless defined_workloads.include?(original_workload_name) && workload_number.match?(/^\d{4}$/)
|
71
|
+
|
72
|
+
created_date = DateTime.parse(workload["created"])
|
73
|
+
diff_in_days = (now - created_date).to_i
|
74
|
+
next unless diff_in_days >= stale_run_workload_created_days
|
75
|
+
|
76
|
+
run_workloads.push({
|
77
|
+
name: workload_name,
|
78
|
+
date: created_date,
|
79
|
+
days: diff_in_days
|
80
|
+
})
|
81
|
+
end
|
82
|
+
|
83
|
+
run_workloads
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def confirm_delete
|
88
|
+
return true if config.options[:yes]
|
89
|
+
|
90
|
+
Shell.confirm("\nAre you sure you want to delete these #{stale_run_workloads.length} run workloads?")
|
91
|
+
end
|
92
|
+
|
93
|
+
def delete_workload(workload)
|
94
|
+
step("Deleting run workload '#{workload[:name]}'") do
|
95
|
+
cp.delete_workload(workload[:name])
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/command/run_detached.rb
CHANGED
@@ -22,9 +22,12 @@ module Command
|
|
22
22
|
```sh
|
23
23
|
cpl run:detached rails db:prepare -a $APP_NAME
|
24
24
|
|
25
|
-
# Need to quote COMMAND if setting ENV value or passing args
|
25
|
+
# Need to quote COMMAND if setting ENV value or passing args.
|
26
26
|
cpl run:detached 'LOG_LEVEL=warn rails db:migrate' -a $APP_NAME
|
27
27
|
|
28
|
+
# COMMAND may also be passed at the end (in this case, no need to quote).
|
29
|
+
cpl run:detached -a $APP_NAME -- rails db:migrate
|
30
|
+
|
28
31
|
# Uses some other image.
|
29
32
|
cpl run:detached rails db:migrate -a $APP_NAME --image /some/full/image/path
|
30
33
|
|
@@ -35,7 +38,7 @@ module Command
|
|
35
38
|
cpl run:detached rails db:migrate -a $APP_NAME --image appimage:123 # Exact image name
|
36
39
|
cpl run:detached rails db:migrate -a $APP_NAME --image latest # Latest sequential image
|
37
40
|
|
38
|
-
# Uses a different workload
|
41
|
+
# Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
|
39
42
|
cpl run:detached rails db:migrate:status -a $APP_NAME -w other-workload
|
40
43
|
```
|
41
44
|
EX
|
@@ -79,7 +82,7 @@ module Command
|
|
79
82
|
|
80
83
|
# Ensure no scaling
|
81
84
|
spec["defaultOptions"]["autoscaling"]["minScale"] = 1
|
82
|
-
spec["defaultOptions"]["autoscaling"]["
|
85
|
+
spec["defaultOptions"]["autoscaling"]["maxScale"] = 1
|
83
86
|
spec["defaultOptions"]["capacityAI"] = false
|
84
87
|
|
85
88
|
# Override image if specified
|
data/lib/command/setup_app.rb
CHANGED
@@ -14,16 +14,16 @@ module Command
|
|
14
14
|
DESC
|
15
15
|
|
16
16
|
def call
|
17
|
-
templates = config[:setup]
|
17
|
+
templates = config[:setup]
|
18
18
|
|
19
19
|
app = cp.fetch_gvc
|
20
20
|
if app
|
21
21
|
raise "App '#{config.app}' already exists. If you want to update this app, " \
|
22
22
|
"either run 'cpl delete -a #{config.app}' and then re-run this command, " \
|
23
|
-
"or run 'cpl apply-template #{templates} -a #{config.app}'."
|
23
|
+
"or run 'cpl apply-template #{templates.join(' ')} -a #{config.app}'."
|
24
24
|
end
|
25
25
|
|
26
|
-
|
26
|
+
Cpl::Cli.start(["apply-template", *templates, "-a", config.app])
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
data/lib/core/config.rb
CHANGED
@@ -15,24 +15,15 @@ class Config
|
|
15
15
|
@app = options[:app]
|
16
16
|
|
17
17
|
load_app_config
|
18
|
-
|
19
|
-
@apps = config[:apps]
|
20
|
-
|
21
|
-
pick_current_config if app
|
22
|
-
warn_deprecated_options if current
|
18
|
+
load_apps
|
23
19
|
end
|
24
20
|
|
25
21
|
def [](key)
|
26
22
|
ensure_current_config!
|
27
23
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
elsif old_key && current.key?(old_key)
|
32
|
-
current.fetch(old_key)
|
33
|
-
else
|
34
|
-
raise "Can't find option '#{key}' for app '#{app}' in 'controlplane.yml'."
|
35
|
-
end
|
24
|
+
raise "Can't find option '#{key}' for app '#{app}' in 'controlplane.yml'." unless current.key?(key)
|
25
|
+
|
26
|
+
current.fetch(key)
|
36
27
|
end
|
37
28
|
|
38
29
|
def script_path
|
@@ -49,8 +40,8 @@ class Config
|
|
49
40
|
raise "Can't find current config, please specify an app." unless current
|
50
41
|
end
|
51
42
|
|
52
|
-
def ensure_current_config_app!(
|
53
|
-
raise "Can't find app '#{
|
43
|
+
def ensure_current_config_app!(app_name)
|
44
|
+
raise "Can't find app '#{app_name}' in 'controlplane.yml'." unless current
|
54
45
|
end
|
55
46
|
|
56
47
|
def ensure_config!
|
@@ -61,20 +52,36 @@ class Config
|
|
61
52
|
raise "Can't find key 'apps' in 'controlplane.yml'." unless config[:apps]
|
62
53
|
end
|
63
54
|
|
64
|
-
def ensure_config_app!(
|
65
|
-
raise "App '#{
|
55
|
+
def ensure_config_app!(app_name, app_options)
|
56
|
+
raise "App '#{app_name}' is empty in 'controlplane.yml'." unless app_options
|
57
|
+
end
|
58
|
+
|
59
|
+
def app_matches_current?(app_name, app_options)
|
60
|
+
app && (app_name.to_s == app || (app_options[:match_if_app_name_starts_with] && app.start_with?(app_name.to_s)))
|
66
61
|
end
|
67
62
|
|
68
|
-
def pick_current_config
|
69
|
-
|
70
|
-
|
71
|
-
|
63
|
+
def pick_current_config(app_name, app_options)
|
64
|
+
@current = app_options
|
65
|
+
@org = self[:cpln_org]
|
66
|
+
ensure_current_config_app!(app_name)
|
67
|
+
end
|
68
|
+
|
69
|
+
def load_apps # rubocop:disable Metrics/MethodLength
|
70
|
+
@apps = config[:apps].to_h do |app_name, app_options|
|
71
|
+
ensure_config_app!(app_name, app_options)
|
72
|
+
|
73
|
+
app_options_with_new_keys = app_options.to_h do |key, value|
|
74
|
+
new_key = new_option_keys[key]
|
75
|
+
new_key ? [new_key, value] : [key, value]
|
76
|
+
end
|
77
|
+
|
78
|
+
if app_matches_current?(app_name, app_options_with_new_keys)
|
79
|
+
pick_current_config(app_name, app_options_with_new_keys)
|
80
|
+
warn_deprecated_options(app_options)
|
81
|
+
end
|
72
82
|
|
73
|
-
|
74
|
-
@org = self[:cpln_org]
|
75
|
-
break
|
83
|
+
[app_name, app_options_with_new_keys]
|
76
84
|
end
|
77
|
-
ensure_current_config_app!(app)
|
78
85
|
end
|
79
86
|
|
80
87
|
def load_app_config
|
@@ -100,19 +107,19 @@ class Config
|
|
100
107
|
end
|
101
108
|
end
|
102
109
|
|
103
|
-
def
|
110
|
+
def new_option_keys
|
104
111
|
{
|
105
|
-
|
106
|
-
|
107
|
-
|
112
|
+
org: :cpln_org,
|
113
|
+
location: :default_location,
|
114
|
+
prefix: :match_if_app_name_starts_with
|
108
115
|
}
|
109
116
|
end
|
110
117
|
|
111
|
-
def warn_deprecated_options
|
112
|
-
deprecated_option_keys =
|
118
|
+
def warn_deprecated_options(app_options)
|
119
|
+
deprecated_option_keys = new_option_keys.filter { |old_key| app_options.key?(old_key) }
|
113
120
|
return if deprecated_option_keys.empty?
|
114
121
|
|
115
|
-
deprecated_option_keys.each do |
|
122
|
+
deprecated_option_keys.each do |old_key, new_key|
|
116
123
|
Shell.warn_deprecated("Option '#{old_key}' is deprecated, " \
|
117
124
|
"please use '#{new_key}' instead (in 'controlplane.yml').")
|
118
125
|
end
|