cpl 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +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
|
[![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
|
-
|
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
|