cpl 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 711286ef49686f6c51df2a94c533b3df94f4d71d3fb6018b6ed61c210593dd47
4
- data.tar.gz: '085357faac6b5b27e493049b34c27debab21fa19950af21548fe11889140b557'
3
+ metadata.gz: d76f46cb9427fc137ece07123d0ddd0d1add98e1ffa046a95e1394916784676e
4
+ data.tar.gz: 266681f356c78d8a4636ae27cb90485a49eda80c27a6199bd3505ddd72cd3451
5
5
  SHA512:
6
- metadata.gz: ac229234f4919d8e1b3485095476ef0fddac04573d8dd03aba6a3737fd5e90b319538e04c51d98e5f33f244fe304c519d949a624a377bbc9ee9067754a0c71dd
7
- data.tar.gz: 0ba5203d4f8539dde89a2eb5a52dc92c2098d11bb1914002cd57628081465d08e9842f47546da563bc18edda17fbf12101bfefca8fdcee4088825c0e56ee0fa0
6
+ metadata.gz: 33f9ec2f7af612611d3a55114097f21a2a2cc9605b3bd2813345b9a06f8308a28d204bd71f49f96e2d1a77798e920621651e457801d795b40f3bf51abe91e985
7
+ data.tar.gz: d2c82fb6b24cf54e306e83f21b0c8a1b519147e1f8ec66b81442b2ae05f2c752eb18c60cb94c66c2cd11ea921db228116b2bc3ead49c77f1317ce0aa4e290ff1
@@ -0,0 +1,19 @@
1
+ name: Check Links
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+
9
+ jobs:
10
+ check_cpln_links:
11
+ runs-on: ubuntu-latest
12
+ name: Check Links
13
+ steps:
14
+ - name: Checkout repository
15
+ uses: actions/checkout@v3
16
+ - name: Validate outgoing links to Control Plane
17
+ run: |
18
+ TERM=xterm-color ./script/check_cpln_links
19
+ shell: bash
data/.overcommit.yml CHANGED
@@ -1,4 +1,7 @@
1
1
  PreCommit:
2
+ ValidateLinks:
3
+ enabled: true
4
+ command: ["bash", "./script/check_cpln_links"]
2
5
  CommandDocs:
3
6
  enabled: true
4
7
  command: ["bundle", "exec", "rake", "check_command_docs"]
data/CHANGELOG.md CHANGED
@@ -16,6 +16,28 @@ _Please add entries here for your pull requests that are not yet released._
16
16
 
17
17
  ### Fixed
18
18
 
19
+ - Fixed issue where cpln profile was not switched back to `default` if an error happened while running `copy-image-from-upstream` command. [PR 135](https://github.com/shakacode/heroku-to-control-plane/pull/135) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
20
+ - Fixed issue that didn't allow using upstream with `match_if_app_name_starts_with` set to `true` in `copy-image-from-upstream` command. [PR 136](https://github.com/shakacode/heroku-to-control-plane/pull/136) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
21
+
22
+ ### Added
23
+
24
+ - Added `--no-clean-on-failure` option to `run:detached` command to skip deletion of failed workload run. [PR 133](https://github.com/shakacode/heroku-to-control-plane/pull/133) by [Justin Gordon](https://github.com/justin808) and [Rafael Gomes](https://github.com/rafaelgomesxyz).
25
+ - Added `--domain` option to `maintenance`, `maintenance:on` and `maintenance:off` commands. [PR 131](https://github.com/shakacode/heroku-to-control-plane/pull/131) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
26
+ - Added `default_domain` config to specify domain for `maintenance`, `maintenance:on` and `maintenance:off` commands. [PR 131](https://github.com/shakacode/heroku-to-control-plane/pull/131) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
27
+ - Added option to specify upstream for `copy-image-from-upstream` command through `CPLN_UPSTREAM` env var. [PR 138](https://github.com/shakacode/heroku-to-control-plane/pull/138) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
28
+
29
+ ### Changed
30
+
31
+ - `build-image` command now accepts extra options and passes them to `docker build`. [PR 126](https://github.com/shakacode/heroku-to-control-plane/pull/126) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
32
+ - `CPLN_ORG_UPSTREAM` env var now takes precedence over config from `controlplane.yml` in `copy-image-from-upstream` command. [PR 137](https://github.com/shakacode/heroku-to-control-plane/pull/137) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
33
+ - `info` command now works properly for apps with `match_if_app_name_starts_with` set to `true`.[PR 139](https://github.com/shakacode/heroku-to-control-plane/pull/139) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
34
+ - `info` command now lists workloads in the same order as `controlplane.yml`. [PR 139](https://github.com/shakacode/heroku-to-control-plane/pull/139) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
35
+ - Improved domain workload matching for `maintenance`, `maintenance:on` and `maintenance:off` commands (instead of matching only by workload, it now matches by org + app + workload, which is more accurate). [PR 140](https://github.com/shakacode/heroku-to-control-plane/pull/140) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
36
+
37
+ ## [1.2.0] - 2024-01-03
38
+
39
+ ### Fixed
40
+
19
41
  - Fixed issue where `info` command does not respect `CPLN_ORG` env var. [PR 88](https://github.com/shakacode/heroku-to-control-plane/pull/88) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
20
42
  - Fixed issues with running `cpl --version` and `cpl --help` where no configuration file exists. [PR 100](https://github.com/shakacode/heroku-to-control-plane/pull/100) by [Mostafa Ahangarha](https://github.com/ahangarha).
21
43
  - Fixed issue where `delete` command fails to delete apps with volumesets. [PR 123](https://github.com/shakacode/heroku-to-control-plane/pull/123) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
@@ -117,7 +139,8 @@ _Please add entries here for your pull requests that are not yet released._
117
139
 
118
140
  - Initial release
119
141
 
120
- [Unreleased]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.1.2...HEAD
142
+ [Unreleased]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.2.0...HEAD
143
+ [1.2.0]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.1.2...v1.2.0
121
144
  [1.1.2]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.1.1...v1.1.2
122
145
  [1.1.1]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.1.0...v1.1.1
123
146
  [1.1.0]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.0.4...v1.1.0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cpl (1.2.0)
4
+ cpl (1.3.0)
5
5
  debug (~> 1.7.1)
6
6
  dotenv (~> 2.8.1)
7
7
  psych (~> 5.1.0)
@@ -24,10 +24,10 @@ GEM
24
24
  dotenv (2.8.1)
25
25
  hashdiff (1.0.1)
26
26
  iniparse (1.5.0)
27
- io-console (0.7.1)
28
- irb (1.11.0)
27
+ io-console (0.7.2)
28
+ irb (1.12.0)
29
29
  rdoc
30
- reline (>= 0.3.8)
30
+ reline (>= 0.4.2)
31
31
  json (2.6.3)
32
32
  overcommit (0.60.0)
33
33
  childprocess (>= 0.6.3, < 5)
@@ -44,7 +44,7 @@ GEM
44
44
  rdoc (6.6.2)
45
45
  psych (>= 4.0.0)
46
46
  regexp_parser (2.6.2)
47
- reline (0.4.1)
47
+ reline (0.4.3)
48
48
  io-console (~> 0.5)
49
49
  rexml (3.2.5)
50
50
  rspec (3.12.0)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Heroku to Control Plane `cpl` CLI
1
+ # The power of Kubernetes with the ease of Heroku!
2
2
 
3
3
  <meta name="author" content="Justin Gordon and Sergey Tarasov">
4
4
  <meta name="description" content="Instructions on how to migrate from Heroku to Control Plane and a CLI called cpl to make it easier.">
@@ -6,16 +6,24 @@
6
6
  <meta name="keywords" content="Control Plane, Heroku, Kubernetes, K8, Infrastructure">
7
7
  <meta name="google-site-verification" content="dIV4nMplcYl6YOKOaZMqgvdKXhLJ4cdYY6pS6e_YrPU" />
8
8
 
9
- _A gem that provides **Heroku Flow** functionality on Control Plane, including docs for migrating from [Heroku](https://heroku.com) to [Control Plane](https://shakacode.controlplane.com/)._
10
-
11
9
  [![RSpec](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rspec.yml/badge.svg)](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rspec.yml)
12
10
  [![Rubocop](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rubocop.yml/badge.svg)](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rubocop.yml)
13
11
 
14
12
  [![Gem](https://badge.fury.io/rb/cpl.svg)](https://badge.fury.io/rb/cpl)
15
13
 
14
+
15
+ Here's a playbook for migrating from [Heroku Flow](https://www.heroku.com/flow) to [Control Plane](https://shakacode.controlplane.com) with our `cpl` gem source code.
16
+
17
+ ----
18
+
19
+ _If you need a free demo account for Control Plane (no CC required), you can contact [Justin Gordon, CEO of ShakaCode](mailto:justin@shakacode.com)._
20
+
16
21
  ---
17
22
 
18
- _If you need a free demo account for Control Plane (no CC required), you can contact [controlplane@shakacode.com](mailto:controlplane@shakacode.com)._
23
+ Be sure to see the [demo app](https://github.com/shakacode/react-webpack-rails-tutorial/tree/master/.controlplane)
24
+ If you would like to see the simple YAML configuration and setup,
25
+ Also, check [how the `cpl` gem (this project) is used in the Github actions](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.github/actions/deploy-to-control-plane/action.yml).
26
+ Here is a brief [video overview](https://www.youtube.com/watch?v=llaQoAV_6Iw).
19
27
 
20
28
  ---
21
29
 
@@ -117,7 +125,7 @@ npm update -g @controlplane/cli
117
125
 
118
126
  5. Run `cpln image docker-login --org <your-org>` to ensure that you have access to the Control Plane Docker registry.
119
127
 
120
- 6. Install Heroku to Control Plane `cpl` CLI as a [Ruby gem](https://rubygems.org/gems/cpl): `gem install cpl`. If you want to hack on the source code, see [CONTRIBUTING.md](CONTRIBUTING.md).
128
+ 6. Install Heroku to Control Plane `cpl` CLI as a [Ruby gem](https://rubygems.org/gems/cpl): `gem install cpl`. If you want to hack on the source code, see [CONTRIBUTING.md](CONTRIBUTING.md).
121
129
 
122
130
  7. You can use [this Dockerfile](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/Dockerfile) as an example for your project. Ensure that you have Docker running.
123
131
 
@@ -258,6 +266,10 @@ apps:
258
266
  # This is relative to the `.controlplane/` directory.
259
267
  release_script: release_script
260
268
 
269
+ # default_domain is used for commands that require a domain
270
+ # including `maintenance`, `maintenance:on`, `maintenance:off`.
271
+ default_domain: domain.com
272
+
261
273
  my-app-other:
262
274
  <<: *common
263
275
 
@@ -475,6 +487,13 @@ cpl --help
475
487
 
476
488
  ## Examples
477
489
 
478
- - See the `examples/` and `templates/` directories of this repository.
490
+ - See this repository's `examples/` and `templates/` directories.
479
491
  - See the `.controlplane/` directory of this live example:
480
- [react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial/tree/master/.controlplane)
492
+ [react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial/tree/master/.controlplane).
493
+ - See [how the `cpl` gem is used in the Github actions](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.github/actions/deploy-to-control-plane/action.yml).
494
+ - Here is a brief [video overview](https://www.youtube.com/watch?v=llaQoAV_6Iw).
495
+
496
+ ## Resources
497
+ * If you need a free demo account for Control Plane (no CC required), you can contact [Justin Gordon, CEO of ShakaCode](mailto:justin@shakacode.com).
498
+ * [Control Plane Site](https://shakacode.controlplane.com)
499
+ * [Join our Slack to Discuss Heroku to Control Plane](https://reactrails.slack.com/join/shared_invite/enQtNjY3NTczMjczNzYxLTlmYjdiZmY3MTVlMzU2YWE0OWM0MzNiZDI0MzdkZGFiZTFkYTFkOGVjODBmOWEyYWQ3MzA2NGE1YWJjNmVlMGE)
data/docs/commands.md CHANGED
@@ -41,6 +41,7 @@ cpl apply-template gvc postgres redis rails -a $APP_NAME
41
41
  - Automatically assigns image numbers, e.g., `app:1`, `app:2`, etc.
42
42
  - Uses `.controlplane/Dockerfile` or a different Dockerfile specified through `dockerfile` in the `.controlplane/controlplane.yml` file
43
43
  - If a commit is provided through `--commit` or `-c`, it will be set as the runtime env var `GIT_COMMIT`
44
+ - Accepts extra options that are passed to `docker build`
44
45
 
45
46
  ```sh
46
47
  cpl build-image -a $APP_NAME
@@ -86,7 +87,7 @@ cpl config -a $APP_NAME
86
87
  ### `copy-image-from-upstream`
87
88
 
88
89
  - Copies an image (by default the latest) from a source org to the current org
89
- - The source org must be specified through `upstream` in the `.controlplane/controlplane.yml` file
90
+ - The source app must be specified either through the `CPLN_UPSTREAM` env var or `upstream` in the `.controlplane/controlplane.yml` file
90
91
  - Additionally, the token for the source org must be provided through `--upstream-token` or `-t`
91
92
  - A `cpln` profile will be temporarily created to pull the image from the source org
92
93
 
@@ -331,28 +332,25 @@ cpl ps:swait -a $APP_NAME -w $WORKLOAD_NAME
331
332
  cpl run -a $APP_NAME
332
333
 
333
334
  # Need to quote COMMAND if setting ENV value or passing args.
334
- cpl run 'LOG_LEVEL=warn rails db:migrate' -a $APP_NAME
335
-
336
- # COMMAND may also be passed at the end.
337
335
  cpl run -a $APP_NAME -- 'LOG_LEVEL=warn rails db:migrate'
338
336
 
339
337
  # Runs command, displays output, and exits shell.
340
- cpl run ls / -a $APP_NAME
341
- cpl run rails db:migrate:status -a $APP_NAME
338
+ cpl run -a $APP_NAME -- ls /
339
+ cpl run -a $APP_NAME -- rails db:migrate:status
342
340
 
343
341
  # Runs command and keeps shell open.
344
- cpl run rails c -a $APP_NAME
342
+ cpl run -a $APP_NAME -- rails c
345
343
 
346
344
  # Uses a different image (which may not be promoted yet).
347
- cpl run rails db:migrate -a $APP_NAME --image appimage:123 # Exact image name
348
- cpl run rails db:migrate -a $APP_NAME --image latest # Latest sequential image
345
+ cpl run -a $APP_NAME --image appimage:123 -- rails db:migrate # Exact image name
346
+ cpl run -a $APP_NAME --image latest -- rails db:migrate # Latest sequential image
349
347
 
350
348
  # Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
351
- cpl run bash -a $APP_NAME -w other-workload
349
+ cpl run -a $APP_NAME -w other-workload -- bash
352
350
 
353
351
  # Overrides remote CPLN_TOKEN env variable with local token.
354
352
  # Useful when superuser rights are needed in remote container.
355
- cpl run bash -a $APP_NAME --use-local-token
353
+ cpl run -a $APP_NAME --use-local-token -- bash
356
354
  ```
357
355
 
358
356
  ### `run:cleanup`
@@ -374,26 +372,26 @@ cpl run:cleanup -a $APP_NAME
374
372
  - Implemented with only async execution methods, more suitable for production tasks
375
373
  - Has alternative log fetch implementation with only JSON-polling and no WebSockets
376
374
  - Less responsive but more stable, useful for CI tasks
375
+ - Deletes the workload whenever finished with success
376
+ - Deletes the workload whenever finished with failure by default
377
+ - Use `--no-clean-on-failure` to disable cleanup to help with debugging failed runs
377
378
 
378
379
  ```sh
379
380
  cpl run:detached rails db:prepare -a $APP_NAME
380
381
 
381
382
  # Need to quote COMMAND if setting ENV value or passing args.
382
- cpl run:detached 'LOG_LEVEL=warn rails db:migrate' -a $APP_NAME
383
-
384
- # COMMAND may also be passed at the end.
385
383
  cpl run:detached -a $APP_NAME -- 'LOG_LEVEL=warn rails db:migrate'
386
384
 
387
385
  # Uses a different image (which may not be promoted yet).
388
- cpl run:detached rails db:migrate -a $APP_NAME --image appimage:123 # Exact image name
389
- cpl run:detached rails db:migrate -a $APP_NAME --image latest # Latest sequential image
386
+ cpl run:detached -a $APP_NAME --image appimage:123 -- rails db:migrate # Exact image name
387
+ cpl run:detached -a $APP_NAME --image latest -- rails db:migrate # Latest sequential image
390
388
 
391
389
  # Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
392
- cpl run:detached rails db:migrate:status -a $APP_NAME -w other-workload
390
+ cpl run:detached -a $APP_NAME -w other-workload -- rails db:migrate:status
393
391
 
394
392
  # Overrides remote CPLN_TOKEN env variable with local token.
395
393
  # Useful when superuser rights are needed in remote container.
396
- cpl run:detached rails db:migrate:status -a $APP_NAME --use-local-token
394
+ cpl run:detached -a $APP_NAME --use-local-token -- rails db:migrate:status
397
395
  ```
398
396
 
399
397
  ### `setup-app`
data/docs/dns.md ADDED
@@ -0,0 +1,9 @@
1
+ # DNS Setup Example
2
+
3
+ About reactrails.com DNS, steps:
4
+ 1. create CNAME or ALIAS record pointing to rails-xxxx.cpln.app
5
+ 1. go to CPLN Domains -> create
6
+ 1. add reactrails.com copy code from there
7
+ 1. add TXT record with code as _cpln.reactrails.com
8
+ 1. hit ok on CPLN, route to rails workload when asked
9
+ 1. wait domain check and certificates created
@@ -101,6 +101,10 @@ apps:
101
101
  # This is relative to the `.controlplane/` directory.
102
102
  release_script: release_script
103
103
 
104
+ # default_domain is used for commands that require a domain
105
+ # including `maintenance`, `maintenance:on`, `maintenance:off`.
106
+ default_domain: domain.com
107
+
104
108
  my-app-other:
105
109
  <<: *common
106
110
 
data/lib/command/base.rb CHANGED
@@ -1,9 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../core/helpers"
4
+
3
5
  module Command
4
6
  class Base # rubocop:disable Metrics/ClassLength
5
7
  attr_reader :config
6
8
 
9
+ include Helpers
10
+
7
11
  # Used to call the command (`cpl NAME`)
8
12
  # NAME = ""
9
13
  # Displayed when running `cpl help` or `cpl help NAME` (defaults to `NAME`)
@@ -15,6 +19,9 @@ module Command
15
19
  DEFAULT_ARGS = [].freeze
16
20
  # Options for the command (use option methods below)
17
21
  OPTIONS = [].freeze
22
+ # Does not throw error if `true` and extra options
23
+ # that are not specified in `OPTIONS` are passed to the command
24
+ ACCEPTS_EXTRA_OPTIONS = false
18
25
  # Displayed when running `cpl help`
19
26
  # DESCRIPTION = ""
20
27
  # Displayed when running `cpl help NAME`
@@ -122,6 +129,18 @@ module Command
122
129
  }
123
130
  end
124
131
 
132
+ def self.domain_option(required: false)
133
+ {
134
+ name: :domain,
135
+ params: {
136
+ banner: "DOMAIN_NAME",
137
+ desc: "Domain name",
138
+ type: :string,
139
+ required: required
140
+ }
141
+ }
142
+ end
143
+
125
144
  def self.upstream_token_option(required: false)
126
145
  {
127
146
  name: :upstream_token,
@@ -218,6 +237,18 @@ module Command
218
237
  }
219
238
  end
220
239
 
240
+ def self.clean_on_failure_option(required: false)
241
+ {
242
+ name: :clean_on_failure,
243
+ params: {
244
+ desc: "Deletes workload when finished with failure (success always deletes)",
245
+ type: :boolean,
246
+ required: required,
247
+ default: true
248
+ }
249
+ }
250
+ end
251
+
221
252
  def self.all_options
222
253
  methods.grep(/_option$/).map { |method| send(method.to_s) }
223
254
  end
@@ -7,12 +7,14 @@ module Command
7
7
  app_option(required: true),
8
8
  commit_option
9
9
  ].freeze
10
+ ACCEPTS_EXTRA_OPTIONS = true
10
11
  DESCRIPTION = "Builds and pushes the image to Control Plane"
11
12
  LONG_DESCRIPTION = <<~DESC
12
13
  - Builds and pushes the image to Control Plane
13
14
  - Automatically assigns image numbers, e.g., `app:1`, `app:2`, etc.
14
15
  - Uses `.controlplane/Dockerfile` or a different Dockerfile specified through `dockerfile` in the `.controlplane/controlplane.yml` file
15
16
  - If a commit is provided through `--commit` or `-c`, it will be set as the runtime env var `GIT_COMMIT`
17
+ - Accepts extra options that are passed to `docker build`
16
18
  DESC
17
19
 
18
20
  def call # rubocop:disable Metrics/MethodLength
@@ -32,7 +34,9 @@ module Command
32
34
  build_args = []
33
35
  build_args.push("GIT_COMMIT=#{commit}") if commit
34
36
 
35
- cp.image_build(image_url, dockerfile: dockerfile, build_args: build_args)
37
+ cp.image_build(image_url, dockerfile: dockerfile,
38
+ docker_args: config.args,
39
+ build_args: build_args)
36
40
 
37
41
  progress.puts("\nPushed image to '/org/#{config.org}/image/#{image_name}'.")
38
42
  end
@@ -11,7 +11,7 @@ module Command
11
11
  DESCRIPTION = "Copies an image (by default the latest) from a source org to the current org"
12
12
  LONG_DESCRIPTION = <<~DESC
13
13
  - Copies an image (by default the latest) from a source org to the current org
14
- - The source org must be specified through `upstream` in the `.controlplane/controlplane.yml` file
14
+ - The source app must be specified either through the `CPLN_UPSTREAM` env var or `upstream` in the `.controlplane/controlplane.yml` file
15
15
  - Additionally, the token for the source org must be provided through `--upstream-token` or `-t`
16
16
  - A `cpln` profile will be temporarily created to pull the image from the source org
17
17
  DESC
@@ -28,8 +28,8 @@ module Command
28
28
  def call # rubocop:disable Metrics/MethodLength
29
29
  ensure_docker_running!
30
30
 
31
- @upstream = config[:upstream]
32
- @upstream_org = config.apps[@upstream.to_sym][:cpln_org] || ENV.fetch("CPLN_ORG_UPSTREAM", nil)
31
+ @upstream = ENV.fetch("CPLN_UPSTREAM", nil) || config[:upstream]
32
+ @upstream_org = ENV.fetch("CPLN_ORG_UPSTREAM", nil) || config.find_app_config(@upstream)&.dig(:cpln_org)
33
33
  ensure_upstream_org!
34
34
 
35
35
  create_upstream_profile
@@ -38,6 +38,7 @@ module Command
38
38
  pull_image_from_upstream
39
39
  push_image_to_app
40
40
  ensure
41
+ cp.profile_switch("default")
41
42
  delete_upstream_profile
42
43
  end
43
44
 
@@ -60,7 +61,7 @@ module Command
60
61
  def create_upstream_profile
61
62
  step("Creating upstream profile") do
62
63
  loop do
63
- @upstream_profile = "upstream-#{rand(1000..9999)}"
64
+ @upstream_profile = "upstream-#{random_four_digits}"
64
65
  break unless cp.profile_exists?(@upstream_profile)
65
66
  end
66
67
 
data/lib/command/info.rb CHANGED
@@ -32,7 +32,7 @@ module Command
32
32
  @missing_apps_workloads = {}
33
33
  @missing_apps_starting_with = {}
34
34
 
35
- if config.app && !config.current[:match_if_app_name_starts_with]
35
+ if config.app && !config.should_app_start_with?(config.app)
36
36
  single_app_info
37
37
  else
38
38
  multiple_apps_info
@@ -41,20 +41,8 @@ module Command
41
41
 
42
42
  private
43
43
 
44
- def app_matches?(app, app_name, app_options)
45
- app == app_name.to_s || (app_options[:match_if_app_name_starts_with] && app.start_with?(app_name.to_s))
46
- end
47
-
48
- def find_app_options(app)
49
- @app_options ||= {}
50
- @app_options[app] ||= config.apps.find do |app_name, app_options|
51
- app_matches?(app, app_name, app_options)
52
- end&.last
53
- end
54
-
55
44
  def find_workloads(app)
56
- app_options = find_app_options(app)
57
- return [] if app_options.nil?
45
+ app_options = config.find_app_config(app)
58
46
 
59
47
  (app_options[:app_workloads] + app_options[:additional_workloads] + [app_options[:one_off_workload]]).uniq
60
48
  end
@@ -75,21 +63,19 @@ module Command
75
63
  end
76
64
 
77
65
  if config.app
78
- result.select { |app, _| app_matches?(app, config.app, config.current) }
66
+ result.select { |app, _| config.app_matches?(app, config.app, config.current) }
79
67
  else
80
- result.reject { |app, _| find_app_options(app).nil? }
68
+ result.reject { |app, _| config.find_app_config(app).nil? }
81
69
  end
82
70
  end
83
71
 
84
- def orgs # rubocop:disable Metrics/MethodLength
72
+ def orgs
85
73
  result = []
86
74
 
87
75
  if config.org
88
76
  result.push(config.org)
89
77
  else
90
- config.apps.each do |app_name, app_options|
91
- next if config.app && !app_matches?(config.app, app_name, app_options)
92
-
78
+ config.apps.each do |_, app_options|
93
79
  org = app_options[:cpln_org]
94
80
  result.push(org) if org && !result.include?(org)
95
81
  end
@@ -102,7 +88,7 @@ module Command
102
88
  result = []
103
89
 
104
90
  config.apps.each do |app_name, app_options|
105
- next if config.app && !app_matches?(config.app, app_name, app_options)
91
+ next if config.app && !config.app_matches?(config.app, app_name, app_options)
106
92
 
107
93
  app_org = app_options[:cpln_org]
108
94
  result.push(app_name.to_s) if app_org == org
@@ -113,7 +99,7 @@ module Command
113
99
  end
114
100
 
115
101
  def any_app_starts_with?(app)
116
- @app_workloads.keys.find { |app_name| app_matches?(app_name, app, config.apps[app.to_sym]) }
102
+ @app_workloads.keys.find { |app_name| config.app_matches?(app_name, app, config.apps[app.to_sym]) }
117
103
  end
118
104
 
119
105
  def check_any_app_starts_with(app)
@@ -184,8 +170,7 @@ module Command
184
170
  "(replace 'whatever' with whatever suffix you want):"
185
171
 
186
172
  @missing_apps_starting_with.each do |app, _workloads|
187
- app_with_suffix = "#{app}#{app.end_with?('-') ? '' : '-'}whatever"
188
- puts " - `cpl setup-app -a #{app_with_suffix}`"
173
+ puts " - `cpl setup-app -a #{app}-whatever`"
189
174
  end
190
175
  end
191
176
 
@@ -197,7 +182,7 @@ module Command
197
182
  @defined_workloads = find_workloads(config.app)
198
183
  @available_workloads = fetch_workloads(config.app)
199
184
 
200
- workloads = (@defined_workloads + @available_workloads).uniq.sort
185
+ workloads = (@defined_workloads + @available_workloads).uniq
201
186
  workloads.each do |workload|
202
187
  print_workload(config.app, workload)
203
188
  end
@@ -217,7 +202,7 @@ module Command
217
202
  @defined_workloads = find_workloads(app)
218
203
  @available_workloads = @app_workloads[app] || []
219
204
 
220
- workloads = (@defined_workloads + @available_workloads).uniq.sort
205
+ workloads = (@defined_workloads + @available_workloads).uniq
221
206
  workloads.each do |workload|
222
207
  print_workload(app, workload)
223
208
  end
@@ -4,7 +4,8 @@ module Command
4
4
  class Maintenance < Base
5
5
  NAME = "maintenance"
6
6
  OPTIONS = [
7
- app_option(required: true)
7
+ app_option(required: true),
8
+ domain_option
8
9
  ].freeze
9
10
  DESCRIPTION = "Checks if maintenance mode is on or off for an app"
10
11
  LONG_DESCRIPTION = <<~DESC
@@ -20,15 +21,18 @@ module Command
20
21
  one_off_workload = config[:one_off_workload]
21
22
  maintenance_workload = config.current[:maintenance_workload] || "maintenance"
22
23
 
23
- domain_data = cp.find_domain_for([one_off_workload, maintenance_workload])
24
+ domain_data = if config.domain
25
+ cp.fetch_domain(config.domain)
26
+ else
27
+ cp.find_domain_for([one_off_workload, maintenance_workload])
28
+ end
24
29
  unless domain_data
25
30
  raise "Can't find domain. " \
26
31
  "Maintenance mode is only supported for domains that use path based routing mode " \
27
32
  "and have a route configured for the prefix '/' on either port 80 or 443."
28
33
  end
29
34
 
30
- domain_workload = cp.get_domain_workload(domain_data)
31
- if domain_workload == maintenance_workload
35
+ if cp.domain_workload_matches?(domain_data, maintenance_workload)
32
36
  puts "on"
33
37
  else
34
38
  puts "off"
@@ -4,7 +4,8 @@ module Command
4
4
  class MaintenanceOff < Base
5
5
  NAME = "maintenance:off"
6
6
  OPTIONS = [
7
- app_option(required: true)
7
+ app_option(required: true),
8
+ domain_option
8
9
  ].freeze
9
10
  DESCRIPTION = "Disables maintenance mode for an app"
10
11
  LONG_DESCRIPTION = <<~DESC
@@ -18,7 +19,11 @@ module Command
18
19
  one_off_workload = config[:one_off_workload]
19
20
  maintenance_workload = config.current[:maintenance_workload] || "maintenance"
20
21
 
21
- domain_data = cp.find_domain_for([one_off_workload, maintenance_workload])
22
+ domain_data = if config.domain
23
+ cp.fetch_domain(config.domain)
24
+ else
25
+ cp.find_domain_for([one_off_workload, maintenance_workload])
26
+ end
22
27
  unless domain_data
23
28
  raise "Can't find domain. " \
24
29
  "Maintenance mode is only supported for domains that use path based routing mode " \
@@ -26,8 +31,7 @@ module Command
26
31
  end
27
32
 
28
33
  domain = domain_data["name"]
29
- domain_workload = cp.get_domain_workload(domain_data)
30
- if domain_workload == one_off_workload
34
+ if cp.domain_workload_matches?(domain_data, one_off_workload)
31
35
  progress.puts("Maintenance mode is already disabled for app '#{config.app}'.")
32
36
  return
33
37
  end
@@ -4,7 +4,8 @@ module Command
4
4
  class MaintenanceOn < Base
5
5
  NAME = "maintenance:on"
6
6
  OPTIONS = [
7
- app_option(required: true)
7
+ app_option(required: true),
8
+ domain_option
8
9
  ].freeze
9
10
  DESCRIPTION = "Enables maintenance mode for an app"
10
11
  LONG_DESCRIPTION = <<~DESC
@@ -18,7 +19,11 @@ module Command
18
19
  one_off_workload = config[:one_off_workload]
19
20
  maintenance_workload = config.current[:maintenance_workload] || "maintenance"
20
21
 
21
- domain_data = cp.find_domain_for([one_off_workload, maintenance_workload])
22
+ domain_data = if config.domain
23
+ cp.fetch_domain(config.domain)
24
+ else
25
+ cp.find_domain_for([one_off_workload, maintenance_workload])
26
+ end
22
27
  unless domain_data
23
28
  raise "Can't find domain. " \
24
29
  "Maintenance mode is only supported for domains that use path based routing mode " \
@@ -26,8 +31,7 @@ module Command
26
31
  end
27
32
 
28
33
  domain = domain_data["name"]
29
- domain_workload = cp.get_domain_workload(domain_data)
30
- if domain_workload == maintenance_workload
34
+ if cp.domain_workload_matches?(domain_data, maintenance_workload)
31
35
  progress.puts("Maintenance mode is already enabled for app '#{config.app}'.")
32
36
  return
33
37
  end
data/lib/command/run.rb CHANGED
@@ -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 ls / -a $APP_NAME
39
- cpl run rails db:migrate:status -a $APP_NAME
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 rails c -a $APP_NAME
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 rails db:migrate -a $APP_NAME --image appimage:123 # Exact image name
46
- cpl run rails db:migrate -a $APP_NAME --image latest # Latest sequential image
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 bash -a $APP_NAME -w other-workload
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 bash -a $APP_NAME --use-local-token
50
+ cpl run -a $APP_NAME --use-local-token -- bash
54
51
  ```
55
52
  EX
56
53
 
57
- attr_reader :location, :workload, :one_off, :container
54
+ attr_reader :location, :workload_to_clone, :workload_clone, :container
58
55
 
59
56
  def call # rubocop:disable Metrics/MethodLength
60
57
  @location = config.location
61
- @workload = config.options["workload"] || config[:one_off_workload]
62
- @one_off = "#{workload}-run-#{rand(1000..9999)}"
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 '#{workload}' on app '#{config.options[:app]}' to '#{one_off}'") do
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(one_off)
69
- wait_for_replica(one_off, location)
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(one_off)
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!(workload).fetch("spec")
81
- container_spec = spec["containers"].detect { _1["name"] == workload } || spec["containers"].first
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" => one_off, "spec" => spec)
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(one_off, location: location, container: container, command: command)
141
+ cp.workload_exec(workload_clone, location: location, container: container, command: command)
145
142
  end
146
143
  end
147
144
  end
@@ -10,7 +10,8 @@ module Command
10
10
  image_option,
11
11
  workload_option,
12
12
  location_option,
13
- use_local_token_option
13
+ use_local_token_option,
14
+ clean_on_failure_option
14
15
  ].freeze
15
16
  DESCRIPTION = "Runs one-off **_non-interactive_** replicas (close analog of `heroku run:detached`)"
16
17
  LONG_DESCRIPTION = <<~DESC
@@ -19,50 +20,46 @@ module Command
19
20
  - Implemented with only async execution methods, more suitable for production tasks
20
21
  - Has alternative log fetch implementation with only JSON-polling and no WebSockets
21
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
22
26
  DESC
23
27
  EXAMPLES = <<~EX
24
28
  ```sh
25
29
  cpl run:detached rails db:prepare -a $APP_NAME
26
30
 
27
31
  # Need to quote COMMAND if setting ENV value or passing args.
28
- cpl run:detached 'LOG_LEVEL=warn rails db:migrate' -a $APP_NAME
29
-
30
- # COMMAND may also be passed at the end.
31
32
  cpl run:detached -a $APP_NAME -- 'LOG_LEVEL=warn rails db:migrate'
32
33
 
33
34
  # Uses a different image (which may not be promoted yet).
34
- cpl run:detached rails db:migrate -a $APP_NAME --image appimage:123 # Exact image name
35
- cpl run:detached rails db:migrate -a $APP_NAME --image latest # Latest sequential image
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
36
37
 
37
38
  # Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
38
- cpl run:detached rails db:migrate:status -a $APP_NAME -w other-workload
39
+ cpl run:detached -a $APP_NAME -w other-workload -- rails db:migrate:status
39
40
 
40
41
  # Overrides remote CPLN_TOKEN env variable with local token.
41
42
  # Useful when superuser rights are needed in remote container.
42
- cpl run:detached rails db:migrate:status -a $APP_NAME --use-local-token
43
+ cpl run:detached -a $APP_NAME --use-local-token -- rails db:migrate:status
43
44
  ```
44
45
  EX
45
46
 
46
47
  WORKLOAD_SLEEP_CHECK = 2
47
48
 
48
- attr_reader :location, :workload, :one_off, :container
49
+ attr_reader :location, :workload_to_clone, :workload_clone, :container
49
50
 
50
- def call # rubocop:disable Metrics/MethodLength
51
+ def call
51
52
  @location = config.location
52
- @workload = config.options["workload"] || config[:one_off_workload]
53
- @one_off = "#{workload}-runner-#{rand(1000..9999)}"
53
+ @workload_to_clone = config.options["workload"] || config[:one_off_workload]
54
+ @workload_clone = "#{workload_to_clone}-runner-#{random_four_digits}"
54
55
 
55
- step("Cloning workload '#{workload}' on app '#{config.options[:app]}' to '#{one_off}'") do
56
+ step("Cloning workload '#{workload_to_clone}' on app '#{config.options[:app]}' to '#{workload_clone}'") do
56
57
  clone_workload
57
58
  end
58
59
 
59
- wait_for_workload(one_off)
60
+ wait_for_workload(workload_clone)
60
61
  show_logs_waiting
61
62
  ensure
62
- if cp.fetch_workload(one_off)
63
- progress.puts
64
- ensure_workload_deleted(one_off)
65
- end
66
63
  exit(1) if @crashed
67
64
  end
68
65
 
@@ -70,8 +67,8 @@ module Command
70
67
 
71
68
  def clone_workload # rubocop:disable Metrics/MethodLength
72
69
  # Get base specs of workload
73
- spec = cp.fetch_workload!(workload).fetch("spec")
74
- container_spec = spec["containers"].detect { _1["name"] == workload } || spec["containers"].first
70
+ spec = cp.fetch_workload!(workload_to_clone).fetch("spec")
71
+ container_spec = spec["containers"].detect { _1["name"] == workload_to_clone } || spec["containers"].first
75
72
  @container = container_spec["name"]
76
73
 
77
74
  # remove other containers if any
@@ -105,7 +102,7 @@ module Command
105
102
  container_spec["env"] << { "name" => "CONTROLPLANE_RUNNER", "value" => runner_script }
106
103
 
107
104
  # Create workload clone
108
- cp.apply_hash("kind" => "workload", "name" => one_off, "spec" => spec)
105
+ cp.apply_hash("kind" => "workload", "name" => workload_clone, "spec" => spec)
109
106
  end
110
107
 
111
108
  def runner_script # rubocop:disable Metrics/MethodLength
@@ -119,12 +116,20 @@ module Command
119
116
  end
120
117
 
121
118
  script += <<~SHELL
122
- if ! eval "#{args_join(config.args)}"; then echo "----- CRASHED -----"; fi
123
-
124
- echo "-- FINISHED RUNNER SCRIPT, DELETING WORKLOAD --"
125
- sleep 10 # grace time for logs propagation
126
- curl ${CPLN_ENDPOINT}${CPLN_WORKLOAD} -H "Authorization: ${CONTROLPLANE_TOKEN}" -X DELETE -s -o /dev/null
127
- while true; do sleep 1; done # wait for SIGTERM
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
128
133
  SHELL
129
134
 
130
135
  script
@@ -133,7 +138,8 @@ module Command
133
138
  def show_logs_waiting # rubocop:disable Metrics/MethodLength
134
139
  progress.puts("Scheduled, fetching logs (it's a cron job, so it may take up to a minute to start)...\n\n")
135
140
  begin
136
- while cp.fetch_workload(one_off)
141
+ @finished = false
142
+ while cp.fetch_workload(workload_clone) && !@finished
137
143
  sleep(WORKLOAD_SLEEP_CHECK)
138
144
  print_uniq_logs
139
145
  end
@@ -151,6 +157,7 @@ module Command
151
157
 
152
158
  (entries - @printed_log_entries).sort.each do |(_ts, val)|
153
159
  @crashed = true if val.match?(/^----- CRASHED -----$/)
160
+ @finished = true if val.match?(/^-- FINISHED RUNNER SCRIPT(, DELETING WORKLOAD)? --$/)
154
161
  puts val
155
162
  end
156
163
 
@@ -158,7 +165,7 @@ module Command
158
165
  end
159
166
 
160
167
  def normalized_log_entries(from:, to:)
161
- log = cp.log_get(workload: one_off, from: from, to: to)
168
+ log = cp.log_get(workload: workload_clone, from: from, to: to)
162
169
 
163
170
  log["data"]["result"]
164
171
  .each_with_object([]) { |obj, result| result.concat(obj["values"]) }
data/lib/core/config.rb CHANGED
@@ -38,6 +38,10 @@ class Config # rubocop:disable Metrics/ClassLength
38
38
  @location ||= load_location_from_options || load_location_from_env || load_location_from_file
39
39
  end
40
40
 
41
+ def domain
42
+ @domain ||= load_domain_from_options || load_domain_from_file
43
+ end
44
+
41
45
  def [](key)
42
46
  ensure_current_config!
43
47
 
@@ -98,6 +102,20 @@ class Config # rubocop:disable Metrics/ClassLength
98
102
  end
99
103
  end
100
104
 
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
+ )
110
+ end
111
+
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
117
+ end
118
+
101
119
  private
102
120
 
103
121
  def ensure_current_config!
@@ -116,20 +134,6 @@ class Config # rubocop:disable Metrics/ClassLength
116
134
  raise "Can't find config for app '#{app_name}' in 'controlplane.yml'." unless app_options
117
135
  end
118
136
 
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
- )
124
- end
125
-
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
132
-
133
137
  def ensure_app!
134
138
  return if app
135
139
 
@@ -253,6 +257,16 @@ class Config # rubocop:disable Metrics/ClassLength
253
257
  strip_str_and_validate(current.fetch(:default_location))
254
258
  end
255
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
+
256
270
  def warn_deprecated_options(app_options)
257
271
  deprecated_option_keys = new_option_keys.select { |old_key| app_options.key?(old_key) }
258
272
  return if deprecated_option_keys.empty?
@@ -44,12 +44,13 @@ class Controlplane # rubocop:disable Metrics/ClassLength
44
44
  api.query_images(org: a_org, gvc: a_gvc, gvc_op_type: gvc_op)
45
45
  end
46
46
 
47
- def image_build(image, dockerfile:, build_args: [], push: true)
47
+ def image_build(image, dockerfile:, docker_args: [], build_args: [], push: true)
48
48
  # https://docs.controlplane.com/guides/push-image#step-2
49
49
  # Might need to use `docker buildx build` if compatiblitity issues arise
50
50
  cmd = "docker build --platform=linux/amd64 -t #{image} -f #{dockerfile}"
51
51
  cmd += " --progress=plain" if ControlplaneApiDirect.trace
52
52
 
53
+ cmd += " #{docker_args.join(' ')}" if docker_args.any?
53
54
  build_args.each { |build_arg| cmd += " --build-arg #{build_arg}" }
54
55
  cmd += " #{config.app_dir}"
55
56
  perform!(cmd)
@@ -264,13 +265,21 @@ class Controlplane # rubocop:disable Metrics/ClassLength
264
265
  route = find_domain_route(domain_data)
265
266
  next false if route.nil?
266
267
 
267
- workloads.any? { |workload| route["workloadLink"].split("/").last == workload }
268
+ workloads.any? { |workload| route["workloadLink"].match?(%r{/org/#{org}/gvc/#{gvc}/workload/#{workload}}) }
268
269
  end
269
270
  end
270
271
 
271
- def get_domain_workload(data)
272
+ def fetch_domain(domain)
273
+ domain_data = api.fetch_domain(org: org, domain: domain)
274
+ route = find_domain_route(domain_data)
275
+ return nil if route.nil?
276
+
277
+ domain_data
278
+ end
279
+
280
+ def domain_workload_matches?(data, workload)
272
281
  route = find_domain_route(data)
273
- route["workloadLink"].split("/").last
282
+ route["workloadLink"].match?(%r{/org/#{org}/gvc/#{gvc}/workload/#{workload}})
274
283
  end
275
284
 
276
285
  def set_domain_workload(data, workload)
@@ -94,6 +94,10 @@ class ControlplaneApi # rubocop:disable Metrics/ClassLength
94
94
  api_json("/org/#{org}/gvc/#{gvc}/volumeset/#{volumeset}", method: :delete)
95
95
  end
96
96
 
97
+ def fetch_domain(org:, domain:)
98
+ api_json("/org/#{org}/domain/#{domain}", method: :get)
99
+ end
100
+
97
101
  def list_domains(org:)
98
102
  api_json("/org/#{org}/domain", method: :get)
99
103
  end
data/lib/core/helpers.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "securerandom"
4
+
3
5
  module Helpers
4
6
  def strip_str_and_validate(str)
5
7
  return str if str.nil?
@@ -7,4 +9,8 @@ module Helpers
7
9
  str = str.strip
8
10
  str.empty? ? nil : str
9
11
  end
12
+
13
+ def random_four_digits
14
+ SecureRandom.random_number(1000..9999)
15
+ end
10
16
  end
data/lib/cpl/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cpl
4
- VERSION = "1.2.0"
4
+ VERSION = "1.3.0"
5
5
  MIN_CPLN_VERSION = "0.0.71"
6
6
  end
data/lib/cpl.rb CHANGED
@@ -142,6 +142,7 @@ module Cpl
142
142
  requires_args = command_class::REQUIRES_ARGS
143
143
  default_args = command_class::DEFAULT_ARGS
144
144
  command_options = command_class::OPTIONS + ::Command::Base.common_options
145
+ accepts_extra_options = command_class::ACCEPTS_EXTRA_OPTIONS
145
146
  description = command_class::DESCRIPTION
146
147
  long_description = command_class::LONG_DESCRIPTION
147
148
  examples = command_class::EXAMPLES
@@ -178,7 +179,9 @@ module Cpl
178
179
  default_args
179
180
  end
180
181
 
181
- raise_args_error.call(args, nil) if (args.empty? && requires_args) || (!args.empty? && !requires_args)
182
+ if (args.empty? && requires_args) || (!args.empty? && !requires_args && !accepts_extra_options)
183
+ raise_args_error.call(args, nil)
184
+ end
182
185
 
183
186
  begin
184
187
  config = Config.new(args, options, required_options)
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env bash
2
+
3
+ bad_links=("controlplane.com/shakacode")
4
+ proper_links=("shakacode.controlplane.com")
5
+
6
+ bold=$(tput bold)
7
+ normal=$(tput sgr0)
8
+
9
+ exit_status=0
10
+ accumulated_results=""
11
+ seen_bad_links_indexes=()
12
+
13
+ for ((idx = 0; idx < ${#bad_links[@]}; idx++)); do
14
+ results=$(git grep \
15
+ --recursive \
16
+ --line-number \
17
+ --fixed-strings \
18
+ --break \
19
+ --heading \
20
+ --color=always -- \
21
+ "${bad_links[idx]}" \
22
+ ':!script/check_cpln_links')
23
+
24
+ # Line would become really unwieldly if everything was mushed into the
25
+ # conditional, so let's ignore this check here.
26
+ # shellcheck disable=SC2181
27
+ if [ $? -eq 0 ]; then
28
+ accumulated_results+="$results"
29
+ seen_bad_links_indexes+=("$idx")
30
+ exit_status=1
31
+ fi
32
+ done
33
+
34
+ if [ "$exit_status" -eq 1 ]; then
35
+ echo "${bold}[!] Found the following bad links:${normal}"
36
+ echo ""
37
+ echo "$accumulated_results"
38
+ echo ""
39
+ echo "${bold}[*] Please update accordingly:${normal}"
40
+ for bad_link_index in "${seen_bad_links_indexes[@]}"; do
41
+ echo " ${bad_links[bad_link_index]} -> ${proper_links[bad_link_index]}"
42
+ done
43
+ fi
44
+
45
+ exit "$exit_status"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cpl
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Gordon
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-01-04 00:00:00.000000000 Z
12
+ date: 2024-03-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: debug
@@ -202,6 +202,7 @@ executables:
202
202
  extensions: []
203
203
  extra_rdoc_files: []
204
204
  files:
205
+ - ".github/workflows/check_cpln_links.yml"
205
206
  - ".github/workflows/command_docs.yml"
206
207
  - ".github/workflows/rspec.yml"
207
208
  - ".github/workflows/rubocop.yml"
@@ -223,6 +224,7 @@ files:
223
224
  - docs/assets/memcached.png
224
225
  - docs/assets/sidekiq-pre-stop-hook.png
225
226
  - docs/commands.md
227
+ - docs/dns.md
226
228
  - docs/migrating.md
227
229
  - docs/postgres.md
228
230
  - docs/redis.md
@@ -285,6 +287,7 @@ files:
285
287
  - rakelib/create_release.rake
286
288
  - script/add_command
287
289
  - script/check_command_docs
290
+ - script/check_cpln_links
288
291
  - script/rename_command
289
292
  - script/update_command_docs
290
293
  - templates/daily-task.yml