cpl 1.1.2 → 1.2.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/rspec.yml +1 -1
- data/CHANGELOG.md +23 -1
- data/CONTRIBUTING.md +2 -6
- data/Gemfile.lock +7 -7
- data/README.md +34 -11
- data/docs/commands.md +13 -5
- data/docs/migrating.md +3 -3
- data/examples/controlplane.yml +63 -4
- data/lib/command/apply_template.rb +2 -1
- data/lib/command/base.rb +31 -0
- data/lib/command/config.rb +0 -5
- 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 +5 -8
- data/lib/command/latest_image.rb +1 -0
- data/lib/command/maintenance.rb +1 -0
- data/lib/command/no_command.rb +1 -0
- data/lib/command/ps.rb +5 -1
- data/lib/command/run.rb +2 -2
- data/lib/command/run_detached.rb +2 -1
- data/lib/command/setup_app.rb +3 -3
- data/lib/command/version.rb +1 -0
- data/lib/core/config.rb +180 -66
- data/lib/core/controlplane.rb +15 -3
- data/lib/core/controlplane_api.rb +9 -1
- data/lib/core/controlplane_api_direct.rb +18 -2
- data/lib/core/helpers.rb +10 -0
- data/lib/core/shell.rb +25 -3
- data/lib/cpl/version.rb +1 -1
- data/lib/cpl.rb +28 -2
- 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
- metadata +11 -3
data/lib/command/info.rb
CHANGED
@@ -4,7 +4,6 @@ module Command
|
|
4
4
|
class Info < Base # rubocop:disable Metrics/ClassLength
|
5
5
|
NAME = "info"
|
6
6
|
OPTIONS = [
|
7
|
-
org_option,
|
8
7
|
app_option
|
9
8
|
].freeze
|
10
9
|
DESCRIPTION = "Displays the diff between defined/available apps/workloads (apps equal GVCs)"
|
@@ -17,7 +16,7 @@ module Command
|
|
17
16
|
DESC
|
18
17
|
EXAMPLES = <<~EX
|
19
18
|
```sh
|
20
|
-
# Shows diff for all apps in all orgs.
|
19
|
+
# Shows diff for all apps in all orgs (based on `.controlplane/controlplane.yml`).
|
21
20
|
cpl info
|
22
21
|
|
23
22
|
# Shows diff for all apps in a specific org.
|
@@ -27,6 +26,7 @@ module Command
|
|
27
26
|
cpl info -a $APP_NAME
|
28
27
|
```
|
29
28
|
EX
|
29
|
+
WITH_INFO_HEADER = false
|
30
30
|
|
31
31
|
def call
|
32
32
|
@missing_apps_workloads = {}
|
@@ -81,15 +81,12 @@ module Command
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
-
def orgs # rubocop:disable Metrics/
|
84
|
+
def orgs # rubocop:disable Metrics/MethodLength
|
85
85
|
result = []
|
86
86
|
|
87
|
-
if config.
|
88
|
-
result.push(config.
|
87
|
+
if config.org
|
88
|
+
result.push(config.org)
|
89
89
|
else
|
90
|
-
org_from_env = ENV.fetch("CPLN_ORG", nil)
|
91
|
-
result.push(org_from_env) if org_from_env
|
92
|
-
|
93
90
|
config.apps.each do |app_name, app_options|
|
94
91
|
next if config.app && !app_matches?(config.app, app_name, app_options)
|
95
92
|
|
data/lib/command/latest_image.rb
CHANGED
data/lib/command/maintenance.rb
CHANGED
@@ -14,6 +14,7 @@ module Command
|
|
14
14
|
- Optionally specify the maintenance workload through `maintenance_workload` in the `.controlplane/controlplane.yml` file (defaults to 'maintenance')
|
15
15
|
- 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
|
16
16
|
DESC
|
17
|
+
WITH_INFO_HEADER = false
|
17
18
|
|
18
19
|
def call # rubocop:disable Metrics/MethodLength
|
19
20
|
one_off_workload = config[:one_off_workload]
|
data/lib/command/no_command.rb
CHANGED
data/lib/command/ps.rb
CHANGED
@@ -5,6 +5,7 @@ module Command
|
|
5
5
|
NAME = "ps"
|
6
6
|
OPTIONS = [
|
7
7
|
app_option(required: true),
|
8
|
+
location_option,
|
8
9
|
workload_option
|
9
10
|
].freeze
|
10
11
|
DESCRIPTION = "Shows running replicas in app"
|
@@ -20,16 +21,19 @@ module Command
|
|
20
21
|
cpl ps -a $APP_NAME -w $WORKLOAD_NAME
|
21
22
|
```
|
22
23
|
EX
|
24
|
+
WITH_INFO_HEADER = false
|
23
25
|
|
24
26
|
def call
|
25
27
|
cp.fetch_gvc!
|
26
28
|
|
29
|
+
location = config.location
|
30
|
+
|
27
31
|
workloads = [config.options[:workload]] if config.options[:workload]
|
28
32
|
workloads ||= config[:app_workloads] + config[:additional_workloads]
|
29
33
|
workloads.each do |workload|
|
30
34
|
cp.fetch_workload!(workload)
|
31
35
|
|
32
|
-
result = cp.workload_get_replicas(workload, location:
|
36
|
+
result = cp.workload_get_replicas(workload, location: location)
|
33
37
|
result["items"].each { |replica| puts replica }
|
34
38
|
end
|
35
39
|
end
|
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
|
@@ -57,7 +57,7 @@ module Command
|
|
57
57
|
attr_reader :location, :workload, :one_off, :container
|
58
58
|
|
59
59
|
def call # rubocop:disable Metrics/MethodLength
|
60
|
-
@location = config
|
60
|
+
@location = config.location
|
61
61
|
@workload = config.options["workload"] || config[:one_off_workload]
|
62
62
|
@one_off = "#{workload}-run-#{rand(1000..9999)}"
|
63
63
|
|
data/lib/command/run_detached.rb
CHANGED
@@ -9,6 +9,7 @@ module Command
|
|
9
9
|
app_option(required: true),
|
10
10
|
image_option,
|
11
11
|
workload_option,
|
12
|
+
location_option,
|
12
13
|
use_local_token_option
|
13
14
|
].freeze
|
14
15
|
DESCRIPTION = "Runs one-off **_non-interactive_** replicas (close analog of `heroku run:detached`)"
|
@@ -47,7 +48,7 @@ module Command
|
|
47
48
|
attr_reader :location, :workload, :one_off, :container
|
48
49
|
|
49
50
|
def call # rubocop:disable Metrics/MethodLength
|
50
|
-
@location = config
|
51
|
+
@location = config.location
|
51
52
|
@workload = config.options["workload"] || config[:one_off_workload]
|
52
53
|
@one_off = "#{workload}-runner-#{rand(1000..9999)}"
|
53
54
|
|
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,41 @@
|
|
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
|
22
39
|
end
|
23
40
|
|
24
41
|
def [](key)
|
@@ -41,91 +58,122 @@ class Config # rubocop:disable Metrics/ClassLength
|
|
41
58
|
apps[app_name.to_sym]&.dig(:match_if_app_name_starts_with) || false
|
42
59
|
end
|
43
60
|
|
44
|
-
|
45
|
-
|
46
|
-
def ensure_current_config!
|
47
|
-
raise "Can't find current config, please specify an app." unless current
|
61
|
+
def app_dir
|
62
|
+
Pathname.new(config_file_path).parent.parent.to_s
|
48
63
|
end
|
49
64
|
|
50
|
-
def
|
51
|
-
|
65
|
+
def config
|
66
|
+
@config ||= begin
|
67
|
+
global_config = YAML.safe_load_file(config_file_path, symbolize_names: true, aliases: true)
|
68
|
+
ensure_config!(global_config)
|
69
|
+
ensure_config_apps!(global_config)
|
70
|
+
|
71
|
+
global_config
|
72
|
+
end
|
52
73
|
end
|
53
74
|
|
54
|
-
def
|
55
|
-
|
75
|
+
def apps
|
76
|
+
@apps ||= config[:apps].to_h do |app_name, app_options|
|
77
|
+
ensure_config_app!(app_name, app_options)
|
56
78
|
|
57
|
-
|
58
|
-
|
79
|
+
app_options_with_new_keys = app_options.to_h do |key, value|
|
80
|
+
new_key = new_option_keys[key]
|
81
|
+
new_key ? [new_key, value] : [key, value]
|
82
|
+
end
|
83
|
+
|
84
|
+
[app_name, app_options_with_new_keys]
|
85
|
+
end
|
59
86
|
end
|
60
87
|
|
61
|
-
def
|
62
|
-
|
88
|
+
def current
|
89
|
+
return unless app
|
90
|
+
|
91
|
+
@current ||= begin
|
92
|
+
app_config = find_app_config(app)
|
93
|
+
ensure_config_app!(app, app_config)
|
94
|
+
|
95
|
+
warn_deprecated_options(app_config)
|
96
|
+
|
97
|
+
app_config
|
98
|
+
end
|
63
99
|
end
|
64
100
|
|
65
|
-
|
66
|
-
|
101
|
+
private
|
102
|
+
|
103
|
+
def ensure_current_config!
|
104
|
+
raise "Can't find current config, please specify an app." unless current
|
67
105
|
end
|
68
106
|
|
69
|
-
def
|
70
|
-
raise "
|
107
|
+
def ensure_config!(global_config)
|
108
|
+
raise "'controlplane.yml' is empty." unless global_config
|
71
109
|
end
|
72
110
|
|
73
|
-
def
|
74
|
-
|
111
|
+
def ensure_config_apps!(global_config)
|
112
|
+
raise "Can't find key 'apps' in 'controlplane.yml'." unless global_config[:apps]
|
75
113
|
end
|
76
114
|
|
77
|
-
def
|
78
|
-
|
79
|
-
|
115
|
+
def ensure_config_app!(app_name, app_options)
|
116
|
+
raise "Can't find config for app '#{app_name}' in 'controlplane.yml'." unless app_options
|
117
|
+
end
|
80
118
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
end
|
87
|
-
ensure_current_config_org!(app_name)
|
119
|
+
def app_matches?(app_name1, app_name2, app_options)
|
120
|
+
app_name1 && app_name2 &&
|
121
|
+
(app_name1.to_s == app_name2.to_s ||
|
122
|
+
(app_options[:match_if_app_name_starts_with] && app_name1.to_s.start_with?(app_name2.to_s))
|
123
|
+
)
|
88
124
|
end
|
89
125
|
|
90
|
-
def
|
91
|
-
@
|
92
|
-
|
126
|
+
def find_app_config(app_name1)
|
127
|
+
@app_configs ||= {}
|
128
|
+
@app_configs[app_name1] ||= apps.find do |app_name2, app_config|
|
129
|
+
app_matches?(app_name1, app_name2, app_config)
|
130
|
+
end&.last
|
131
|
+
end
|
93
132
|
|
94
|
-
|
95
|
-
|
96
|
-
new_key ? [new_key, value] : [key, value]
|
97
|
-
end
|
133
|
+
def ensure_app!
|
134
|
+
return if app
|
98
135
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
136
|
+
raise "No app provided. " \
|
137
|
+
"The app can be provided either through the CPLN_APP env var " \
|
138
|
+
"('allow_app_override_by_env' must be set to true in 'controlplane.yml'), " \
|
139
|
+
"or the --app command option."
|
140
|
+
end
|
103
141
|
|
104
|
-
|
105
|
-
|
142
|
+
def ensure_org!
|
143
|
+
return if org
|
106
144
|
|
107
|
-
|
145
|
+
raise "No org provided. " \
|
146
|
+
"The org can be provided either through the CPLN_ORG env var " \
|
147
|
+
"('allow_org_override_by_env' must be set to true in 'controlplane.yml'), " \
|
148
|
+
"the --org command option, " \
|
149
|
+
"or the 'cpln_org' key in 'controlplane.yml'."
|
108
150
|
end
|
109
151
|
|
110
|
-
def
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
152
|
+
def ensure_required_options! # rubocop:disable Metrics/CyclomaticComplexity
|
153
|
+
ensure_app! if required_options.include?(:app)
|
154
|
+
ensure_org! if required_options.include?(:org) || app
|
155
|
+
|
156
|
+
missing_str = required_options
|
157
|
+
.reject { |option_name| %i[org app].include?(option_name) || options.key?(option_name) }
|
158
|
+
.map { |option_name| "--#{option_name}" }
|
159
|
+
.join(", ")
|
160
|
+
|
161
|
+
raise "Required options missing: #{missing_str}" unless missing_str.empty?
|
116
162
|
end
|
117
163
|
|
118
|
-
def
|
119
|
-
|
164
|
+
def config_file_path # rubocop:disable Metrics/MethodLength
|
165
|
+
@config_file_path ||= begin
|
166
|
+
path = Pathname.new(".").expand_path
|
120
167
|
|
121
|
-
|
122
|
-
|
123
|
-
|
168
|
+
loop do
|
169
|
+
config_file = path + CONFIG_FILE_LOCATION
|
170
|
+
break config_file if File.file?(config_file)
|
124
171
|
|
125
|
-
|
172
|
+
path = path.parent
|
126
173
|
|
127
|
-
|
128
|
-
|
174
|
+
if path.root?
|
175
|
+
raise "Can't find project config file at 'project_folder/#{CONFIG_FILE_LOCATION}', please create it."
|
176
|
+
end
|
129
177
|
end
|
130
178
|
end
|
131
179
|
end
|
@@ -135,10 +183,76 @@ class Config # rubocop:disable Metrics/ClassLength
|
|
135
183
|
org: :cpln_org,
|
136
184
|
location: :default_location,
|
137
185
|
prefix: :match_if_app_name_starts_with,
|
186
|
+
setup: :setup_app_templates,
|
138
187
|
old_image_retention_days: :image_retention_days
|
139
188
|
}
|
140
189
|
end
|
141
190
|
|
191
|
+
def load_app_from_env
|
192
|
+
app_from_env = strip_str_and_validate(ENV.fetch("CPLN_APP", nil))
|
193
|
+
return unless app_from_env
|
194
|
+
|
195
|
+
app_config = find_app_config(app_from_env)
|
196
|
+
ensure_config_app!(app_from_env, app_config)
|
197
|
+
|
198
|
+
key_exists = app_config.key?(:allow_app_override_by_env)
|
199
|
+
allowed_locally = key_exists && app_config[:allow_app_override_by_env]
|
200
|
+
allowed_globally = !key_exists && config[:allow_app_override_by_env]
|
201
|
+
return unless allowed_locally || allowed_globally
|
202
|
+
|
203
|
+
@app_comes_from_env = true
|
204
|
+
|
205
|
+
app_from_env
|
206
|
+
end
|
207
|
+
|
208
|
+
def load_app_from_options
|
209
|
+
app_from_options = strip_str_and_validate(options[:app])
|
210
|
+
return unless app_from_options
|
211
|
+
|
212
|
+
app_config = find_app_config(app_from_options)
|
213
|
+
ensure_config_app!(app_from_options, app_config)
|
214
|
+
|
215
|
+
app_from_options
|
216
|
+
end
|
217
|
+
|
218
|
+
def load_org_from_env
|
219
|
+
org_from_env = strip_str_and_validate(ENV.fetch("CPLN_ORG", nil))
|
220
|
+
return unless org_from_env
|
221
|
+
|
222
|
+
key_exists = current&.key?(:allow_org_override_by_env)
|
223
|
+
allowed_locally = key_exists && current[:allow_org_override_by_env]
|
224
|
+
allowed_globally = !key_exists && config[:allow_org_override_by_env]
|
225
|
+
return unless allowed_locally || allowed_globally
|
226
|
+
|
227
|
+
@org_comes_from_env = true
|
228
|
+
|
229
|
+
org_from_env
|
230
|
+
end
|
231
|
+
|
232
|
+
def load_org_from_options
|
233
|
+
strip_str_and_validate(options[:org])
|
234
|
+
end
|
235
|
+
|
236
|
+
def load_org_from_file
|
237
|
+
return unless current&.key?(:cpln_org)
|
238
|
+
|
239
|
+
strip_str_and_validate(current[:cpln_org])
|
240
|
+
end
|
241
|
+
|
242
|
+
def load_location_from_options
|
243
|
+
strip_str_and_validate(options[:location])
|
244
|
+
end
|
245
|
+
|
246
|
+
def load_location_from_env
|
247
|
+
strip_str_and_validate(ENV.fetch("CPLN_LOCATION", nil))
|
248
|
+
end
|
249
|
+
|
250
|
+
def load_location_from_file
|
251
|
+
return unless current&.key?(:default_location)
|
252
|
+
|
253
|
+
strip_str_and_validate(current.fetch(:default_location))
|
254
|
+
end
|
255
|
+
|
142
256
|
def warn_deprecated_options(app_options)
|
143
257
|
deprecated_option_keys = new_option_keys.select { |old_key| app_options.key?(old_key) }
|
144
258
|
return if deprecated_option_keys.empty?
|
data/lib/core/controlplane.rb
CHANGED
@@ -23,9 +23,10 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def profile_create(profile, token)
|
26
|
+
sensitive_data_pattern = /(?<=--token )(\S+)/
|
26
27
|
cmd = "cpln profile create #{profile} --token #{token}"
|
27
28
|
cmd += " > /dev/null" if Shell.should_hide_output?
|
28
|
-
perform!(cmd)
|
29
|
+
perform!(cmd, sensitive_data_pattern: sensitive_data_pattern)
|
29
30
|
end
|
30
31
|
|
31
32
|
def profile_delete(profile)
|
@@ -47,6 +48,7 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
47
48
|
# https://docs.controlplane.com/guides/push-image#step-2
|
48
49
|
# Might need to use `docker buildx build` if compatiblitity issues arise
|
49
50
|
cmd = "docker build --platform=linux/amd64 -t #{image} -f #{dockerfile}"
|
51
|
+
cmd += " --progress=plain" if ControlplaneApiDirect.trace
|
50
52
|
|
51
53
|
build_args.each { |build_arg| cmd += " --build-arg #{build_arg}" }
|
52
54
|
cmd += " #{config.app_dir}"
|
@@ -234,6 +236,16 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
234
236
|
perform!(cmd)
|
235
237
|
end
|
236
238
|
|
239
|
+
# volumeset
|
240
|
+
|
241
|
+
def fetch_volumesets(a_gvc = gvc)
|
242
|
+
api.list_volumesets(org: org, gvc: a_gvc)
|
243
|
+
end
|
244
|
+
|
245
|
+
def delete_volumeset(volumeset, a_gvc = gvc)
|
246
|
+
api.delete_volumeset(org: org, gvc: a_gvc, volumeset: volumeset)
|
247
|
+
end
|
248
|
+
|
237
249
|
# domain
|
238
250
|
|
239
251
|
def find_domain_route(data)
|
@@ -346,8 +358,8 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
346
358
|
system(cmd)
|
347
359
|
end
|
348
360
|
|
349
|
-
def perform!(cmd)
|
350
|
-
Shell.debug("CMD", cmd)
|
361
|
+
def perform!(cmd, sensitive_data_pattern: nil)
|
362
|
+
Shell.debug("CMD", cmd, sensitive_data_pattern: sensitive_data_pattern)
|
351
363
|
|
352
364
|
system(cmd) || exit(false)
|
353
365
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class ControlplaneApi
|
3
|
+
class ControlplaneApi # rubocop:disable Metrics/ClassLength
|
4
4
|
def gvc_list(org:)
|
5
5
|
api_json("/org/#{org}/gvc", method: :get)
|
6
6
|
end
|
@@ -86,6 +86,14 @@ class ControlplaneApi
|
|
86
86
|
api_json("/org/#{org}/gvc/#{gvc}/workload/#{workload}", method: :delete)
|
87
87
|
end
|
88
88
|
|
89
|
+
def list_volumesets(org:, gvc:)
|
90
|
+
api_json("/org/#{org}/gvc/#{gvc}/volumeset", method: :get)
|
91
|
+
end
|
92
|
+
|
93
|
+
def delete_volumeset(org:, gvc:, volumeset:)
|
94
|
+
api_json("/org/#{org}/gvc/#{gvc}/volumeset/#{volumeset}", method: :delete)
|
95
|
+
end
|
96
|
+
|
89
97
|
def list_domains(org:)
|
90
98
|
api_json("/org/#{org}/domain", method: :get)
|
91
99
|
end
|
@@ -17,7 +17,12 @@ class ControlplaneApiDirect
|
|
17
17
|
|
18
18
|
API_TOKEN_REGEX = /^[\w\-._]+$/.freeze
|
19
19
|
|
20
|
-
|
20
|
+
class << self
|
21
|
+
attr_accessor :trace
|
22
|
+
end
|
23
|
+
|
24
|
+
def call(url, method:, host: :api, body: nil) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
25
|
+
trace = ControlplaneApiDirect.trace
|
21
26
|
uri = URI("#{api_host(host)}#{url}")
|
22
27
|
request = API_METHODS[method].new(uri)
|
23
28
|
request["Content-Type"] = "application/json"
|
@@ -26,7 +31,11 @@ class ControlplaneApiDirect
|
|
26
31
|
|
27
32
|
Shell.debug(method.upcase, "#{uri} #{body&.to_json}")
|
28
33
|
|
29
|
-
|
34
|
+
http = Net::HTTP.new(uri.hostname, uri.port)
|
35
|
+
http.use_ssl = uri.scheme == "https"
|
36
|
+
http.set_debug_output($stdout) if trace
|
37
|
+
|
38
|
+
response = http.start { |ht| ht.request(request) }
|
30
39
|
|
31
40
|
case response
|
32
41
|
when Net::HTTPOK
|
@@ -35,6 +44,9 @@ class ControlplaneApiDirect
|
|
35
44
|
true
|
36
45
|
when Net::HTTPNotFound
|
37
46
|
nil
|
47
|
+
when Net::HTTPForbidden
|
48
|
+
org = self.class.parse_org(url)
|
49
|
+
raise("Double check your org #{org}. #{response} #{response.body}")
|
38
50
|
else
|
39
51
|
raise("#{response} #{response.body}")
|
40
52
|
end
|
@@ -65,4 +77,8 @@ class ControlplaneApiDirect
|
|
65
77
|
remove_class_variable(:@@api_token) if defined?(@@api_token)
|
66
78
|
end
|
67
79
|
# rubocop:enable Style/ClassVars
|
80
|
+
|
81
|
+
def self.parse_org(url)
|
82
|
+
url.match(%r{^/org/([^/]+)})[1]
|
83
|
+
end
|
68
84
|
end
|
data/lib/core/helpers.rb
ADDED
data/lib/core/shell.rb
CHANGED
@@ -36,7 +36,7 @@ class Shell
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def self.confirm(message)
|
39
|
-
shell.yes?("#{message} (y/
|
39
|
+
shell.yes?("#{message} (y/N)")
|
40
40
|
end
|
41
41
|
|
42
42
|
def self.warn(message)
|
@@ -55,11 +55,33 @@ class Shell
|
|
55
55
|
@verbose = verbose
|
56
56
|
end
|
57
57
|
|
58
|
-
def self.debug(prefix, message)
|
59
|
-
|
58
|
+
def self.debug(prefix, message, sensitive_data_pattern: nil)
|
59
|
+
return unless verbose
|
60
|
+
|
61
|
+
filtered_message = hide_sensitive_data(message, sensitive_data_pattern)
|
62
|
+
stderr.puts("\n[#{color(prefix, :red)}] #{filtered_message}")
|
60
63
|
end
|
61
64
|
|
62
65
|
def self.should_hide_output?
|
63
66
|
tmp_stderr && !verbose
|
64
67
|
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Hide sensitive data based on the passed pattern
|
71
|
+
#
|
72
|
+
# @param [String] message
|
73
|
+
# The message to get processed.
|
74
|
+
# @param [Regexp, nil] pattern
|
75
|
+
# The regular expression to be used. If not provided, no filter gets applied.
|
76
|
+
#
|
77
|
+
# @return [String]
|
78
|
+
# Filtered message.
|
79
|
+
#
|
80
|
+
# @example
|
81
|
+
# hide_sensitive_data("--token abcd", /(?<=--token )(\S+)/)
|
82
|
+
def self.hide_sensitive_data(message, pattern = nil)
|
83
|
+
return message unless pattern.is_a?(Regexp)
|
84
|
+
|
85
|
+
message.gsub(pattern, "XXXXXXX")
|
86
|
+
end
|
65
87
|
end
|