cpl 0.6.0 → 0.7.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/.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
|