cpl 1.1.2 → 1.3.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/.github/workflows/rspec.yml +1 -1
- data/.overcommit.yml +3 -0
- data/CHANGELOG.md +47 -2
- data/CONTRIBUTING.md +2 -6
- data/Gemfile.lock +8 -8
- data/README.md +57 -15
- data/docs/commands.md +29 -23
- data/docs/dns.md +9 -0
- data/docs/migrating.md +3 -3
- data/examples/controlplane.yml +67 -4
- data/lib/command/apply_template.rb +2 -1
- data/lib/command/base.rb +62 -0
- data/lib/command/build_image.rb +5 -1
- data/lib/command/config.rb +0 -5
- data/lib/command/copy_image_from_upstream.rb +5 -4
- data/lib/command/delete.rb +40 -11
- data/lib/command/env.rb +1 -0
- data/lib/command/generate.rb +45 -0
- data/lib/command/info.rb +15 -33
- data/lib/command/latest_image.rb +1 -0
- data/lib/command/maintenance.rb +9 -4
- data/lib/command/maintenance_off.rb +8 -4
- data/lib/command/maintenance_on.rb +8 -4
- data/lib/command/no_command.rb +1 -0
- data/lib/command/ps.rb +5 -1
- data/lib/command/run.rb +20 -23
- data/lib/command/run_detached.rb +38 -30
- data/lib/command/setup_app.rb +3 -3
- data/lib/command/version.rb +1 -0
- data/lib/core/config.rb +194 -66
- data/lib/core/controlplane.rb +28 -7
- data/lib/core/controlplane_api.rb +13 -1
- data/lib/core/controlplane_api_direct.rb +18 -2
- data/lib/core/helpers.rb +16 -0
- data/lib/core/shell.rb +25 -3
- data/lib/cpl/version.rb +1 -1
- data/lib/cpl.rb +32 -3
- data/lib/generator_templates/Dockerfile +27 -0
- data/lib/generator_templates/controlplane.yml +57 -0
- data/lib/generator_templates/entrypoint.sh +8 -0
- data/lib/generator_templates/templates/gvc.yml +21 -0
- data/lib/generator_templates/templates/postgres.yml +176 -0
- data/lib/generator_templates/templates/rails.yml +36 -0
- data/script/check_cpln_links +45 -0
- metadata +14 -3
data/lib/command/run.rb
CHANGED
@@ -10,6 +10,7 @@ module Command
|
|
10
10
|
app_option(required: true),
|
11
11
|
image_option,
|
12
12
|
workload_option,
|
13
|
+
location_option,
|
13
14
|
use_local_token_option,
|
14
15
|
terminal_size_option
|
15
16
|
].freeze
|
@@ -17,7 +18,6 @@ module Command
|
|
17
18
|
LONG_DESCRIPTION = <<~DESC
|
18
19
|
- Runs one-off **_interactive_** replicas (analog of `heroku run`)
|
19
20
|
- Uses `Standard` workload type and `cpln exec` as the execution method, with CLI streaming
|
20
|
-
- May not work correctly with tasks that last over 5 minutes (there's a Control Plane scaling bug at the moment)
|
21
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`)
|
22
22
|
|
23
23
|
> **IMPORTANT:** Useful for development where it's needed for interaction, and where network connection drops and
|
@@ -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
|
-
@location = config
|
61
|
-
@
|
62
|
-
@
|
57
|
+
@location = config.location
|
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
|
@@ -111,7 +108,7 @@ module Command
|
|
111
108
|
end
|
112
109
|
|
113
110
|
# Create workload clone
|
114
|
-
cp.apply_hash("kind" => "workload", "name" =>
|
111
|
+
cp.apply_hash("kind" => "workload", "name" => workload_clone, "spec" => spec)
|
115
112
|
end
|
116
113
|
|
117
114
|
def runner_script # rubocop:disable Metrics/MethodLength
|
@@ -141,7 +138,7 @@ module Command
|
|
141
138
|
def run_in_replica
|
142
139
|
progress.puts("Connecting...\n\n")
|
143
140
|
command = %(bash -c 'eval "$CONTROLPLANE_RUNNER"')
|
144
|
-
cp.workload_exec(
|
141
|
+
cp.workload_exec(workload_clone, location: location, container: container, command: command)
|
145
142
|
end
|
146
143
|
end
|
147
144
|
end
|
data/lib/command/run_detached.rb
CHANGED
@@ -9,7 +9,9 @@ module Command
|
|
9
9
|
app_option(required: true),
|
10
10
|
image_option,
|
11
11
|
workload_option,
|
12
|
-
|
12
|
+
location_option,
|
13
|
+
use_local_token_option,
|
14
|
+
clean_on_failure_option
|
13
15
|
].freeze
|
14
16
|
DESCRIPTION = "Runs one-off **_non-interactive_** replicas (close analog of `heroku run:detached`)"
|
15
17
|
LONG_DESCRIPTION = <<~DESC
|
@@ -18,50 +20,46 @@ module Command
|
|
18
20
|
- Implemented with only async execution methods, more suitable for production tasks
|
19
21
|
- Has alternative log fetch implementation with only JSON-polling and no WebSockets
|
20
22
|
- Less responsive but more stable, useful for CI tasks
|
23
|
+
- Deletes the workload whenever finished with success
|
24
|
+
- Deletes the workload whenever finished with failure by default
|
25
|
+
- Use `--no-clean-on-failure` to disable cleanup to help with debugging failed runs
|
21
26
|
DESC
|
22
27
|
EXAMPLES = <<~EX
|
23
28
|
```sh
|
24
29
|
cpl run:detached rails db:prepare -a $APP_NAME
|
25
30
|
|
26
31
|
# Need to quote COMMAND if setting ENV value or passing args.
|
27
|
-
cpl run:detached 'LOG_LEVEL=warn rails db:migrate' -a $APP_NAME
|
28
|
-
|
29
|
-
# COMMAND may also be passed at the end.
|
30
32
|
cpl run:detached -a $APP_NAME -- 'LOG_LEVEL=warn rails db:migrate'
|
31
33
|
|
32
34
|
# Uses a different image (which may not be promoted yet).
|
33
|
-
cpl run:detached
|
34
|
-
cpl run:detached
|
35
|
+
cpl run:detached -a $APP_NAME --image appimage:123 -- rails db:migrate # Exact image name
|
36
|
+
cpl run:detached -a $APP_NAME --image latest -- rails db:migrate # Latest sequential image
|
35
37
|
|
36
38
|
# Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
|
37
|
-
cpl run:detached
|
39
|
+
cpl run:detached -a $APP_NAME -w other-workload -- rails db:migrate:status
|
38
40
|
|
39
41
|
# Overrides remote CPLN_TOKEN env variable with local token.
|
40
42
|
# Useful when superuser rights are needed in remote container.
|
41
|
-
cpl run:detached
|
43
|
+
cpl run:detached -a $APP_NAME --use-local-token -- rails db:migrate:status
|
42
44
|
```
|
43
45
|
EX
|
44
46
|
|
45
47
|
WORKLOAD_SLEEP_CHECK = 2
|
46
48
|
|
47
|
-
attr_reader :location, :
|
49
|
+
attr_reader :location, :workload_to_clone, :workload_clone, :container
|
48
50
|
|
49
|
-
def call
|
50
|
-
@location = config
|
51
|
-
@
|
52
|
-
@
|
51
|
+
def call
|
52
|
+
@location = config.location
|
53
|
+
@workload_to_clone = config.options["workload"] || config[:one_off_workload]
|
54
|
+
@workload_clone = "#{workload_to_clone}-runner-#{random_four_digits}"
|
53
55
|
|
54
|
-
step("Cloning workload '#{
|
56
|
+
step("Cloning workload '#{workload_to_clone}' on app '#{config.options[:app]}' to '#{workload_clone}'") do
|
55
57
|
clone_workload
|
56
58
|
end
|
57
59
|
|
58
|
-
wait_for_workload(
|
60
|
+
wait_for_workload(workload_clone)
|
59
61
|
show_logs_waiting
|
60
62
|
ensure
|
61
|
-
if cp.fetch_workload(one_off)
|
62
|
-
progress.puts
|
63
|
-
ensure_workload_deleted(one_off)
|
64
|
-
end
|
65
63
|
exit(1) if @crashed
|
66
64
|
end
|
67
65
|
|
@@ -69,8 +67,8 @@ module Command
|
|
69
67
|
|
70
68
|
def clone_workload # rubocop:disable Metrics/MethodLength
|
71
69
|
# Get base specs of workload
|
72
|
-
spec = cp.fetch_workload!(
|
73
|
-
container_spec = spec["containers"].detect { _1["name"] ==
|
70
|
+
spec = cp.fetch_workload!(workload_to_clone).fetch("spec")
|
71
|
+
container_spec = spec["containers"].detect { _1["name"] == workload_to_clone } || spec["containers"].first
|
74
72
|
@container = container_spec["name"]
|
75
73
|
|
76
74
|
# remove other containers if any
|
@@ -104,7 +102,7 @@ module Command
|
|
104
102
|
container_spec["env"] << { "name" => "CONTROLPLANE_RUNNER", "value" => runner_script }
|
105
103
|
|
106
104
|
# Create workload clone
|
107
|
-
cp.apply_hash("kind" => "workload", "name" =>
|
105
|
+
cp.apply_hash("kind" => "workload", "name" => workload_clone, "spec" => spec)
|
108
106
|
end
|
109
107
|
|
110
108
|
def runner_script # rubocop:disable Metrics/MethodLength
|
@@ -118,12 +116,20 @@ module Command
|
|
118
116
|
end
|
119
117
|
|
120
118
|
script += <<~SHELL
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
119
|
+
crashed=0
|
120
|
+
if ! eval "#{args_join(config.args)}"; then
|
121
|
+
crashed=1
|
122
|
+
echo "----- CRASHED -----"
|
123
|
+
fi
|
124
|
+
clean_on_failure=#{config.options[:clean_on_failure] ? 1 : 0}
|
125
|
+
if [ $crashed -eq 0 ] || [ $clean_on_failure -eq 1 ]; then
|
126
|
+
echo "-- FINISHED RUNNER SCRIPT, DELETING WORKLOAD --"
|
127
|
+
sleep 30 # grace time for logs propagation
|
128
|
+
curl ${CPLN_ENDPOINT}${CPLN_WORKLOAD} -H "Authorization: ${CONTROLPLANE_TOKEN}" -X DELETE -s -o /dev/null
|
129
|
+
while true; do sleep 1; done # wait for SIGTERM
|
130
|
+
else
|
131
|
+
echo "-- FINISHED RUNNER SCRIPT --"
|
132
|
+
fi
|
127
133
|
SHELL
|
128
134
|
|
129
135
|
script
|
@@ -132,7 +138,8 @@ module Command
|
|
132
138
|
def show_logs_waiting # rubocop:disable Metrics/MethodLength
|
133
139
|
progress.puts("Scheduled, fetching logs (it's a cron job, so it may take up to a minute to start)...\n\n")
|
134
140
|
begin
|
135
|
-
|
141
|
+
@finished = false
|
142
|
+
while cp.fetch_workload(workload_clone) && !@finished
|
136
143
|
sleep(WORKLOAD_SLEEP_CHECK)
|
137
144
|
print_uniq_logs
|
138
145
|
end
|
@@ -150,6 +157,7 @@ module Command
|
|
150
157
|
|
151
158
|
(entries - @printed_log_entries).sort.each do |(_ts, val)|
|
152
159
|
@crashed = true if val.match?(/^----- CRASHED -----$/)
|
160
|
+
@finished = true if val.match?(/^-- FINISHED RUNNER SCRIPT(, DELETING WORKLOAD)? --$/)
|
153
161
|
puts val
|
154
162
|
end
|
155
163
|
|
@@ -157,7 +165,7 @@ module Command
|
|
157
165
|
end
|
158
166
|
|
159
167
|
def normalized_log_entries(from:, to:)
|
160
|
-
log = cp.log_get(workload:
|
168
|
+
log = cp.log_get(workload: workload_clone, from: from, to: to)
|
161
169
|
|
162
170
|
log["data"]["result"]
|
163
171
|
.each_with_object([]) { |obj, result| result.concat(obj["values"]) }
|
data/lib/command/setup_app.rb
CHANGED
@@ -9,12 +9,12 @@ module Command
|
|
9
9
|
DESCRIPTION = "Creates an app and all its workloads"
|
10
10
|
LONG_DESCRIPTION = <<~DESC
|
11
11
|
- Creates an app and all its workloads
|
12
|
-
- Specify the templates for the app and workloads through `
|
13
|
-
- This should
|
12
|
+
- Specify the templates for the app and workloads through `setup_app_templates` in the `.controlplane/controlplane.yml` file
|
13
|
+
- This should only be used for temporary apps like review apps, never for persistent apps like production (to update workloads for those, use 'cpl apply-template' instead)
|
14
14
|
DESC
|
15
15
|
|
16
16
|
def call
|
17
|
-
templates = config[:
|
17
|
+
templates = config[:setup_app_templates]
|
18
18
|
|
19
19
|
app = cp.fetch_gvc
|
20
20
|
if app
|
data/lib/command/version.rb
CHANGED
data/lib/core/config.rb
CHANGED
@@ -1,24 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "helpers"
|
4
|
+
|
3
5
|
class Config # rubocop:disable Metrics/ClassLength
|
4
|
-
attr_reader :
|
5
|
-
:org, :org_comes_from_env, :app, :apps, :app_dir,
|
6
|
+
attr_reader :org_comes_from_env, :app_comes_from_env,
|
6
7
|
# command line options
|
7
|
-
:args, :options
|
8
|
+
:args, :options, :required_options
|
9
|
+
|
10
|
+
include Helpers
|
8
11
|
|
9
|
-
|
12
|
+
CONFIG_FILE_LOCATION = ".controlplane/controlplane.yml"
|
10
13
|
|
11
|
-
def initialize(args, options)
|
14
|
+
def initialize(args, options, required_options)
|
12
15
|
@args = args
|
13
16
|
@options = options
|
14
|
-
@
|
15
|
-
@org_comes_from_env = false
|
16
|
-
@app = options[:app]
|
17
|
+
@required_options = required_options
|
17
18
|
|
18
|
-
|
19
|
-
load_apps
|
19
|
+
ensure_required_options!
|
20
20
|
|
21
21
|
Shell.verbose_mode(options[:verbose])
|
22
|
+
trace_mode = options[:trace]
|
23
|
+
return unless trace_mode
|
24
|
+
|
25
|
+
ControlplaneApiDirect.trace = trace_mode
|
26
|
+
Shell.warn("Trace mode is enabled, this will print sensitive information to the console.")
|
27
|
+
end
|
28
|
+
|
29
|
+
def org
|
30
|
+
@org ||= load_org_from_options || load_org_from_env || load_org_from_file
|
31
|
+
end
|
32
|
+
|
33
|
+
def app
|
34
|
+
@app ||= load_app_from_options || load_app_from_env
|
35
|
+
end
|
36
|
+
|
37
|
+
def location
|
38
|
+
@location ||= load_location_from_options || load_location_from_env || load_location_from_file
|
39
|
+
end
|
40
|
+
|
41
|
+
def domain
|
42
|
+
@domain ||= load_domain_from_options || load_domain_from_file
|
22
43
|
end
|
23
44
|
|
24
45
|
def [](key)
|
@@ -41,91 +62,122 @@ class Config # rubocop:disable Metrics/ClassLength
|
|
41
62
|
apps[app_name.to_sym]&.dig(:match_if_app_name_starts_with) || false
|
42
63
|
end
|
43
64
|
|
44
|
-
|
65
|
+
def app_dir
|
66
|
+
Pathname.new(config_file_path).parent.parent.to_s
|
67
|
+
end
|
45
68
|
|
46
|
-
def
|
47
|
-
|
69
|
+
def config
|
70
|
+
@config ||= begin
|
71
|
+
global_config = YAML.safe_load_file(config_file_path, symbolize_names: true, aliases: true)
|
72
|
+
ensure_config!(global_config)
|
73
|
+
ensure_config_apps!(global_config)
|
74
|
+
|
75
|
+
global_config
|
76
|
+
end
|
48
77
|
end
|
49
78
|
|
50
|
-
def
|
51
|
-
|
79
|
+
def apps
|
80
|
+
@apps ||= config[:apps].to_h do |app_name, app_options|
|
81
|
+
ensure_config_app!(app_name, app_options)
|
82
|
+
|
83
|
+
app_options_with_new_keys = app_options.to_h do |key, value|
|
84
|
+
new_key = new_option_keys[key]
|
85
|
+
new_key ? [new_key, value] : [key, value]
|
86
|
+
end
|
87
|
+
|
88
|
+
[app_name, app_options_with_new_keys]
|
89
|
+
end
|
52
90
|
end
|
53
91
|
|
54
|
-
def
|
55
|
-
return
|
92
|
+
def current
|
93
|
+
return unless app
|
56
94
|
|
57
|
-
|
58
|
-
|
95
|
+
@current ||= begin
|
96
|
+
app_config = find_app_config(app)
|
97
|
+
ensure_config_app!(app, app_config)
|
98
|
+
|
99
|
+
warn_deprecated_options(app_config)
|
100
|
+
|
101
|
+
app_config
|
102
|
+
end
|
59
103
|
end
|
60
104
|
|
61
|
-
def
|
62
|
-
|
105
|
+
def app_matches?(app_name1, app_name2, app_options)
|
106
|
+
app_name1 && app_name2 &&
|
107
|
+
(app_name1.to_s == app_name2.to_s ||
|
108
|
+
(app_options[:match_if_app_name_starts_with] && app_name1.to_s.start_with?(app_name2.to_s))
|
109
|
+
)
|
63
110
|
end
|
64
111
|
|
65
|
-
def
|
66
|
-
|
112
|
+
def find_app_config(app_name1)
|
113
|
+
@app_configs ||= {}
|
114
|
+
@app_configs[app_name1] ||= apps.find do |app_name2, app_config|
|
115
|
+
app_matches?(app_name1, app_name2, app_config)
|
116
|
+
end&.last
|
67
117
|
end
|
68
118
|
|
69
|
-
|
70
|
-
|
119
|
+
private
|
120
|
+
|
121
|
+
def ensure_current_config!
|
122
|
+
raise "Can't find current config, please specify an app." unless current
|
71
123
|
end
|
72
124
|
|
73
|
-
def
|
74
|
-
|
125
|
+
def ensure_config!(global_config)
|
126
|
+
raise "'controlplane.yml' is empty." unless global_config
|
75
127
|
end
|
76
128
|
|
77
|
-
def
|
78
|
-
|
79
|
-
|
129
|
+
def ensure_config_apps!(global_config)
|
130
|
+
raise "Can't find key 'apps' in 'controlplane.yml'." unless global_config[:apps]
|
131
|
+
end
|
80
132
|
|
81
|
-
|
82
|
-
|
83
|
-
else
|
84
|
-
@org = ENV.fetch("CPLN_ORG", nil)
|
85
|
-
@org_comes_from_env = true
|
86
|
-
end
|
87
|
-
ensure_current_config_org!(app_name)
|
133
|
+
def ensure_config_app!(app_name, app_options)
|
134
|
+
raise "Can't find config for app '#{app_name}' in 'controlplane.yml'." unless app_options
|
88
135
|
end
|
89
136
|
|
90
|
-
def
|
91
|
-
|
92
|
-
ensure_config_app!(app_name, app_options)
|
137
|
+
def ensure_app!
|
138
|
+
return if app
|
93
139
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
if app_matches_current?(app_name, app_options_with_new_keys)
|
100
|
-
pick_current_config(app_name, app_options_with_new_keys)
|
101
|
-
warn_deprecated_options(app_options)
|
102
|
-
end
|
140
|
+
raise "No app provided. " \
|
141
|
+
"The app can be provided either through the CPLN_APP env var " \
|
142
|
+
"('allow_app_override_by_env' must be set to true in 'controlplane.yml'), " \
|
143
|
+
"or the --app command option."
|
144
|
+
end
|
103
145
|
|
104
|
-
|
105
|
-
|
146
|
+
def ensure_org!
|
147
|
+
return if org
|
106
148
|
|
107
|
-
|
149
|
+
raise "No org provided. " \
|
150
|
+
"The org can be provided either through the CPLN_ORG env var " \
|
151
|
+
"('allow_org_override_by_env' must be set to true in 'controlplane.yml'), " \
|
152
|
+
"the --org command option, " \
|
153
|
+
"or the 'cpln_org' key in 'controlplane.yml'."
|
108
154
|
end
|
109
155
|
|
110
|
-
def
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
156
|
+
def ensure_required_options! # rubocop:disable Metrics/CyclomaticComplexity
|
157
|
+
ensure_app! if required_options.include?(:app)
|
158
|
+
ensure_org! if required_options.include?(:org) || app
|
159
|
+
|
160
|
+
missing_str = required_options
|
161
|
+
.reject { |option_name| %i[org app].include?(option_name) || options.key?(option_name) }
|
162
|
+
.map { |option_name| "--#{option_name}" }
|
163
|
+
.join(", ")
|
164
|
+
|
165
|
+
raise "Required options missing: #{missing_str}" unless missing_str.empty?
|
116
166
|
end
|
117
167
|
|
118
|
-
def
|
119
|
-
|
168
|
+
def config_file_path # rubocop:disable Metrics/MethodLength
|
169
|
+
@config_file_path ||= begin
|
170
|
+
path = Pathname.new(".").expand_path
|
120
171
|
|
121
|
-
|
122
|
-
|
123
|
-
|
172
|
+
loop do
|
173
|
+
config_file = path + CONFIG_FILE_LOCATION
|
174
|
+
break config_file if File.file?(config_file)
|
124
175
|
|
125
|
-
|
176
|
+
path = path.parent
|
126
177
|
|
127
|
-
|
128
|
-
|
178
|
+
if path.root?
|
179
|
+
raise "Can't find project config file at 'project_folder/#{CONFIG_FILE_LOCATION}', please create it."
|
180
|
+
end
|
129
181
|
end
|
130
182
|
end
|
131
183
|
end
|
@@ -135,10 +187,86 @@ class Config # rubocop:disable Metrics/ClassLength
|
|
135
187
|
org: :cpln_org,
|
136
188
|
location: :default_location,
|
137
189
|
prefix: :match_if_app_name_starts_with,
|
190
|
+
setup: :setup_app_templates,
|
138
191
|
old_image_retention_days: :image_retention_days
|
139
192
|
}
|
140
193
|
end
|
141
194
|
|
195
|
+
def load_app_from_env
|
196
|
+
app_from_env = strip_str_and_validate(ENV.fetch("CPLN_APP", nil))
|
197
|
+
return unless app_from_env
|
198
|
+
|
199
|
+
app_config = find_app_config(app_from_env)
|
200
|
+
ensure_config_app!(app_from_env, app_config)
|
201
|
+
|
202
|
+
key_exists = app_config.key?(:allow_app_override_by_env)
|
203
|
+
allowed_locally = key_exists && app_config[:allow_app_override_by_env]
|
204
|
+
allowed_globally = !key_exists && config[:allow_app_override_by_env]
|
205
|
+
return unless allowed_locally || allowed_globally
|
206
|
+
|
207
|
+
@app_comes_from_env = true
|
208
|
+
|
209
|
+
app_from_env
|
210
|
+
end
|
211
|
+
|
212
|
+
def load_app_from_options
|
213
|
+
app_from_options = strip_str_and_validate(options[:app])
|
214
|
+
return unless app_from_options
|
215
|
+
|
216
|
+
app_config = find_app_config(app_from_options)
|
217
|
+
ensure_config_app!(app_from_options, app_config)
|
218
|
+
|
219
|
+
app_from_options
|
220
|
+
end
|
221
|
+
|
222
|
+
def load_org_from_env
|
223
|
+
org_from_env = strip_str_and_validate(ENV.fetch("CPLN_ORG", nil))
|
224
|
+
return unless org_from_env
|
225
|
+
|
226
|
+
key_exists = current&.key?(:allow_org_override_by_env)
|
227
|
+
allowed_locally = key_exists && current[:allow_org_override_by_env]
|
228
|
+
allowed_globally = !key_exists && config[:allow_org_override_by_env]
|
229
|
+
return unless allowed_locally || allowed_globally
|
230
|
+
|
231
|
+
@org_comes_from_env = true
|
232
|
+
|
233
|
+
org_from_env
|
234
|
+
end
|
235
|
+
|
236
|
+
def load_org_from_options
|
237
|
+
strip_str_and_validate(options[:org])
|
238
|
+
end
|
239
|
+
|
240
|
+
def load_org_from_file
|
241
|
+
return unless current&.key?(:cpln_org)
|
242
|
+
|
243
|
+
strip_str_and_validate(current[:cpln_org])
|
244
|
+
end
|
245
|
+
|
246
|
+
def load_location_from_options
|
247
|
+
strip_str_and_validate(options[:location])
|
248
|
+
end
|
249
|
+
|
250
|
+
def load_location_from_env
|
251
|
+
strip_str_and_validate(ENV.fetch("CPLN_LOCATION", nil))
|
252
|
+
end
|
253
|
+
|
254
|
+
def load_location_from_file
|
255
|
+
return unless current&.key?(:default_location)
|
256
|
+
|
257
|
+
strip_str_and_validate(current.fetch(:default_location))
|
258
|
+
end
|
259
|
+
|
260
|
+
def load_domain_from_options
|
261
|
+
strip_str_and_validate(options[:domain])
|
262
|
+
end
|
263
|
+
|
264
|
+
def load_domain_from_file
|
265
|
+
return unless current&.key?(:default_domain)
|
266
|
+
|
267
|
+
strip_str_and_validate(current.fetch(:default_domain))
|
268
|
+
end
|
269
|
+
|
142
270
|
def warn_deprecated_options(app_options)
|
143
271
|
deprecated_option_keys = new_option_keys.select { |old_key| app_options.key?(old_key) }
|
144
272
|
return if deprecated_option_keys.empty?
|