cpl 1.2.0 → 1.3.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 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