cpl 2.0.2 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/command/run.rb CHANGED
@@ -36,12 +36,17 @@ module Command
36
36
  - - log async fetching for non-interactive mode
37
37
  - The Dockerfile entrypoint is used as the command by default, which assumes `exec "${@}"` to be present,
38
38
  and the args ["bash", "-c", cmd_to_run] are passed
39
- - The entrypoint can be overriden through `--entrypoint`, which must be a single command or a script path that exists in the container,
39
+ - The entrypoint can be overridden through `--entrypoint`, which must be a single command or a script path that exists in the container,
40
40
  and the args ["bash", "-c", cmd_to_run] are passed,
41
41
  unless the entrypoint is `bash`, in which case the args ["-c", cmd_to_run] are passed
42
42
  - Providing `--entrypoint none` sets the entrypoint to `bash` by default
43
43
  - If `fix_terminal_size` is `true` in the `.controlplane/controlplane.yml` file,
44
- the remote terminal size will be fixed to match the local terminal size (may also be overriden through `--terminal-size`)
44
+ the remote terminal size will be fixed to match the local terminal size (may also be overridden through `--terminal-size`)
45
+ - By default, all jobs use a CPU size of 1 (1 core) and a memory size of 2Gi (2 gibibytes)
46
+ (can be configured through `runner_job_default_cpu` and `runner_job_default_memory` in `controlplane.yml`,
47
+ and also overridden per job through `--cpu` and `--memory`)
48
+ - By default, the job is stopped if it takes longer than 6 hours to finish
49
+ (can be configured though `runner_job_timeout` in `controlplane.yml`)
45
50
  DESC
46
51
  EXAMPLES = <<~EX
47
52
  ```sh
@@ -84,12 +89,17 @@ module Command
84
89
  ```
85
90
  EX
86
91
 
92
+ DEFAULT_JOB_CPU = "1"
93
+ DEFAULT_JOB_MEMORY = "2Gi"
94
+ DEFAULT_JOB_TIMEOUT = 21_600 # 6 hours
95
+ DEFAULT_JOB_HISTORY_LIMIT = 10
87
96
  MAGIC_END = "---cpl run command finished---"
88
97
 
89
98
  attr_reader :interactive, :detached, :location, :original_workload, :runner_workload,
99
+ :default_image, :default_cpu, :default_memory, :job_timeout, :job_history_limit,
90
100
  :container, :expected_deployed_version, :job, :replica, :command
91
101
 
92
- def call # rubocop:disable Metrics/MethodLength
102
+ def call # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
93
103
  @interactive = config.options[:interactive] || interactive_command?
94
104
  @detached = config.options[:detached]
95
105
  @log_method = config.options[:log_method]
@@ -97,6 +107,11 @@ module Command
97
107
  @location = config.location
98
108
  @original_workload = config.options[:workload] || config[:one_off_workload]
99
109
  @runner_workload = "#{original_workload}-runner"
110
+ @default_image = "#{config.app}:#{Controlplane::NO_IMAGE_AVAILABLE}"
111
+ @default_cpu = config.current[:runner_job_default_cpu] || DEFAULT_JOB_CPU
112
+ @default_memory = config.current[:runner_job_default_memory] || DEFAULT_JOB_MEMORY
113
+ @job_timeout = config.current[:runner_job_timeout] || DEFAULT_JOB_TIMEOUT
114
+ @job_history_limit = DEFAULT_JOB_HISTORY_LIMIT
100
115
 
101
116
  unless interactive
102
117
  @internal_sigint = false
@@ -110,12 +125,10 @@ module Command
110
125
  end
111
126
  end
112
127
 
113
- if cp.fetch_workload(runner_workload).nil?
114
- create_runner_workload
115
- wait_for_runner_workload_create
116
- end
128
+ create_runner_workload if cp.fetch_workload(runner_workload).nil?
129
+ wait_for_runner_workload_deploy
117
130
  update_runner_workload
118
- wait_for_runner_workload_update
131
+ wait_for_runner_workload_update if expected_deployed_version
119
132
 
120
133
  start_job
121
134
  wait_for_replica_for_job
@@ -154,6 +167,11 @@ module Command
154
167
  container_spec.delete("livenessProbe")
155
168
  container_spec.delete("readinessProbe")
156
169
 
170
+ # Set image, CPU, and memory to default values
171
+ container_spec["image"] = default_image
172
+ container_spec["cpu"] = default_cpu
173
+ container_spec["memory"] = default_memory
174
+
157
175
  # Ensure cron workload won't run per schedule
158
176
  spec["defaultOptions"]["suspend"] = true
159
177
 
@@ -163,9 +181,14 @@ module Command
163
181
 
164
182
  # Set cron job props
165
183
  spec["type"] = "cron"
184
+ spec["job"] = {
185
+ # Next job set to run on January 1st, 2029
186
+ "schedule" => "0 0 1 1 1",
166
187
 
167
- # Next job set to run on January 1st, 2029
168
- spec["job"] = { "schedule" => "0 0 1 1 1", "restartPolicy" => "Never" }
188
+ "restartPolicy" => "Never",
189
+ "activeDeadlineSeconds" => job_timeout,
190
+ "historyLimit" => job_history_limit
191
+ }
169
192
 
170
193
  # Create runner workload
171
194
  cp.apply_hash("kind" => "workload", "name" => runner_workload, "spec" => spec)
@@ -173,46 +196,60 @@ module Command
173
196
  end
174
197
 
175
198
  def update_runner_workload # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
176
- step("Updating runner workload '#{runner_workload}' based on '#{original_workload}'") do # rubocop:disable Metrics/BlockLength
177
- @expected_deployed_version = cp.cron_workload_deployed_version(runner_workload)
178
- should_update = false
199
+ should_update = false
200
+ spec = nil
179
201
 
202
+ step("Checking if runner workload '#{runner_workload}' needs to be updated") do # rubocop:disable Metrics/BlockLength
180
203
  _, original_container_spec = base_workload_specs(original_workload)
181
204
  spec, container_spec = base_workload_specs(runner_workload)
182
205
 
183
- # Override image if specified
184
- image = config.options[:image]
185
- image_link = if image
186
- image = latest_image if image == "latest"
187
- "/org/#{config.org}/image/#{image}"
188
- else
189
- original_container_spec["image"]
190
- end
191
- if container_spec["image"] != image_link
192
- container_spec["image"] = image_link
206
+ # Keep ENV synced between original and runner workloads
207
+ original_env_str = original_container_spec["env"]&.sort_by { |env| env["name"] }.to_s
208
+ env_str = container_spec["env"]&.sort_by { |env| env["name"] }.to_s
209
+ if original_env_str != env_str
210
+ container_spec["env"] = original_container_spec["env"]
211
+ should_update = true
212
+ end
213
+
214
+ if container_spec["image"] != default_image
215
+ container_spec["image"] = default_image
193
216
  should_update = true
194
217
  end
195
218
 
196
- # Container overrides
197
- if config.options[:cpu] && container_spec["cpu"] != config.options[:cpu]
198
- container_spec["cpu"] = config.options[:cpu]
219
+ if container_spec["cpu"] != default_cpu
220
+ container_spec["cpu"] = default_cpu
199
221
  should_update = true
200
222
  end
201
- if config.options[:memory] && container_spec["memory"] != config.options[:memory]
202
- container_spec["memory"] = config.options[:memory]
223
+
224
+ if container_spec["memory"] != default_memory
225
+ container_spec["memory"] = default_memory
203
226
  should_update = true
204
227
  end
205
228
 
206
- next true unless should_update
229
+ if spec["job"]["activeDeadlineSeconds"] != job_timeout
230
+ spec["job"]["activeDeadlineSeconds"] = job_timeout
231
+ should_update = true
232
+ end
207
233
 
234
+ if spec["job"]["historyLimit"] != job_history_limit
235
+ spec["job"]["historyLimit"] = job_history_limit
236
+ should_update = true
237
+ end
238
+
239
+ true
240
+ end
241
+
242
+ return unless should_update
243
+
244
+ step("Updating runner workload '#{runner_workload}'") do
208
245
  # Update runner workload
209
- @expected_deployed_version += 1
246
+ @expected_deployed_version = cp.cron_workload_deployed_version(runner_workload) + 1
210
247
  cp.apply_hash("kind" => "workload", "name" => runner_workload, "spec" => spec)
211
248
  end
212
249
  end
213
250
 
214
- def wait_for_runner_workload_create
215
- step("Waiting for runner workload '#{runner_workload}' to be created", retry_on_failure: true) do
251
+ def wait_for_runner_workload_deploy
252
+ step("Waiting for runner workload '#{runner_workload}' to be deployed", retry_on_failure: true) do
216
253
  !cp.cron_workload_deployed_version(runner_workload).nil?
217
254
  end
218
255
  end
@@ -302,12 +339,14 @@ module Command
302
339
  def base_workload_specs(workload)
303
340
  spec = cp.fetch_workload!(workload).fetch("spec")
304
341
  container_spec = spec["containers"].detect { _1["name"] == original_workload } || spec["containers"].first
305
- @container = container_spec["name"]
306
342
 
307
343
  [spec, container_spec]
308
344
  end
309
345
 
310
346
  def build_job_start_yaml # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
347
+ _, original_container_spec = base_workload_specs(original_workload)
348
+ @container = original_container_spec["name"]
349
+
311
350
  job_start_hash = { "name" => container }
312
351
 
313
352
  if config.options[:use_local_token]
@@ -335,6 +374,18 @@ module Command
335
374
  job_start_hash["args"].push('eval "$CPL_RUNNER_SCRIPT"')
336
375
  end
337
376
 
377
+ image = config.options[:image]
378
+ image_link = if image
379
+ image = cp.latest_image if image == "latest"
380
+ "/org/#{config.org}/image/#{image}"
381
+ else
382
+ original_container_spec["image"]
383
+ end
384
+
385
+ job_start_hash["image"] = image_link
386
+ job_start_hash["cpu"] = config.options[:cpu] if config.options[:cpu]
387
+ job_start_hash["memory"] = config.options[:memory] if config.options[:memory]
388
+
338
389
  job_start_hash.to_yaml
339
390
  end
340
391
 
@@ -434,6 +485,8 @@ module Command
434
485
  end
435
486
 
436
487
  def print_detached_commands
488
+ return unless replica
489
+
437
490
  app_workload_replica_config = app_workload_replica_args.join(" ")
438
491
  progress.puts(
439
492
  "\n\n" \
@@ -451,7 +504,7 @@ module Command
451
504
  Shell.debug("JOB STATUS", status)
452
505
 
453
506
  case status
454
- when "active"
507
+ when "active", "pending"
455
508
  sleep 1
456
509
  when "successful"
457
510
  break ExitCode::SUCCESS
@@ -5,18 +5,27 @@ module Command
5
5
  NAME = "setup-app"
6
6
  OPTIONS = [
7
7
  app_option(required: true),
8
- skip_secret_access_binding_option
8
+ skip_secret_access_binding_option,
9
+ skip_secrets_setup_option,
10
+ skip_post_creation_hook_option
9
11
  ].freeze
10
12
  DESCRIPTION = "Creates an app and all its workloads"
11
13
  LONG_DESCRIPTION = <<~DESC
12
14
  - Creates an app and all its workloads
13
15
  - Specify the templates for the app and workloads through `setup_app_templates` in the `.controlplane/controlplane.yml` file
14
- - 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)
15
- - Automatically binds the app to the secrets policy, as long as both the identity and the policy exist
16
- - Use `--skip-secret-access-binding` to prevent the automatic bind
16
+ - This should only be used for temporary apps like review apps, never for persistent apps like production or staging (to update workloads for those, use 'cpl apply-template' instead)
17
+ - Configures app to have org-level secrets with default name "{APP_PREFIX}-secrets"
18
+ using org-level policy with default name "{APP_PREFIX}-secrets-policy" (names can be customized, see docs)
19
+ - Creates identity for secrets if it does not exist
20
+ - Use `--skip-secrets-setup` to prevent the automatic setup of secrets,
21
+ or set it through `skip_secrets_setup` in the `.controlplane/controlplane.yml` file
22
+ - Runs a post-creation hook after the app is created if `hooks.post_creation` is specified in the `.controlplane/controlplane.yml` file
23
+ - If the hook exits with a non-zero code, the command will stop executing and also exit with a non-zero code
24
+ - Use `--skip-post-creation-hook` to skip the hook if specified in `controlplane.yml`
17
25
  DESC
26
+ VALIDATIONS = %w[config templates].freeze
18
27
 
19
- def call # rubocop:disable Metrics/MethodLength
28
+ def call # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
20
29
  templates = config[:setup_app_templates]
21
30
 
22
31
  app = cp.fetch_gvc
@@ -26,24 +35,79 @@ module Command
26
35
  "or run 'cpl apply-template #{templates.join(' ')} -a #{config.app}'."
27
36
  end
28
37
 
29
- Cpl::Cli.start(["apply-template", *templates, "-a", config.app])
38
+ skip_secrets_setup = config.options[:skip_secret_access_binding] ||
39
+ config.options[:skip_secrets_setup] || config.current[:skip_secrets_setup]
30
40
 
31
- return if config.options[:skip_secret_access_binding]
41
+ create_secret_and_policy_if_not_exist unless skip_secrets_setup
42
+
43
+ args = []
44
+ args.push("--add-app-identity") unless skip_secrets_setup
45
+ Cpl::Cli.start(["apply-template", *templates, "-a", config.app, *args])
46
+
47
+ bind_identity_to_policy unless skip_secrets_setup
48
+ run_post_creation_hook unless config.options[:skip_post_creation_hook]
49
+ end
50
+
51
+ private
52
+
53
+ def create_secret_and_policy_if_not_exist
54
+ create_secret_if_not_exists
55
+ create_policy_if_not_exists
32
56
 
33
57
  progress.puts
58
+ end
34
59
 
35
- if cp.fetch_identity(app_identity).nil? || cp.fetch_policy(app_secrets_policy).nil?
36
- raise "Can't bind identity to policy: identity '#{app_identity}' or " \
37
- "policy '#{app_secrets_policy}' doesn't exist. " \
38
- "Please create them or use `--skip-secret-access-binding` to ignore this message." \
39
- "You can also set a custom secrets name with `secrets_name` " \
40
- "and a custom secrets policy name with `secrets_policy_name` " \
41
- "in the `.controlplane/controlplane.yml` file."
60
+ def create_secret_if_not_exists
61
+ if cp.fetch_secret(config.secrets)
62
+ progress.puts("Secret '#{config.secrets}' already exists. Skipping creation...")
63
+ else
64
+ step("Creating secret '#{config.secrets}'") do
65
+ cp.apply_hash(build_secret_hash)
66
+ end
42
67
  end
68
+ end
43
69
 
44
- step("Binding identity to policy") do
45
- cp.bind_identity_to_policy(app_identity_link, app_secrets_policy)
70
+ def create_policy_if_not_exists
71
+ if cp.fetch_policy(config.secrets_policy)
72
+ progress.puts("Policy '#{config.secrets_policy}' already exists. Skipping creation...")
73
+ else
74
+ step("Creating policy '#{config.secrets_policy}'") do
75
+ cp.apply_hash(build_policy_hash)
76
+ end
46
77
  end
47
78
  end
79
+
80
+ def build_secret_hash
81
+ {
82
+ "kind" => "secret",
83
+ "name" => config.secrets,
84
+ "type" => "dictionary",
85
+ "data" => {}
86
+ }
87
+ end
88
+
89
+ def build_policy_hash
90
+ {
91
+ "kind" => "policy",
92
+ "name" => config.secrets_policy,
93
+ "targetKind" => "secret",
94
+ "targetLinks" => ["//secret/#{config.secrets}"]
95
+ }
96
+ end
97
+
98
+ def bind_identity_to_policy
99
+ progress.puts
100
+
101
+ step("Binding identity '#{config.identity}' to policy '#{config.secrets_policy}'") do
102
+ cp.bind_identity_to_policy(config.identity_link, config.secrets_policy)
103
+ end
104
+ end
105
+
106
+ def run_post_creation_hook
107
+ post_creation_hook = config.current.dig(:hooks, :post_creation)
108
+ return unless post_creation_hook
109
+
110
+ run_command_in_latest_image(post_creation_hook, title: "post-creation hook")
111
+ end
48
112
  end
49
113
  end
data/lib/command/test.rb CHANGED
@@ -11,6 +11,7 @@ module Command
11
11
  - For debugging purposes
12
12
  DESC
13
13
  HIDE = true
14
+ VALIDATIONS = [].freeze
14
15
 
15
16
  def call
16
17
  # Modify this method to trigger the code you want to test.
@@ -9,6 +9,7 @@ module Command
9
9
  - Can also be done with `cpl --version` or `cpl -v`
10
10
  DESC
11
11
  WITH_INFO_HEADER = false
12
+ VALIDATIONS = [].freeze
12
13
 
13
14
  def call
14
15
  puts Cpl::VERSION
data/lib/core/config.rb CHANGED
@@ -18,6 +18,8 @@ class Config # rubocop:disable Metrics/ClassLength
18
18
 
19
19
  ensure_required_options!
20
20
 
21
+ warn_deprecated_options
22
+
21
23
  Shell.verbose_mode(options[:verbose])
22
24
  trace_mode = options[:trace]
23
25
  return unless trace_mode
@@ -38,10 +40,34 @@ class Config # rubocop:disable Metrics/ClassLength
38
40
  current&.fetch(:name)
39
41
  end
40
42
 
43
+ def identity
44
+ "#{app}-identity"
45
+ end
46
+
47
+ def identity_link
48
+ "/org/#{org}/gvc/#{app}/identity/#{identity}"
49
+ end
50
+
51
+ def secrets
52
+ current&.dig(:secrets_name) || "#{app_prefix}-secrets"
53
+ end
54
+
55
+ def secrets_policy
56
+ current&.dig(:secrets_policy_name) || "#{secrets}-policy"
57
+ end
58
+
41
59
  def location
42
60
  @location ||= load_location_from_options || load_location_from_env || load_location_from_file
43
61
  end
44
62
 
63
+ def location_link
64
+ "/org/#{org}/location/#{location}"
65
+ end
66
+
67
+ def image_link(image)
68
+ "/org/#{org}/image/#{image}"
69
+ end
70
+
45
71
  def domain
46
72
  @domain ||= load_domain_from_options || load_domain_from_file
47
73
  end
@@ -84,6 +110,8 @@ class Config # rubocop:disable Metrics/ClassLength
84
110
  @apps ||= config[:apps].to_h do |app_name, app_options|
85
111
  ensure_config_app!(app_name, app_options)
86
112
 
113
+ check_deprecated_options(app_options)
114
+
87
115
  app_options_with_new_keys = app_options.to_h do |key, value|
88
116
  new_key = new_option_keys[key]
89
117
  new_key ? [new_key, value] : [key, value]
@@ -96,14 +124,7 @@ class Config # rubocop:disable Metrics/ClassLength
96
124
  def current
97
125
  return unless app
98
126
 
99
- @current ||= begin
100
- app_config = find_app_config(app)
101
- ensure_config_app!(app, app_config)
102
-
103
- warn_deprecated_options(app_config)
104
-
105
- app_config
106
- end
127
+ @current ||= find_app_config(app)
107
128
  end
108
129
 
109
130
  def app_matches?(app_name1, app_name2, app_options)
@@ -275,11 +296,18 @@ class Config # rubocop:disable Metrics/ClassLength
275
296
  strip_str_and_validate(current.fetch(:default_domain))
276
297
  end
277
298
 
278
- def warn_deprecated_options(app_options)
279
- deprecated_option_keys = new_option_keys.select { |old_key| app_options.key?(old_key) }
280
- return if deprecated_option_keys.empty?
299
+ def check_deprecated_options(app_options)
300
+ @deprecated_option_keys ||= {}
301
+
302
+ new_option_keys.each do |old_key, new_key|
303
+ @deprecated_option_keys[old_key] = new_key if app_options.key?(old_key)
304
+ end
305
+ end
306
+
307
+ def warn_deprecated_options
308
+ return if !@deprecated_option_keys || @deprecated_option_keys.empty?
281
309
 
282
- deprecated_option_keys.each do |old_key, new_key|
310
+ @deprecated_option_keys.each do |old_key, new_key|
283
311
  Shell.warn_deprecated("Option '#{old_key}' is deprecated, " \
284
312
  "please use '#{new_key}' instead (in 'controlplane.yml').")
285
313
  end
@@ -3,6 +3,8 @@
3
3
  class Controlplane # rubocop:disable Metrics/ClassLength
4
4
  attr_reader :config, :api, :gvc, :org
5
5
 
6
+ NO_IMAGE_AVAILABLE = "NO_IMAGE_AVAILABLE"
7
+
6
8
  def initialize(config)
7
9
  @config = config
8
10
  @api = ControlplaneApi.new
@@ -37,6 +39,51 @@ class Controlplane # rubocop:disable Metrics/ClassLength
37
39
 
38
40
  # image
39
41
 
42
+ def latest_image(a_gvc = gvc, a_org = org, refresh: false)
43
+ @latest_image ||= {}
44
+ @latest_image[a_gvc] = nil if refresh
45
+ @latest_image[a_gvc] ||=
46
+ begin
47
+ items = query_images(a_gvc, a_org)["items"]
48
+ latest_image_from(items, app_name: a_gvc)
49
+ end
50
+ end
51
+
52
+ def latest_image_next(a_gvc = gvc, a_org = org, commit: nil)
53
+ commit ||= config.options[:commit]
54
+
55
+ @latest_image_next ||= {}
56
+ @latest_image_next[a_gvc] ||= begin
57
+ latest_image_name = latest_image(a_gvc, a_org)
58
+ image = latest_image_name.split(":").first
59
+ image += ":#{extract_image_number(latest_image_name) + 1}"
60
+ image += "_#{commit}" if commit
61
+ image
62
+ end
63
+ end
64
+
65
+ def latest_image_from(items, app_name: gvc, name_only: true)
66
+ matching_items = items.select { |item| item["name"].start_with?("#{app_name}:") }
67
+
68
+ # Or special string to indicate no image available
69
+ if matching_items.empty?
70
+ name_only ? "#{app_name}:#{NO_IMAGE_AVAILABLE}" : nil
71
+ else
72
+ latest_item = matching_items.max_by { |item| extract_image_number(item["name"]) }
73
+ name_only ? latest_item["name"] : latest_item
74
+ end
75
+ end
76
+
77
+ def extract_image_number(image_name)
78
+ return 0 if image_name.end_with?(NO_IMAGE_AVAILABLE)
79
+
80
+ image_name.match(/:(\d+)/)&.captures&.first.to_i
81
+ end
82
+
83
+ def extract_image_commit(image_name)
84
+ image_name.match(/_(\h+)$/)&.captures&.first
85
+ end
86
+
40
87
  def query_images(a_gvc = gvc, a_org = org, partial_gvc_match: nil)
41
88
  partial_gvc_match = config.should_app_start_with?(a_gvc) if partial_gvc_match.nil?
42
89
  gvc_op = partial_gvc_match ? "~" : "="
@@ -209,6 +256,11 @@ class Controlplane # rubocop:disable Metrics/ClassLength
209
256
  api.update_workload(org: org, gvc: gvc, workload: workload, data: data)
210
257
  end
211
258
 
259
+ def workload_suspended?(workload)
260
+ details = fetch_workload!(workload)
261
+ details["spec"]["defaultOptions"]["suspend"]
262
+ end
263
+
212
264
  def workload_force_redeployment(workload)
213
265
  cmd = "cpln workload force-redeployment #{workload} #{gvc_org}"
214
266
  perform!(cmd)
@@ -324,6 +376,12 @@ class Controlplane # rubocop:disable Metrics/ClassLength
324
376
  api.log_get(org: org, gvc: gvc, workload: workload, replica: replica, from: from, to: to)
325
377
  end
326
378
 
379
+ # secrets
380
+
381
+ def fetch_secret(secret)
382
+ api.fetch_secret(org: org, secret: secret)
383
+ end
384
+
327
385
  # identities
328
386
 
329
387
  def fetch_identity(identity, a_gvc = gvc)
@@ -52,7 +52,7 @@ class ControlplaneApi # rubocop:disable Metrics/ClassLength
52
52
  # params << "direction=forward"
53
53
  params = params.map { |k, v| %(#{k}=#{CGI.escape(v)}) }.join("&")
54
54
 
55
- api_json_direct("/logs/org/#{org}/loki/api/v1/query_range?#{params}", method: :get, host: :logs)
55
+ api_json("/logs/org/#{org}/loki/api/v1/query_range?#{params}", method: :get, host: :logs)
56
56
  end
57
57
 
58
58
  def query_workloads(org:, gvc:, workload:, gvc_op_type:, workload_op_type:) # rubocop:disable Metrics/MethodLength
@@ -116,6 +116,14 @@ class ControlplaneApi # rubocop:disable Metrics/ClassLength
116
116
  api_json("/org/#{org}/domain/#{domain}", method: :patch, body: data)
117
117
  end
118
118
 
119
+ def fetch_secret(org:, secret:)
120
+ api_json("/org/#{org}/secret/#{secret}", method: :get)
121
+ end
122
+
123
+ def delete_secret(org:, secret:)
124
+ api_json("/org/#{org}/secret/#{secret}", method: :delete)
125
+ end
126
+
119
127
  def fetch_identity(org:, gvc:, identity:)
120
128
  api_json("/org/#{org}/gvc/#{gvc}/identity/#{identity}", method: :get)
121
129
  end
@@ -124,6 +132,10 @@ class ControlplaneApi # rubocop:disable Metrics/ClassLength
124
132
  api_json("/org/#{org}/policy/#{policy}", method: :get)
125
133
  end
126
134
 
135
+ def delete_policy(org:, policy:)
136
+ api_json("/org/#{org}/policy/#{policy}", method: :delete)
137
+ end
138
+
127
139
  private
128
140
 
129
141
  def fetch_query_pages(result)
@@ -152,13 +164,7 @@ class ControlplaneApi # rubocop:disable Metrics/ClassLength
152
164
  result
153
165
  end
154
166
 
155
- # switch between cpln rest and api
156
167
  def api_json(...)
157
168
  ControlplaneApiDirect.new.call(...)
158
169
  end
159
-
160
- # only for api (where not impelemented in cpln rest)
161
- def api_json_direct(...)
162
- ControlplaneApiDirect.new.call(...)
163
- end
164
170
  end
@@ -98,7 +98,7 @@ class ControlplaneApiDirect
98
98
  end
99
99
 
100
100
  def refresh_api_token
101
- @@api_token[:token] = `cpln profile token`.chomp
101
+ @@api_token[:token] = Shell.cmd("cpln", "profile", "token")[:output].chomp
102
102
  end
103
103
 
104
104
  def self.reset_api_token