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 +4 -4
- data/.github/workflows/check_cpln_links.yml +19 -0
- data/.overcommit.yml +3 -0
- data/CHANGELOG.md +24 -1
- data/Gemfile.lock +5 -5
- data/README.md +26 -7
- data/docs/commands.md +16 -18
- data/docs/dns.md +9 -0
- data/examples/controlplane.yml +4 -0
- data/lib/command/base.rb +31 -0
- data/lib/command/build_image.rb +5 -1
- data/lib/command/copy_image_from_upstream.rb +5 -4
- data/lib/command/info.rb +11 -26
- data/lib/command/maintenance.rb +8 -4
- data/lib/command/maintenance_off.rb +8 -4
- data/lib/command/maintenance_on.rb +8 -4
- data/lib/command/run.rb +18 -21
- data/lib/command/run_detached.rb +36 -29
- data/lib/core/config.rb +28 -14
- data/lib/core/controlplane.rb +13 -4
- data/lib/core/controlplane_api.rb +4 -0
- data/lib/core/helpers.rb +6 -0
- data/lib/cpl/version.rb +1 -1
- data/lib/cpl.rb +4 -1
- data/script/check_cpln_links +45 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d76f46cb9427fc137ece07123d0ddd0d1add98e1ffa046a95e1394916784676e
|
4
|
+
data.tar.gz: 266681f356c78d8a4636ae27cb90485a49eda80c27a6199bd3505ddd72cd3451
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
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.
|
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.
|
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.
|
28
|
-
irb (1.
|
27
|
+
io-console (0.7.2)
|
28
|
+
irb (1.12.0)
|
29
29
|
rdoc
|
30
|
-
reline (>= 0.
|
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.
|
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
|
-
#
|
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
|
[](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rspec.yml)
|
12
10
|
[](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rubocop.yml)
|
13
11
|
|
14
12
|
[](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
|
-
|
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
|
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
|
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
|
341
|
-
cpl run rails db:migrate:status
|
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
|
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
|
348
|
-
cpl run
|
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
|
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
|
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
|
389
|
-
cpl run:detached
|
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
|
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
|
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
|
data/examples/controlplane.yml
CHANGED
@@ -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
|
data/lib/command/build_image.rb
CHANGED
@@ -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,
|
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
|
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 =
|
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-#{
|
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.
|
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 =
|
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, _|
|
68
|
+
result.reject { |app, _| config.find_app_config(app).nil? }
|
81
69
|
end
|
82
70
|
end
|
83
71
|
|
84
|
-
def orgs
|
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 |
|
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
|
-
|
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
|
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
|
205
|
+
workloads = (@defined_workloads + @available_workloads).uniq
|
221
206
|
workloads.each do |workload|
|
222
207
|
print_workload(app, workload)
|
223
208
|
end
|
data/lib/command/maintenance.rb
CHANGED
@@ -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 =
|
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
|
-
|
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 =
|
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
|
-
|
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 =
|
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
|
-
|
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
|
39
|
-
cpl run rails db:migrate:status
|
35
|
+
cpl run -a $APP_NAME -- ls /
|
36
|
+
cpl run -a $APP_NAME -- rails db:migrate:status
|
40
37
|
|
41
38
|
# Runs command and keeps shell open.
|
42
|
-
cpl run
|
39
|
+
cpl run -a $APP_NAME -- rails c
|
43
40
|
|
44
41
|
# Uses a different image (which may not be promoted yet).
|
45
|
-
cpl run
|
46
|
-
cpl run
|
42
|
+
cpl run -a $APP_NAME --image appimage:123 -- rails db:migrate # Exact image name
|
43
|
+
cpl run -a $APP_NAME --image latest -- rails db:migrate # Latest sequential image
|
47
44
|
|
48
45
|
# Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
|
49
|
-
cpl run
|
46
|
+
cpl run -a $APP_NAME -w other-workload -- bash
|
50
47
|
|
51
48
|
# Overrides remote CPLN_TOKEN env variable with local token.
|
52
49
|
# Useful when superuser rights are needed in remote container.
|
53
|
-
cpl run
|
50
|
+
cpl run -a $APP_NAME --use-local-token -- bash
|
54
51
|
```
|
55
52
|
EX
|
56
53
|
|
57
|
-
attr_reader :location, :
|
54
|
+
attr_reader :location, :workload_to_clone, :workload_clone, :container
|
58
55
|
|
59
56
|
def call # rubocop:disable Metrics/MethodLength
|
60
57
|
@location = config.location
|
61
|
-
@
|
62
|
-
@
|
58
|
+
@workload_to_clone = config.options["workload"] || config[:one_off_workload]
|
59
|
+
@workload_clone = "#{workload_to_clone}-run-#{random_four_digits}"
|
63
60
|
|
64
|
-
step("Cloning workload '#{
|
61
|
+
step("Cloning workload '#{workload_to_clone}' on app '#{config.options[:app]}' to '#{workload_clone}'") do
|
65
62
|
clone_workload
|
66
63
|
end
|
67
64
|
|
68
|
-
wait_for_workload(
|
69
|
-
wait_for_replica(
|
65
|
+
wait_for_workload(workload_clone)
|
66
|
+
wait_for_replica(workload_clone, location)
|
70
67
|
run_in_replica
|
71
68
|
ensure
|
72
69
|
progress.puts
|
73
|
-
ensure_workload_deleted(
|
70
|
+
ensure_workload_deleted(workload_clone)
|
74
71
|
end
|
75
72
|
|
76
73
|
private
|
77
74
|
|
78
75
|
def clone_workload # rubocop:disable Metrics/MethodLength
|
79
76
|
# Create a base copy of workload props
|
80
|
-
spec = cp.fetch_workload!(
|
81
|
-
container_spec = spec["containers"].detect { _1["name"] ==
|
77
|
+
spec = cp.fetch_workload!(workload_to_clone).fetch("spec")
|
78
|
+
container_spec = spec["containers"].detect { _1["name"] == workload_to_clone } || spec["containers"].first
|
82
79
|
@container = container_spec["name"]
|
83
80
|
|
84
81
|
# remove other containers if any
|
@@ -111,7 +108,7 @@ module Command
|
|
111
108
|
end
|
112
109
|
|
113
110
|
# Create workload clone
|
114
|
-
cp.apply_hash("kind" => "workload", "name" =>
|
111
|
+
cp.apply_hash("kind" => "workload", "name" => workload_clone, "spec" => spec)
|
115
112
|
end
|
116
113
|
|
117
114
|
def runner_script # rubocop:disable Metrics/MethodLength
|
@@ -141,7 +138,7 @@ module Command
|
|
141
138
|
def run_in_replica
|
142
139
|
progress.puts("Connecting...\n\n")
|
143
140
|
command = %(bash -c 'eval "$CONTROLPLANE_RUNNER"')
|
144
|
-
cp.workload_exec(
|
141
|
+
cp.workload_exec(workload_clone, location: location, container: container, command: command)
|
145
142
|
end
|
146
143
|
end
|
147
144
|
end
|
data/lib/command/run_detached.rb
CHANGED
@@ -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
|
35
|
-
cpl run:detached
|
35
|
+
cpl run:detached -a $APP_NAME --image appimage:123 -- rails db:migrate # Exact image name
|
36
|
+
cpl run:detached -a $APP_NAME --image latest -- rails db:migrate # Latest sequential image
|
36
37
|
|
37
38
|
# Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
|
38
|
-
cpl run:detached
|
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
|
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, :
|
49
|
+
attr_reader :location, :workload_to_clone, :workload_clone, :container
|
49
50
|
|
50
|
-
def call
|
51
|
+
def call
|
51
52
|
@location = config.location
|
52
|
-
@
|
53
|
-
@
|
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 '#{
|
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(
|
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!(
|
74
|
-
container_spec = spec["containers"].detect { _1["name"] ==
|
70
|
+
spec = cp.fetch_workload!(workload_to_clone).fetch("spec")
|
71
|
+
container_spec = spec["containers"].detect { _1["name"] == workload_to_clone } || spec["containers"].first
|
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" =>
|
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
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
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
|
-
|
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:
|
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?
|
data/lib/core/controlplane.rb
CHANGED
@@ -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"].
|
268
|
+
workloads.any? { |workload| route["workloadLink"].match?(%r{/org/#{org}/gvc/#{gvc}/workload/#{workload}}) }
|
268
269
|
end
|
269
270
|
end
|
270
271
|
|
271
|
-
def
|
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"].
|
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
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
|
-
|
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.
|
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-
|
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
|