cpl 1.0.1 → 1.0.3
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/CHANGELOG.md +32 -3
- data/CONTRIBUTING.md +28 -9
- data/Gemfile.lock +2 -2
- data/README.md +44 -109
- data/bin/cpl +1 -1
- data/cpl +4 -13
- data/docs/commands.md +17 -6
- data/docs/migrating.md +262 -0
- data/examples/circleci.yml +1 -2
- data/lib/command/cleanup_old_images.rb +26 -15
- data/lib/command/info.rb +2 -6
- data/lib/command/ps_start.rb +1 -3
- data/lib/command/ps_stop.rb +3 -5
- data/lib/command/ps_wait.rb +35 -0
- data/lib/command/run.rb +2 -2
- data/lib/command/run_cleanup.rb +27 -6
- data/lib/command/run_detached.rb +15 -10
- data/lib/command/test.rb +3 -7
- data/lib/core/config.rb +4 -0
- data/lib/core/controlplane.rb +32 -7
- data/lib/core/controlplane_api.rb +3 -3
- data/lib/cpl/version.rb +1 -1
- data/lib/cpl.rb +1 -1
- metadata +4 -3
- data/lib/main.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d187c354ac6a9a75c0aea498f8fbc060f741809d075439ee22663fdf06f265d
|
4
|
+
data.tar.gz: ffde2f0f3be4663ba85baadf599c6a12882c2ba11734f9220d26366bacc5368d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70941d48b8ccc5bb3fb46b6c96c91fd0d3c2f13f2a702822ee6541231d90957df5a3b3c9d60d2091afad9f6e76be8870953f69085a97315c2a865d534d0c1ce7
|
7
|
+
data.tar.gz: 3a109996c812ec2b2e9dc76c189bd5d3518835e5b221ff8f12416982f3542f1753dcba441dbb3ec32224e807d0c83db54fe78e21cad43da0d829b8d65593a6d5
|
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,38 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
|
3
|
+
All notable changes to this project's source code will be documented in this file. Items under `Unreleased` are upcoming features that will be out in the next version.
|
4
4
|
|
5
|
-
|
5
|
+
## Contributors
|
6
6
|
|
7
|
-
|
7
|
+
Please follow the recommendations outlined at [keepachangelog.com](https://keepachangelog.com). Please use the existing headings and styling as a guide, and add a link for the version diff at the bottom of the file. Also, please update the `Unreleased` link to compare it to the latest release version.
|
8
|
+
|
9
|
+
## Versions
|
10
|
+
|
11
|
+
## [Unreleased]
|
12
|
+
|
13
|
+
Changes since the last non-beta release.
|
14
|
+
|
15
|
+
_Please add entries here for your pull requests that are not yet released._
|
16
|
+
|
17
|
+
### Added
|
18
|
+
|
19
|
+
- Added steps to migrate to docs. [PR 57](https://github.com/shakacode/heroku-to-control-plane/pull/57) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
|
20
|
+
- Added `ps:wait` command. [PR 58](https://github.com/shakacode/heroku-to-control-plane/pull/58) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
|
21
|
+
|
22
|
+
## [1.0.1] - 2023-06-28
|
23
|
+
|
24
|
+
### Fixed
|
25
|
+
|
26
|
+
- Fixed `cleanup-stale-apps` command when app does not have image. [PR 55](https://github.com/shakacode/heroku-to-control-plane/pull/55) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
|
27
|
+
|
28
|
+
### Changed
|
29
|
+
|
30
|
+
- Improved docs. [PR 50](https://github.com/shakacode/heroku-to-control-plane/pull/50) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
|
31
|
+
|
32
|
+
## [1.0.0] - 2023-05-29
|
8
33
|
|
9
34
|
- Initial release
|
35
|
+
|
36
|
+
[Unreleased]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.0.1...HEAD
|
37
|
+
[1.0.1]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.0.0...v1.0.1
|
38
|
+
[1.0.0]: https://github.com/shakacode/heroku-to-control-plane/releases/tag/v1.0.0
|
data/CONTRIBUTING.md
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
# Contributing
|
2
2
|
|
3
3
|
## Installation
|
4
|
-
|
4
|
+
|
5
|
+
Rather than installing `cpl` as a Ruby gem, install this repo locally and alias the `cpl` command globally for easier
|
6
|
+
access, e.g.:
|
5
7
|
|
6
8
|
```sh
|
7
9
|
git clone https://github.com/shakacode/heroku-to-control-plane
|
8
10
|
|
9
11
|
# Create an alias in some local shell startup script, e.g., `.profile`, `.bashrc`, etc.
|
10
|
-
alias cpl="~/projects/heroku-to-control-plane/cpl"
|
12
|
+
alias cpl="~/projects/heroku-to-control-plane/bin/cpl"
|
11
13
|
```
|
12
14
|
|
13
15
|
Or set the path of the Ruby gem in your Gemfile.
|
@@ -16,13 +18,30 @@ Or set the path of the Ruby gem in your Gemfile.
|
|
16
18
|
gem 'cpl', path: '~/projects/heroku-to-control-plane'
|
17
19
|
```
|
18
20
|
|
19
|
-
## Linting
|
20
|
-
|
21
|
+
## Linting/Testing
|
22
|
+
|
23
|
+
Before committing or pushing code, be sure to:
|
24
|
+
|
25
|
+
- Run `bundle exec rake update_command_docs` to sync any doc changes made in the source code to the docs
|
26
|
+
- Run `bundle exec rubocop -a` to fix any linting errors
|
27
|
+
- Run `bundle exec rspec` to run the test suite
|
28
|
+
|
29
|
+
You can also install [overcommit](https://github.com/sds/overcommit) and let it automatically check for you:
|
30
|
+
|
31
|
+
```sh
|
32
|
+
gem install overcommit
|
33
|
+
|
34
|
+
overcommit --install
|
35
|
+
```
|
21
36
|
|
22
37
|
## Debugging
|
23
38
|
|
24
|
-
1.
|
25
|
-
2.
|
26
|
-
|
27
|
-
|
28
|
-
|
39
|
+
1. Add a breakpoint (`debugger`) to any line of code you want to debug.
|
40
|
+
2. Modify the `lib/command/test.rb` file to trigger the code you want to test. To simulate a command, you can use
|
41
|
+
`Cpl::Cli.start` (e.g., `Cpl::Cli.start(["deploy-image", "-a", "my-app-name"])` would be the same as running
|
42
|
+
`cpl deploy-image -a my-app-name`).
|
43
|
+
3. Run the `test` command in your test app with a `.controlplane` directory.
|
44
|
+
|
45
|
+
```sh
|
46
|
+
cpl test
|
47
|
+
```
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cpl (1.0.
|
4
|
+
cpl (1.0.3)
|
5
5
|
debug (~> 1.7.1)
|
6
6
|
dotenv (~> 2.8.1)
|
7
7
|
psych (~> 5.1.0)
|
@@ -25,7 +25,7 @@ GEM
|
|
25
25
|
hashdiff (1.0.1)
|
26
26
|
iniparse (1.5.0)
|
27
27
|
io-console (0.6.0)
|
28
|
-
irb (1.7.
|
28
|
+
irb (1.7.1)
|
29
29
|
reline (>= 0.3.0)
|
30
30
|
json (2.6.3)
|
31
31
|
overcommit (0.60.0)
|
data/README.md
CHANGED
@@ -28,10 +28,8 @@ a **helper CLI** based on templates to save lots of day-to-day typing (and human
|
|
28
28
|
1. [Key Features](#key-features)
|
29
29
|
2. [Concept Mapping](#concept-mapping)
|
30
30
|
3. [Installation](#installation)
|
31
|
-
4. [
|
32
|
-
|
33
|
-
- [Promoting Code Upgrades](#promoting-code-upgrades)
|
34
|
-
5. [Example Project Modifications for Control Plane](#example-project-modifications-for-control-plane)
|
31
|
+
4. [Steps to Migrate](#steps-to-migrate)
|
32
|
+
5. [Config](#config)
|
35
33
|
6. [Environment](#environment)
|
36
34
|
7. [Database](#database)
|
37
35
|
8. [In-memory Databases](#in-memory-databases)
|
@@ -110,104 +108,13 @@ gem install cpl
|
|
110
108
|
**Note:** Do not confuse the `cpl` CLI with the `cpln` CLI. The `cpl` CLI is the Heroku to Control Plane playbook CLI.
|
111
109
|
The `cpln` CLI is the Control Plane CLI.
|
112
110
|
|
113
|
-
##
|
111
|
+
## Steps to Migrate
|
114
112
|
|
115
|
-
|
113
|
+
Click [here](/docs/migrating.md) to see the steps to migrate.
|
116
114
|
|
117
|
-
|
118
|
-
[this `controlplane.yml` file](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/controlplane.yml).
|
119
|
-
- Other files in the `.controlplane/templates/` directory are used by the `cpl setup-app` and `cpl apply-template`
|
120
|
-
commands.
|
115
|
+
## Config
|
121
116
|
|
122
|
-
|
123
|
-
|
124
|
-
For each Git project that you want to deploy to Control Plane, copy project-specific configs to a `.controlplane/`
|
125
|
-
directory at the top of your project. `cpl` will pick those up depending on which project folder tree it runs. Thus,
|
126
|
-
this automates running several projects with different configs without explicitly switching configs.
|
127
|
-
|
128
|
-
Before the initial setup, add the templates for the app to `.controlplane/controlplane.yml`, using the `setup` key, e.g.:
|
129
|
-
|
130
|
-
```yaml
|
131
|
-
my-app:
|
132
|
-
setup:
|
133
|
-
- gvc
|
134
|
-
- postgres
|
135
|
-
- redis
|
136
|
-
- memcached
|
137
|
-
- rails
|
138
|
-
- sidekiq
|
139
|
-
```
|
140
|
-
|
141
|
-
Note how the templates correspond to files in the `.controlplane/templates/` directory.
|
142
|
-
|
143
|
-
Then create a `Dockerfile` for your deployment. See
|
144
|
-
[this example](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/Dockerfile).
|
145
|
-
|
146
|
-
```sh
|
147
|
-
# Provision infrastructure (one-time-only for new apps) using templates.
|
148
|
-
cpl setup-app -a my-app
|
149
|
-
|
150
|
-
# Build and push image with auto-tagging, e.g., "my-app:1_456".
|
151
|
-
cpl build-image -a my-app --commit 456
|
152
|
-
|
153
|
-
# Prepare database.
|
154
|
-
cpl run:detached -a my-app --image latest -- rails db:prepare
|
155
|
-
|
156
|
-
# Deploy latest image.
|
157
|
-
cpl deploy-image -a my-app
|
158
|
-
|
159
|
-
# Open app in browser.
|
160
|
-
cpl open -a my-app
|
161
|
-
```
|
162
|
-
|
163
|
-
### Promoting Code Upgrades
|
164
|
-
|
165
|
-
```sh
|
166
|
-
# Build and push new image with sequential tagging, e.g., "my-app:2".
|
167
|
-
cpl build-image -a my-app
|
168
|
-
|
169
|
-
# Or build and push new image with sequential tagging and commit SHA, e.g., "my-app:2_ABC".
|
170
|
-
cpl build-image -a my-app --commit ABC
|
171
|
-
|
172
|
-
# Run database migrations (or other release tasks) with latest image, while app is still running on previous image.
|
173
|
-
# This is analogous to the release phase.
|
174
|
-
cpl run:detached -a my-app --image latest -- rails db:migrate
|
175
|
-
|
176
|
-
# Deploy latest image.
|
177
|
-
cpl deploy-image -a my-app
|
178
|
-
```
|
179
|
-
|
180
|
-
## Example Project Modifications for Control Plane
|
181
|
-
|
182
|
-
_See this for a complete example._
|
183
|
-
|
184
|
-
To learn how to migrate an app, we recommend following along with
|
185
|
-
[this example project](https://github.com/shakacode/react-webpack-rails-tutorial).
|
186
|
-
|
187
|
-
1. Create the `.controlplane/` directory at the top of your project and copy files from the `templates/` directory of
|
188
|
-
this repository to something as follows:
|
189
|
-
|
190
|
-
```sh
|
191
|
-
app_main_folder/
|
192
|
-
.controlplane/
|
193
|
-
Dockerfile # Your app's Dockerfile, with some Control Plane changes.
|
194
|
-
controlplane.yml
|
195
|
-
entrypoint.sh # App-specific - edit as needed.
|
196
|
-
templates/
|
197
|
-
gvc.yml
|
198
|
-
memcached.yml
|
199
|
-
postgres.yml
|
200
|
-
rails.yml
|
201
|
-
redis.yml
|
202
|
-
sidekiq.yml
|
203
|
-
```
|
204
|
-
|
205
|
-
The example
|
206
|
-
[`.controlplane/` directory](https://github.com/shakacode/react-webpack-rails-tutorial/tree/master/.controlplane)
|
207
|
-
already contains these files.
|
208
|
-
|
209
|
-
2. Edit your `controlplane.yml` file as needed. For example, see
|
210
|
-
[this `controlplane.yml` file](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/controlplane.yml).
|
117
|
+
Here's a complete example of all supported config keys explained:
|
211
118
|
|
212
119
|
```yaml
|
213
120
|
# Keys beginning with "cpln_" correspond to your settings in Control Plane.
|
@@ -221,6 +128,16 @@ aliases:
|
|
221
128
|
# Example apps use only one location. Control Plane offers the ability to use multiple locations.
|
222
129
|
default_location: aws-us-east-2
|
223
130
|
|
131
|
+
# Allows running the command `cpl setup-app`
|
132
|
+
# instead of `cpl apply-template gvc redis postgres memcached rails sidekiq`.
|
133
|
+
setup:
|
134
|
+
- gvc
|
135
|
+
- redis
|
136
|
+
- postgres
|
137
|
+
- memcached
|
138
|
+
- rails
|
139
|
+
- sidekiq
|
140
|
+
|
224
141
|
# Configure the workload name used as a template for one-off scripts, like a Heroku one-off dyno.
|
225
142
|
one_off_workload: rails
|
226
143
|
|
@@ -235,40 +152,58 @@ aliases:
|
|
235
152
|
- postgres
|
236
153
|
- memcached
|
237
154
|
|
238
|
-
# Configure the workload name used when maintenance mode is on (defaults to "maintenance")
|
155
|
+
# Configure the workload name used when maintenance mode is on (defaults to "maintenance").
|
239
156
|
maintenance_workload: maintenance
|
240
157
|
|
158
|
+
# Fixes the remote terminal size to match the local terminal size
|
159
|
+
# when running the commands `cpl run` or `cpl run:detached`.
|
160
|
+
fix_terminal_size: true
|
161
|
+
|
162
|
+
# Apps with a deployed image created before this amount of days will be listed for deletion
|
163
|
+
# when running the command `cpl cleanup-stale-apps`.
|
164
|
+
stale_app_image_deployed_days: 5
|
165
|
+
|
166
|
+
# Images created before this amount of days will be listed for deletion
|
167
|
+
# when running the command `cpl cleanup-old-images`.
|
168
|
+
old_image_retention_days: 5
|
169
|
+
|
170
|
+
# Run workloads created before this amount of days will be listed for deletion
|
171
|
+
# when running the command `cpl run:cleanup`.
|
172
|
+
stale_run_workload_created_days: 2
|
173
|
+
|
241
174
|
apps:
|
242
175
|
my-app-staging:
|
243
176
|
# Use the values from the common section above.
|
244
177
|
<<: *common
|
178
|
+
|
245
179
|
my-app-review:
|
246
180
|
<<: *common
|
181
|
+
|
247
182
|
# If `match_if_app_name_starts_with` is `true`, then use this config for app names starting with this name,
|
248
183
|
# e.g., "my-app-review-pr123", "my-app-review-anything-goes", etc.
|
249
184
|
match_if_app_name_starts_with: true
|
185
|
+
|
250
186
|
my-app-production:
|
251
187
|
<<: *common
|
188
|
+
|
252
189
|
# Use a different organization for production.
|
253
190
|
cpln_org: my-org-production
|
191
|
+
|
254
192
|
# Allows running the command `cpl promote-app-from-upstream -a my-app-production`
|
255
193
|
# to promote the staging app to production.
|
256
194
|
upstream: my-app-staging
|
195
|
+
|
196
|
+
# Used by the command `cpl promote-app-from-upstream` to run a release script before deploying.
|
197
|
+
# This is relative to the `.controlplane/` directory.
|
198
|
+
release_script: release_script
|
199
|
+
|
257
200
|
my-app-other:
|
258
201
|
<<: *common
|
202
|
+
|
259
203
|
# You can specify a different `Dockerfile` relative to the `.controlplane/` directory (defaults to "Dockerfile").
|
260
204
|
dockerfile: ../some_other/Dockerfile
|
261
205
|
```
|
262
206
|
|
263
|
-
3. We recommend that you try out the commands listed in
|
264
|
-
[the example](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/readme.md).
|
265
|
-
These steps will guide you to the following:
|
266
|
-
|
267
|
-
1. Provision the GVC and workloads.
|
268
|
-
2. Build the Docker image.
|
269
|
-
3. Run Rails migrations, like in the Heroku release phase.
|
270
|
-
4. Promote the latest Docker image.
|
271
|
-
|
272
207
|
## Environment
|
273
208
|
|
274
209
|
There are two main places where we can set up environment variables in Control Plane:
|
data/bin/cpl
CHANGED
data/cpl
CHANGED
@@ -1,15 +1,6 @@
|
|
1
|
-
#!/bin/
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
|
4
|
+
require_relative "lib/cpl"
|
4
5
|
|
5
|
-
|
6
|
-
#
|
7
|
-
# Example .env:
|
8
|
-
# CPLN_TOKEN=xxx
|
9
|
-
#
|
10
|
-
if [ -f "$SCRIPT_DIR/.env" ]; then
|
11
|
-
export $(grep -v '^#' $SCRIPT_DIR/.env | xargs -0)
|
12
|
-
fi
|
13
|
-
|
14
|
-
# exec $SCRIPT_DIR/old_commands/main.sh "$@"
|
15
|
-
exec ruby $SCRIPT_DIR/lib/main.rb "$@"
|
6
|
+
Cpl::Cli.start
|
data/docs/commands.md
CHANGED
@@ -285,6 +285,18 @@ cpl ps:stop -a $APP_NAME
|
|
285
285
|
cpl ps:stop -a $APP_NAME -w $WORKLOAD_NAME
|
286
286
|
```
|
287
287
|
|
288
|
+
### `ps:wait`
|
289
|
+
|
290
|
+
- Waits for workloads in app to be ready after re-deployment
|
291
|
+
|
292
|
+
```sh
|
293
|
+
# Waits for all workloads in app.
|
294
|
+
cpl ps:wait -a $APP_NAME
|
295
|
+
|
296
|
+
# Waits for a specific workload in app.
|
297
|
+
cpl ps:swait -a $APP_NAME -w $WORKLOAD_NAME
|
298
|
+
```
|
299
|
+
|
288
300
|
### `run`
|
289
301
|
|
290
302
|
- Runs one-off **_interactive_** replicas (analog of `heroku run`)
|
@@ -329,6 +341,7 @@ cpl run bash -a $APP_NAME --use-local-token
|
|
329
341
|
- Deletes stale run workloads for an app
|
330
342
|
- Workloads are considered stale based on how many days since created
|
331
343
|
- `stale_run_workload_created_days` in the `.controlplane/controlplane.yml` file specifies the number of days after created that the workload is considered stale
|
344
|
+
- Works for both interactive workloads (created with `cpl run`) and non-interactive workloads (created with `cpl run:detached`)
|
332
345
|
- Will ask for explicit user confirmation of deletion
|
333
346
|
|
334
347
|
```sh
|
@@ -352,18 +365,16 @@ cpl run:detached 'LOG_LEVEL=warn rails db:migrate' -a $APP_NAME
|
|
352
365
|
# COMMAND may also be passed at the end (in this case, no need to quote).
|
353
366
|
cpl run:detached -a $APP_NAME -- rails db:migrate
|
354
367
|
|
355
|
-
# Uses some other image.
|
356
|
-
cpl run:detached rails db:migrate -a $APP_NAME --image /some/full/image/path
|
357
|
-
|
358
|
-
# Uses latest app image (which may not be promoted yet).
|
359
|
-
cpl run:detached rails db:migrate -a $APP_NAME --image latest
|
360
|
-
|
361
368
|
# Uses a different image (which may not be promoted yet).
|
362
369
|
cpl run:detached rails db:migrate -a $APP_NAME --image appimage:123 # Exact image name
|
363
370
|
cpl run:detached rails db:migrate -a $APP_NAME --image latest # Latest sequential image
|
364
371
|
|
365
372
|
# Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
|
366
373
|
cpl run:detached rails db:migrate:status -a $APP_NAME -w other-workload
|
374
|
+
|
375
|
+
# Overrides remote CPLN_TOKEN env variable with local token.
|
376
|
+
# Useful when superuser rights are needed in remote container.
|
377
|
+
cpl run:detached rails db:migrate:status -a $APP_NAME --use-local-token
|
367
378
|
```
|
368
379
|
|
369
380
|
### `setup-app`
|
data/docs/migrating.md
ADDED
@@ -0,0 +1,262 @@
|
|
1
|
+
# Steps to Migrate from Heroku to Control Plane
|
2
|
+
|
3
|
+
We recommend following along with
|
4
|
+
[this example project](https://github.com/shakacode/react-webpack-rails-tutorial).
|
5
|
+
|
6
|
+
1. [Clone the Staging Environment](#clone-the-staging-environment)
|
7
|
+
- [Review Special Gems](#review-special-gems)
|
8
|
+
- [Create a Minimum Bootable Config](#create-a-minimum-bootable-config)
|
9
|
+
2. [Create the Review App Process](#create-the-review-app-process)
|
10
|
+
- [Database for Review Apps](#database-for-review-apps)
|
11
|
+
- [Redis and Memcached for Review Apps](#redis-and-memcached-for-review-apps)
|
12
|
+
3. [Deploy to Production](#deploy-to-production)
|
13
|
+
|
14
|
+
## Clone the Staging Environment
|
15
|
+
|
16
|
+
By cloning the staging environment on Heroku, you can speed up the initial provisioning of the app on Control Plane
|
17
|
+
without compromising your current environment.
|
18
|
+
|
19
|
+
Consider migrating just the web dyno first, and get other types of dynos working afterward. You can also move the
|
20
|
+
add-ons to Control Plane later once the app works as expected.
|
21
|
+
|
22
|
+
First, create a new Heroku app with all the add-ons, copying the data from the current staging app.
|
23
|
+
|
24
|
+
Then, copy project-specific configs to a `.controlplane/` directory at the top of your project. `cpl` will pick those up
|
25
|
+
depending on which project folder tree it runs. Thus, this automates running several projects with different configs
|
26
|
+
without explicitly switching configs.
|
27
|
+
|
28
|
+
Edit the `.controlplane/controlplane.yml` file as needed. Note that the `my-app-staging` name used in the examples below
|
29
|
+
is defined in this file. See
|
30
|
+
[this example](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/controlplane.yml).
|
31
|
+
|
32
|
+
Before the initial setup, add the templates for the app to the `.controlplane/controlplane.yml` file, using the `setup`
|
33
|
+
key, e.g.:
|
34
|
+
|
35
|
+
```yaml
|
36
|
+
my-app-staging:
|
37
|
+
<<: *common
|
38
|
+
setup:
|
39
|
+
- gvc
|
40
|
+
- redis
|
41
|
+
- memcached
|
42
|
+
- rails
|
43
|
+
- sidekiq
|
44
|
+
```
|
45
|
+
|
46
|
+
Note how the templates correspond to files in the `.controlplane/templates/` directory. These files will be used by the
|
47
|
+
`cpl setup-app` and `cpl apply-template` commands.
|
48
|
+
|
49
|
+
Ensure that env vars point to the Heroku add-ons in the template for the app (`.controlplane/templates/gvc.yml`). See
|
50
|
+
[this example](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/templates/gvc.yml).
|
51
|
+
|
52
|
+
After that, create a Dockerfile in `.controlplane/Dockerfile` for your deployment. See
|
53
|
+
[this example](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/Dockerfile).
|
54
|
+
|
55
|
+
You should have a folder structure similar to the following:
|
56
|
+
|
57
|
+
```sh
|
58
|
+
app_main_folder/
|
59
|
+
.controlplane/
|
60
|
+
Dockerfile # Your app's Dockerfile, with some Control Plane changes.
|
61
|
+
controlplane.yml
|
62
|
+
entrypoint.sh # App-specific - edit as needed.
|
63
|
+
templates/
|
64
|
+
gvc.yml
|
65
|
+
memcached.yml
|
66
|
+
rails.yml
|
67
|
+
redis.yml
|
68
|
+
sidekiq.yml
|
69
|
+
```
|
70
|
+
|
71
|
+
The example
|
72
|
+
[`.controlplane/` directory](https://github.com/shakacode/react-webpack-rails-tutorial/tree/master/.controlplane)
|
73
|
+
already contains these files.
|
74
|
+
|
75
|
+
Finally, check the app for any Heroku-specific code and update it, such as the `HEROKU_SLUG_COMMIT` env var and other
|
76
|
+
env vars beginning with `HEROKU_`. You should add some logic to check for the Control Plane equivalents - it might be
|
77
|
+
worth adding a `CONTROLPLANE` env var to act as a feature flag and help run different code for Heroku and Control Plane
|
78
|
+
until the migration is complete.
|
79
|
+
|
80
|
+
You might want to [review special gems](#review-special-gems) and
|
81
|
+
[create a minimum bootable config](#create-a-minimum-bootable-config).
|
82
|
+
|
83
|
+
At first, do the deployments from the command line. Then set up CI scripts to trigger the deployment upon merges to
|
84
|
+
master/main.
|
85
|
+
|
86
|
+
Use these commands for the initial setup and deployment:
|
87
|
+
|
88
|
+
```sh
|
89
|
+
# Provision infrastructure (one-time-only for new apps) using templates.
|
90
|
+
cpl setup-app -a my-app-staging
|
91
|
+
|
92
|
+
# Build and push image with auto-tagging, e.g., "my-app-staging:1_456".
|
93
|
+
cpl build-image -a my-app-staging --commit 456
|
94
|
+
|
95
|
+
# Prepare database.
|
96
|
+
cpl run:detached -a my-app-staging --image latest -- rails db:prepare
|
97
|
+
|
98
|
+
# Deploy latest image.
|
99
|
+
cpl deploy-image -a my-app-staging
|
100
|
+
|
101
|
+
# Open app in browser.
|
102
|
+
cpl open -a my-app-staging
|
103
|
+
```
|
104
|
+
|
105
|
+
Then for promoting code upgrades:
|
106
|
+
|
107
|
+
```sh
|
108
|
+
# Build and push new image with sequential tagging, e.g., "my-app-staging:2".
|
109
|
+
cpl build-image -a my-app-staging
|
110
|
+
|
111
|
+
# Or build and push new image with sequential tagging and commit SHA, e.g., "my-app-staging:2_ABC".
|
112
|
+
cpl build-image -a my-app-staging --commit ABC
|
113
|
+
|
114
|
+
# Run database migrations (or other release tasks) with latest image, while app is still running on previous image.
|
115
|
+
# This is analogous to the release phase.
|
116
|
+
cpl run:detached -a my-app-staging --image latest -- rails db:migrate
|
117
|
+
|
118
|
+
# Deploy latest image.
|
119
|
+
cpl deploy-image -a my-app-staging
|
120
|
+
```
|
121
|
+
|
122
|
+
### Review Special Gems
|
123
|
+
|
124
|
+
Make sure to review "special" gems which might be related to Heroku, e.g.:
|
125
|
+
|
126
|
+
- `rails_autoscale_agent`. It's specific to Heroku, so it must be removed.
|
127
|
+
- `puma_worker_killer`. In general, it's unnecessary on Control Plane, as Kubernetes containers will restart on their
|
128
|
+
own logic and may not restart at all if everything is ok.
|
129
|
+
- `rack-timeout`. It could possibly be replaced with Control Plane's `timeout` option.
|
130
|
+
|
131
|
+
You can use the `CONTROLPLANE` env var to separate the gems, e.g.:
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
# Gemfile
|
135
|
+
group :staging, :production do
|
136
|
+
gem "rack-timeout"
|
137
|
+
|
138
|
+
unless ENV.key?("CONTROLPLANE")
|
139
|
+
gem "rails_autoscale_agent"
|
140
|
+
gem "puma_worker_killer"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
```
|
144
|
+
|
145
|
+
### Create a Minimum Bootable Config
|
146
|
+
|
147
|
+
You can try to create a minimum bootable config to migrate parts of your app gradually. To do that, follow these steps:
|
148
|
+
|
149
|
+
1. Rename the existing `application.yml` file to some other name (e.g., `application.old.yml`)
|
150
|
+
2. Create a new **minimal** `application.yml` file, e.g.:
|
151
|
+
|
152
|
+
```yaml
|
153
|
+
SECRET_KEY_BASE: "123"
|
154
|
+
# This should be enabled for `rails s`, not `rails assets:precompile`.
|
155
|
+
# DATABASE_URL: postgres://localhost:5432/dbname
|
156
|
+
# RAILS_SERVE_STATIC_FILES: "true"
|
157
|
+
|
158
|
+
# You will add whatever env vars are required here later.
|
159
|
+
```
|
160
|
+
|
161
|
+
3. Try running `RAILS_ENV=production CONTROLPLANE=true rails assets:precompile`
|
162
|
+
(theoretically, this should work without any additional env vars)
|
163
|
+
4. Fix whatever code needs to be fixed and add missing env vars
|
164
|
+
(the fewer env vars are needed, the cleaner the `Dockerfile` will be)
|
165
|
+
5. Enable `DATABASE_URL` and `RAILS_SERVE_STATIC_FILES` env vars
|
166
|
+
6. Try running `RAILS_ENV=production CONTROLPLANE=true rails s`
|
167
|
+
7. Fix whatever code needs to be fixed and add required env vars to `application.yml`
|
168
|
+
8. Try running your **production** entrypoint command, e.g.,
|
169
|
+
`RAILS_ENV=production RACK_ENV=production CONTROLPLANE=true puma -C config/puma.rb`
|
170
|
+
9. Fix whatever code needs to be fixed and add required env vars to `application.yml`
|
171
|
+
|
172
|
+
Now you should have a minimal bootable config.
|
173
|
+
|
174
|
+
Then you can temporarily set the `LOG_LEVEL=debug` env var and disable unnecessary services to help with the process,
|
175
|
+
e.g.:
|
176
|
+
|
177
|
+
```yaml
|
178
|
+
DISABLE_SPRING: "true"
|
179
|
+
SCOUT_MONITOR: "false"
|
180
|
+
RACK_TIMEOUT_SERVICE_TIMEOUT: "0"
|
181
|
+
```
|
182
|
+
|
183
|
+
## Create the Review App Process
|
184
|
+
|
185
|
+
Add an entry for review apps to the `.controlplane/controlplane.yml` file. By adding a `match_if_app_name_starts_with`
|
186
|
+
key with the value `true`, any app that starts with the entry's name will use this config. Doing this allows you to
|
187
|
+
configure an entry for, e.g., `my-app-review`, and then create review apps starting with that name (e.g.,
|
188
|
+
`my-app-review-1234`, `my-app-review-5678`, etc.). Here's an example:
|
189
|
+
|
190
|
+
```yaml
|
191
|
+
my-app-review:
|
192
|
+
<<: *common
|
193
|
+
match_if_app_name_starts_with: true
|
194
|
+
setup:
|
195
|
+
- gvc
|
196
|
+
- redis
|
197
|
+
- memcached
|
198
|
+
- rails
|
199
|
+
- sidekiq
|
200
|
+
```
|
201
|
+
|
202
|
+
In your CI scripts, you can create a review app using some identifier (e.g., the number of the PR on GitHub).
|
203
|
+
|
204
|
+
```yaml
|
205
|
+
# On CircleCI, you can use `echo $CIRCLE_PULL_REQUEST | grep -Eo '[0-9]+$'` to extract the number of the PR.
|
206
|
+
PR_NUM=$(... extract the number of the PR here ...)
|
207
|
+
echo "export APP_NAME=my-app-review-$PR_NUM" >> $BASH_ENV
|
208
|
+
|
209
|
+
# Only create the app if it doesn't exist yet, as we may have multiple triggers for the review app
|
210
|
+
# (such as when a PR gets updated).
|
211
|
+
if ! cpl exists -a ${APP_NAME}; then
|
212
|
+
cpl setup-app -a ${APP_NAME}
|
213
|
+
echo "export NEW_APP=true" >> $BASH_ENV
|
214
|
+
fi
|
215
|
+
|
216
|
+
# The `NEW_APP` env var that we exported above can be used to either reset or migrate the database before deploying.
|
217
|
+
if [ -n "${NEW_APP}" ]; then
|
218
|
+
cpl run:detached 'LOG_LEVEL=warn rails db:reset' -a ${APP_NAME} --image latest
|
219
|
+
else
|
220
|
+
cpl run:detached 'LOG_LEVEL=warn rails db:migrate_and_wait_replica' -a ${APP_NAME} --image latest
|
221
|
+
fi
|
222
|
+
```
|
223
|
+
|
224
|
+
Then follow the same steps for the initial deployment or code upgrades.
|
225
|
+
|
226
|
+
### Database for Review Apps
|
227
|
+
|
228
|
+
For the review app resources, these should be handled as env vars in the template for the app
|
229
|
+
(`.controlplane/templates/gvc.yml`), .e.g.:
|
230
|
+
|
231
|
+
```yaml
|
232
|
+
- name: DATABASE_URL
|
233
|
+
value: postgres://postgres:XXXXXXXX@cpln-XXXX-staging.XXXXXX.us-east-1.rds.amazonaws.com:5432/APP_GVC
|
234
|
+
```
|
235
|
+
|
236
|
+
Notice that `APP_GVC` is the app name, which is used as the database name on RDS, so that each review app gets its own
|
237
|
+
database on the one RDS instance used for all review apps, which would be, e.g., `my-app-review-1234`.
|
238
|
+
|
239
|
+
### Redis and Memcached for Review Apps
|
240
|
+
|
241
|
+
So long as no persistence is needed for Redis and Memcached, we have templates for workloads that should be sufficient
|
242
|
+
for review apps in the `templates/` directory of this repository. Using these templates results in considerable cost
|
243
|
+
savings compared to paying for the resources on Heroku.
|
244
|
+
|
245
|
+
```yaml
|
246
|
+
- name: MEMCACHE_SERVERS
|
247
|
+
value: memcached.APP_GVC.cpln.local
|
248
|
+
- name: REDIS_URL
|
249
|
+
value: redis://redis.APP_GVC.cpln.local:6379
|
250
|
+
```
|
251
|
+
|
252
|
+
## Deploy to Production
|
253
|
+
|
254
|
+
Only try deploying to production once staging and review apps are working well.
|
255
|
+
|
256
|
+
For simplicity, keep add-ons running on Heroku initially. You could move over the database to RDS first. However, it's a
|
257
|
+
bit simpler to isolate any differences in cost and performance by first moving over your compute to Control Plane.
|
258
|
+
|
259
|
+
Ensure that your Control Plane compute is in the AWS region `US-EAST-1`; otherwise, you'll have noticeable extra latency
|
260
|
+
with your calls to resources. You might also have egress charges from Control Plane.
|
261
|
+
|
262
|
+
Use the `cpl promote-app-from-upstream` command to promote the staging app to production.
|
data/examples/circleci.yml
CHANGED
@@ -59,8 +59,7 @@ build-review-app:
|
|
59
59
|
cpln profile create default --token ${CPLN_TOKEN} --org ${CPLN_ORG} --gvc ${APP_NAME}
|
60
60
|
cpln image docker-login
|
61
61
|
|
62
|
-
|
63
|
-
sudo ln -s ~/heroku-to-control-plane/cpl /usr/local/bin/cpl
|
62
|
+
gem install cpl
|
64
63
|
- run:
|
65
64
|
name: Provision review app if needed
|
66
65
|
command: |
|
@@ -31,6 +31,18 @@ module Command
|
|
31
31
|
|
32
32
|
private
|
33
33
|
|
34
|
+
def app_prefix
|
35
|
+
config.should_app_start_with?(config.app) ? "#{config.app}-" : "#{config.app}:"
|
36
|
+
end
|
37
|
+
|
38
|
+
def remove_deployed_image(app, app_images)
|
39
|
+
return app_images unless cp.fetch_gvc(app)
|
40
|
+
|
41
|
+
# If app exists, remove latest image, because we don't want to delete the image that is currently deployed
|
42
|
+
latest_image_name = latest_image_from(app_images, app_name: app)
|
43
|
+
app_images.filter { |item| item["name"] != latest_image_name }
|
44
|
+
end
|
45
|
+
|
34
46
|
def old_images # rubocop:disable Metrics/MethodLength
|
35
47
|
@old_images ||=
|
36
48
|
begin
|
@@ -39,21 +51,20 @@ module Command
|
|
39
51
|
now = DateTime.now
|
40
52
|
old_image_retention_days = config[:old_image_retention_days]
|
41
53
|
|
42
|
-
images = cp.image_query["items"].filter { |item| item["name"].start_with?(
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
})
|
54
|
+
images = cp.image_query["items"].filter { |item| item["name"].start_with?(app_prefix) }
|
55
|
+
images_by_app = images.group_by { |item| item["repository"] }
|
56
|
+
images_by_app.each do |app, app_images|
|
57
|
+
app_images = remove_deployed_image(app, app_images)
|
58
|
+
app_images.each do |image|
|
59
|
+
created_date = DateTime.parse(image["created"])
|
60
|
+
diff_in_days = (now - created_date).to_i
|
61
|
+
next unless diff_in_days >= old_image_retention_days
|
62
|
+
|
63
|
+
result_images.push({
|
64
|
+
name: image["name"],
|
65
|
+
date: created_date
|
66
|
+
})
|
67
|
+
end
|
57
68
|
end
|
58
69
|
|
59
70
|
result_images
|
data/lib/command/info.rb
CHANGED
@@ -112,10 +112,6 @@ module Command
|
|
112
112
|
result.uniq.sort
|
113
113
|
end
|
114
114
|
|
115
|
-
def should_app_start_with?(app)
|
116
|
-
config.apps[app.to_sym]&.dig(:match_if_app_name_starts_with)
|
117
|
-
end
|
118
|
-
|
119
115
|
def any_app_starts_with?(app)
|
120
116
|
@app_workloads.keys.find { |app_name| app_matches?(app_name, app, config.apps[app.to_sym]) }
|
121
117
|
end
|
@@ -132,7 +128,7 @@ module Command
|
|
132
128
|
end
|
133
129
|
|
134
130
|
def add_to_missing_workloads(app, workload)
|
135
|
-
if should_app_start_with?(app)
|
131
|
+
if config.should_app_start_with?(app)
|
136
132
|
@missing_apps_starting_with[app] ||= []
|
137
133
|
@missing_apps_starting_with[app].push(workload)
|
138
134
|
else
|
@@ -142,7 +138,7 @@ module Command
|
|
142
138
|
end
|
143
139
|
|
144
140
|
def print_app(app, org)
|
145
|
-
if should_app_start_with?(app)
|
141
|
+
if config.should_app_start_with?(app)
|
146
142
|
check_any_app_starts_with(app)
|
147
143
|
elsif cp.fetch_gvc(app, org).nil?
|
148
144
|
@missing_apps_workloads[app] = ["gvc"]
|
data/lib/command/ps_start.rb
CHANGED
@@ -42,9 +42,7 @@ module Command
|
|
42
42
|
|
43
43
|
@workloads.reverse_each do |workload|
|
44
44
|
step("Waiting for workload '#{workload}' to be ready", retry_on_failure: true) do
|
45
|
-
cp.
|
46
|
-
item.dig("status", "ready")
|
47
|
-
end
|
45
|
+
cp.workload_deployments_ready?(workload, expected_status: true)
|
48
46
|
end
|
49
47
|
end
|
50
48
|
end
|
data/lib/command/ps_stop.rb
CHANGED
@@ -6,7 +6,7 @@ module Command
|
|
6
6
|
OPTIONS = [
|
7
7
|
app_option(required: true),
|
8
8
|
workload_option,
|
9
|
-
wait_option("workload to be
|
9
|
+
wait_option("workload to not be ready")
|
10
10
|
].freeze
|
11
11
|
DESCRIPTION = "Stops workloads in app"
|
12
12
|
LONG_DESCRIPTION = <<~DESC
|
@@ -41,10 +41,8 @@ module Command
|
|
41
41
|
progress.puts
|
42
42
|
|
43
43
|
@workloads.each do |workload|
|
44
|
-
step("Waiting for workload '#{workload}' to be
|
45
|
-
cp.
|
46
|
-
!item.dig("status", "ready")
|
47
|
-
end
|
44
|
+
step("Waiting for workload '#{workload}' to not be ready", retry_on_failure: true) do
|
45
|
+
cp.workload_deployments_ready?(workload, expected_status: false)
|
48
46
|
end
|
49
47
|
end
|
50
48
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Command
|
4
|
+
class PsWait < Base
|
5
|
+
NAME = "ps:wait"
|
6
|
+
OPTIONS = [
|
7
|
+
app_option(required: true),
|
8
|
+
workload_option
|
9
|
+
].freeze
|
10
|
+
DESCRIPTION = "Waits for workloads in app to be ready after re-deployment"
|
11
|
+
LONG_DESCRIPTION = <<~DESC
|
12
|
+
- Waits for workloads in app to be ready after re-deployment
|
13
|
+
DESC
|
14
|
+
EXAMPLES = <<~EX
|
15
|
+
```sh
|
16
|
+
# Waits for all workloads in app.
|
17
|
+
cpl ps:wait -a $APP_NAME
|
18
|
+
|
19
|
+
# Waits for a specific workload in app.
|
20
|
+
cpl ps:swait -a $APP_NAME -w $WORKLOAD_NAME
|
21
|
+
```
|
22
|
+
EX
|
23
|
+
|
24
|
+
def call
|
25
|
+
@workloads = [config.options[:workload]] if config.options[:workload]
|
26
|
+
@workloads ||= config[:app_workloads] + config[:additional_workloads]
|
27
|
+
|
28
|
+
@workloads.reverse_each do |workload|
|
29
|
+
step("Waiting for workload '#{workload}' to be ready", retry_on_failure: true) do
|
30
|
+
cp.workload_deployments_ready?(workload, expected_status: true)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/command/run.rb
CHANGED
@@ -99,8 +99,8 @@ module Command
|
|
99
99
|
|
100
100
|
# Override image if specified
|
101
101
|
image = config.options[:image]
|
102
|
-
image =
|
103
|
-
container_spec["image"] = image
|
102
|
+
image = latest_image if image == "latest"
|
103
|
+
container_spec["image"] = "/org/#{config.org}/image/#{image}"
|
104
104
|
|
105
105
|
# Set runner
|
106
106
|
container_spec["env"] ||= []
|
data/lib/command/run_cleanup.rb
CHANGED
@@ -12,6 +12,7 @@ module Command
|
|
12
12
|
- Deletes stale run workloads for an app
|
13
13
|
- Workloads are considered stale based on how many days since created
|
14
14
|
- `stale_run_workload_created_days` in the `.controlplane/controlplane.yml` file specifies the number of days after created that the workload is considered stale
|
15
|
+
- Works for both interactive workloads (created with `cpl run`) and non-interactive workloads (created with `cpl run:detached`)
|
15
16
|
- Will ask for explicit user confirmation of deletion
|
16
17
|
DESC
|
17
18
|
|
@@ -20,8 +21,14 @@ module Command
|
|
20
21
|
|
21
22
|
progress.puts("Stale run workloads:")
|
22
23
|
stale_run_workloads.each do |workload|
|
23
|
-
|
24
|
-
|
24
|
+
output = ""
|
25
|
+
output += if config.should_app_start_with?(config.app)
|
26
|
+
" #{workload[:app]} - #{workload[:name]}"
|
27
|
+
else
|
28
|
+
" #{workload[:name]}"
|
29
|
+
end
|
30
|
+
output += " (#{Shell.color("#{workload[:date]} - #{workload[:days]} days ago", :red)})"
|
31
|
+
progress.puts(output)
|
25
32
|
end
|
26
33
|
|
27
34
|
return unless confirm_delete
|
@@ -62,11 +69,19 @@ module Command
|
|
62
69
|
now = DateTime.now
|
63
70
|
stale_run_workload_created_days = config[:stale_run_workload_created_days]
|
64
71
|
|
65
|
-
|
72
|
+
interactive_workloads = cp.query_workloads(
|
73
|
+
"-run-", partial_gvc_match: config.should_app_start_with?(config.app), partial_workload_match: true
|
74
|
+
)["items"]
|
75
|
+
non_interactive_workloads = cp.query_workloads(
|
76
|
+
"-runner-", partial_gvc_match: config.should_app_start_with?(config.app), partial_workload_match: true
|
77
|
+
)["items"]
|
78
|
+
workloads = interactive_workloads + non_interactive_workloads
|
79
|
+
|
66
80
|
workloads.each do |workload|
|
81
|
+
app_name = workload["links"].find { |link| link["rel"] == "gvc" }["href"].split("/").last
|
67
82
|
workload_name = workload["name"]
|
68
83
|
|
69
|
-
original_workload_name, workload_number = workload_name.split(
|
84
|
+
original_workload_name, workload_number = workload_name.split(/-run-|-runner-/)
|
70
85
|
next unless defined_workloads.include?(original_workload_name) && workload_number.match?(/^\d{4}$/)
|
71
86
|
|
72
87
|
created_date = DateTime.parse(workload["created"])
|
@@ -74,6 +89,7 @@ module Command
|
|
74
89
|
next unless diff_in_days >= stale_run_workload_created_days
|
75
90
|
|
76
91
|
run_workloads.push({
|
92
|
+
app: app_name,
|
77
93
|
name: workload_name,
|
78
94
|
date: created_date,
|
79
95
|
days: diff_in_days
|
@@ -91,8 +107,13 @@ module Command
|
|
91
107
|
end
|
92
108
|
|
93
109
|
def delete_workload(workload)
|
94
|
-
|
95
|
-
|
110
|
+
message = if config.should_app_start_with?(config.app)
|
111
|
+
"Deleting run workload '#{workload[:app]} - #{workload[:name]}'"
|
112
|
+
else
|
113
|
+
"Deleting run workload '#{workload[:name]}'"
|
114
|
+
end
|
115
|
+
step(message) do
|
116
|
+
cp.delete_workload(workload[:name], workload[:app])
|
96
117
|
end
|
97
118
|
end
|
98
119
|
end
|
data/lib/command/run_detached.rb
CHANGED
@@ -8,7 +8,8 @@ module Command
|
|
8
8
|
OPTIONS = [
|
9
9
|
app_option(required: true),
|
10
10
|
image_option,
|
11
|
-
workload_option
|
11
|
+
workload_option,
|
12
|
+
use_local_token_option
|
12
13
|
].freeze
|
13
14
|
DESCRIPTION = "Runs one-off **_non-interactive_** replicas (close analog of `heroku run:detached`)"
|
14
15
|
LONG_DESCRIPTION = <<~DESC
|
@@ -28,18 +29,16 @@ module Command
|
|
28
29
|
# COMMAND may also be passed at the end (in this case, no need to quote).
|
29
30
|
cpl run:detached -a $APP_NAME -- rails db:migrate
|
30
31
|
|
31
|
-
# Uses some other image.
|
32
|
-
cpl run:detached rails db:migrate -a $APP_NAME --image /some/full/image/path
|
33
|
-
|
34
|
-
# Uses latest app image (which may not be promoted yet).
|
35
|
-
cpl run:detached rails db:migrate -a $APP_NAME --image latest
|
36
|
-
|
37
32
|
# Uses a different image (which may not be promoted yet).
|
38
33
|
cpl run:detached rails db:migrate -a $APP_NAME --image appimage:123 # Exact image name
|
39
34
|
cpl run:detached rails db:migrate -a $APP_NAME --image latest # Latest sequential image
|
40
35
|
|
41
36
|
# Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
|
42
37
|
cpl run:detached rails db:migrate:status -a $APP_NAME -w other-workload
|
38
|
+
|
39
|
+
# Overrides remote CPLN_TOKEN env variable with local token.
|
40
|
+
# Useful when superuser rights are needed in remote container.
|
41
|
+
cpl run:detached rails db:migrate:status -a $APP_NAME --use-local-token
|
43
42
|
```
|
44
43
|
EX
|
45
44
|
|
@@ -91,8 +90,8 @@ module Command
|
|
91
90
|
|
92
91
|
# Override image if specified
|
93
92
|
image = config.options[:image]
|
94
|
-
image =
|
95
|
-
container_spec["image"] = image
|
93
|
+
image = latest_image if image == "latest"
|
94
|
+
container_spec["image"] = "/org/#{config.org}/image/#{image}"
|
96
95
|
|
97
96
|
# Set cron job props
|
98
97
|
spec["type"] = "cron"
|
@@ -108,10 +107,16 @@ module Command
|
|
108
107
|
cp.apply("kind" => "workload", "name" => one_off, "spec" => spec)
|
109
108
|
end
|
110
109
|
|
111
|
-
def runner_script
|
110
|
+
def runner_script # rubocop:disable Metrics/MethodLength
|
112
111
|
script = "echo '-- STARTED RUNNER SCRIPT --'\n"
|
113
112
|
script += Scripts.helpers_cleanup
|
114
113
|
|
114
|
+
if config.options["use_local_token"]
|
115
|
+
script += <<~SHELL
|
116
|
+
CPLN_TOKEN=$CONTROLPLANE_TOKEN
|
117
|
+
SHELL
|
118
|
+
end
|
119
|
+
|
115
120
|
script += <<~SHELL
|
116
121
|
if ! eval "#{args_join(config.args)}"; then echo "----- CRASHED -----"; fi
|
117
122
|
|
data/lib/command/test.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Be sure to have run: gem install debug
|
4
3
|
require "debug"
|
5
4
|
|
6
5
|
module Command
|
@@ -14,13 +13,10 @@ module Command
|
|
14
13
|
HIDE = true
|
15
14
|
|
16
15
|
def call
|
17
|
-
#
|
16
|
+
# Modify this method to trigger the code you want to test.
|
18
17
|
# You can use `debugger` to debug.
|
19
|
-
#
|
20
|
-
#
|
21
|
-
# rubocop:disable Lint/Debugger
|
22
|
-
pp latest_image_next
|
23
|
-
# rubocop:enable Lint/Debugger
|
18
|
+
# You can use `Cpl::Cli.start` to simulate a command
|
19
|
+
# (e.g., `Cpl::Cli.start(["deploy-image", "-a", "my-app-name"])`).
|
24
20
|
end
|
25
21
|
end
|
26
22
|
end
|
data/lib/core/config.rb
CHANGED
data/lib/core/controlplane.rb
CHANGED
@@ -45,7 +45,11 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def image_query(app_name = config.app, org_name = config.org)
|
48
|
-
|
48
|
+
# When `match_if_app_name_starts_with` is `true`, we query for images from any gvc containing the name,
|
49
|
+
# otherwise we query for images from a gvc with the exact name.
|
50
|
+
op = config.should_app_start_with?(app_name) ? "~" : "="
|
51
|
+
|
52
|
+
cmd = "cpln image query --org #{org_name} -o yaml --max -1 --prop repository#{op}#{app_name}"
|
49
53
|
perform_yaml(cmd)
|
50
54
|
end
|
51
55
|
|
@@ -86,7 +90,7 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
86
90
|
def gvc_query(app_name = config.app)
|
87
91
|
# When `match_if_app_name_starts_with` is `true`, we query for any gvc containing the name,
|
88
92
|
# otherwise we query for a gvc with the exact name.
|
89
|
-
op = config.
|
93
|
+
op = config.should_app_start_with?(app_name) ? "~" : "="
|
90
94
|
|
91
95
|
cmd = "cpln gvc query --org #{org} -o yaml --prop name#{op}#{app_name}"
|
92
96
|
perform_yaml(cmd)
|
@@ -128,10 +132,11 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
128
132
|
raise "Can't find workload '#{workload}', please create it with 'cpl apply-template #{workload} -a #{config.app}'."
|
129
133
|
end
|
130
134
|
|
131
|
-
def query_workloads(workload,
|
132
|
-
|
135
|
+
def query_workloads(workload, partial_gvc_match: false, partial_workload_match: false)
|
136
|
+
gvc_op = partial_gvc_match ? "~" : "="
|
137
|
+
workload_op = partial_workload_match ? "~" : "="
|
133
138
|
|
134
|
-
api.query_workloads(org: org, gvc: gvc, workload: workload,
|
139
|
+
api.query_workloads(org: org, gvc: gvc, workload: workload, gvc_op_type: gvc_op, workload_op_type: workload_op)
|
135
140
|
end
|
136
141
|
|
137
142
|
def workload_get_replicas(workload, location:)
|
@@ -149,6 +154,26 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
149
154
|
api.workload_deployments(workload: workload, gvc: gvc, org: org)
|
150
155
|
end
|
151
156
|
|
157
|
+
def workload_deployment_version_ready?(version, next_version, expected_status:)
|
158
|
+
return false unless version["workload"] == next_version
|
159
|
+
|
160
|
+
version["containers"]&.all? do |_, container|
|
161
|
+
ready = container.dig("resources", "replicas") == container.dig("resources", "replicasReady")
|
162
|
+
expected_status == true ? ready : !ready
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def workload_deployments_ready?(workload, expected_status:)
|
167
|
+
deployments = fetch_workload_deployments(workload)["items"]
|
168
|
+
deployments.all? do |deployment|
|
169
|
+
next_version = deployment.dig("status", "expectedDeploymentVersion")
|
170
|
+
|
171
|
+
deployment.dig("status", "versions")&.all? do |version|
|
172
|
+
workload_deployment_version_ready?(version, next_version, expected_status: expected_status)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
152
177
|
def workload_set_image_ref(workload, container:, image:)
|
153
178
|
cmd = "cpln workload update #{workload} #{gvc_org}"
|
154
179
|
cmd += " --set spec.containers.#{container}.image=/org/#{config.org}/image/#{image}"
|
@@ -184,8 +209,8 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
184
209
|
perform!(cmd)
|
185
210
|
end
|
186
211
|
|
187
|
-
def delete_workload(workload)
|
188
|
-
api.delete_workload(org: org, gvc:
|
212
|
+
def delete_workload(workload, a_gvc = gvc)
|
213
|
+
api.delete_workload(org: org, gvc: a_gvc, workload: workload)
|
189
214
|
end
|
190
215
|
|
191
216
|
def workload_connect(workload, location:, container: nil, shell: nil)
|
@@ -33,7 +33,7 @@ class ControlplaneApi
|
|
33
33
|
api_json_direct("/logs/org/#{org}/loki/api/v1/query_range?#{params}", method: :get, host: :logs)
|
34
34
|
end
|
35
35
|
|
36
|
-
def query_workloads(org:, gvc:, workload:,
|
36
|
+
def query_workloads(org:, gvc:, workload:, gvc_op_type:, workload_op_type:) # rubocop:disable Metrics/MethodLength
|
37
37
|
body = {
|
38
38
|
kind: "string",
|
39
39
|
spec: {
|
@@ -41,12 +41,12 @@ class ControlplaneApi
|
|
41
41
|
terms: [
|
42
42
|
{
|
43
43
|
rel: "gvc",
|
44
|
-
op:
|
44
|
+
op: gvc_op_type,
|
45
45
|
value: gvc
|
46
46
|
},
|
47
47
|
{
|
48
48
|
property: "name",
|
49
|
-
op:
|
49
|
+
op: workload_op_type,
|
50
50
|
value: workload
|
51
51
|
}
|
52
52
|
]
|
data/lib/cpl/version.rb
CHANGED
data/lib/cpl.rb
CHANGED
@@ -13,7 +13,7 @@ require "yaml"
|
|
13
13
|
require_relative "command/base"
|
14
14
|
|
15
15
|
modules = Dir["#{__dir__}/**/*.rb"].reject do |file|
|
16
|
-
file == __FILE__ || file.end_with?("
|
16
|
+
file == __FILE__ || file.end_with?("base.rb")
|
17
17
|
end
|
18
18
|
modules.sort.each { require(_1) }
|
19
19
|
|
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.0.
|
4
|
+
version: 1.0.3
|
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: 2023-
|
12
|
+
date: 2023-07-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: debug
|
@@ -223,6 +223,7 @@ files:
|
|
223
223
|
- docs/assets/memcached.png
|
224
224
|
- docs/assets/sidekiq-pre-stop-hook.png
|
225
225
|
- docs/commands.md
|
226
|
+
- docs/migrating.md
|
226
227
|
- docs/postgres.md
|
227
228
|
- docs/redis.md
|
228
229
|
- docs/tips.md
|
@@ -255,6 +256,7 @@ files:
|
|
255
256
|
- lib/command/ps_restart.rb
|
256
257
|
- lib/command/ps_start.rb
|
257
258
|
- lib/command/ps_stop.rb
|
259
|
+
- lib/command/ps_wait.rb
|
258
260
|
- lib/command/run.rb
|
259
261
|
- lib/command/run_cleanup.rb
|
260
262
|
- lib/command/run_detached.rb
|
@@ -271,7 +273,6 @@ files:
|
|
271
273
|
- lib/cpl.rb
|
272
274
|
- lib/cpl/version.rb
|
273
275
|
- lib/deprecated_commands.json
|
274
|
-
- lib/main.rb
|
275
276
|
- rakelib/create_release.rake
|
276
277
|
- script/add_command
|
277
278
|
- script/check_command_docs
|