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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da0cd8b4b3f57a141b2a148a35555069f4ff4135803e4ae95932cd3887e3a567
4
- data.tar.gz: dab258fecce6a5948374057418959d736915ab69f84593e1b189c148f62f00be
3
+ metadata.gz: 9d187c354ac6a9a75c0aea498f8fbc060f741809d075439ee22663fdf06f265d
4
+ data.tar.gz: ffde2f0f3be4663ba85baadf599c6a12882c2ba11734f9220d26366bacc5368d
5
5
  SHA512:
6
- metadata.gz: 31c5cfe13e647abf6f34f6b021c808c220db07a48cd223d0d277ad7814ab0942961a7b75c63d89c3c109e7ca670d478285e5289fdb9fe64ccd828917b7fccf22
7
- data.tar.gz: 926431c8d7fb4c5f8ba7930a3a156d70718dcce6eb1c717e9859f5a8546900266e4267cee7c755ca4c99612fb1b574a7cbfdd6ca23a2fd5483c1eb3c743a83db
6
+ metadata.gz: 70941d48b8ccc5bb3fb46b6c96c91fd0d3c2f13f2a702822ee6541231d90957df5a3b3c9d60d2091afad9f6e76be8870953f69085a97315c2a865d534d0c1ce7
7
+ data.tar.gz: 3a109996c812ec2b2e9dc76c189bd5d3518835e5b221ff8f12416982f3542f1753dcba441dbb3ec32224e807d0c83db54fe78e21cad43da0d829b8d65593a6d5
data/CHANGELOG.md CHANGED
@@ -1,9 +1,38 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.1 - 2023-03-09
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
- - Fixed issue with default gems for older Ruby versions (#14)
5
+ ## Contributors
6
6
 
7
- ## 0.1.0 - 2023-02-19
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
- Rather than installing `cpl` as a Ruby gem, install this repo locally and alias `cpl` command globally for easier access, e.g.:
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
- Be sure to run `rubocop -a` before committing code.
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. Install gem: `gem install debug`
25
- 2. Require: Add a `require "debug"` statement to the file you want to debug.
26
- 3. Add breakpoint: Add a `debugger` statement to the line you want to debug.
27
- 4. Modify the `lib/command/test.rb` file to triggger the code that you want to run.
28
- 5. Run the test in your test app with a `.controlplane` directory. `cpl test -a my-app-name`
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.1)
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.0)
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. [Example CLI Flow for Application Build/Deployment](#example-cli-flow-for-application-builddeployment)
32
- - [Initial Setup and Deployment](#initial-setup-and-deployment)
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
- ## Example CLI Flow for Application Build/Deployment
111
+ ## Steps to Migrate
114
112
 
115
- **Notes:**
113
+ Click [here](/docs/migrating.md) to see the steps to migrate.
116
114
 
117
- - `my-app` is an app name defined in the `.controlplane/controlplane.yml` file, such as `ror-tutorial` in
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
- ### Initial Setup and Deployment
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
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "cpl"
4
+ require_relative "../lib/cpl"
5
5
 
6
6
  Cpl::Cli.start
data/cpl CHANGED
@@ -1,15 +1,6 @@
1
- #!/bin/sh
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- SCRIPT_DIR=$(dirname $(realpath $0))
4
+ require_relative "lib/cpl"
4
5
 
5
- # exports .env to vars
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.
@@ -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
- git clone https://github.com/shakacode/heroku-to-control-plane ~/heroku-to-control-plane
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?("#{config.app}:") }
43
-
44
- # Remove latest image, because we don't want to delete the image that is currently deployed
45
- latest_image_name = latest_image_from(images)
46
- images = images.filter { |item| item["name"] != latest_image_name }
47
-
48
- images.each do |image|
49
- created_date = DateTime.parse(image["created"])
50
- diff_in_days = (now - created_date).to_i
51
- next unless diff_in_days >= old_image_retention_days
52
-
53
- result_images.push({
54
- name: image["name"],
55
- date: created_date
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"]
@@ -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.fetch_workload_deployments(workload)&.dig("items")&.any? do |item|
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
@@ -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 not ready")
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 not ready", retry_on_failure: true) do
45
- cp.fetch_workload_deployments(workload)&.dig("items")&.all? do |item|
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 = "/org/#{config.org}/image/#{latest_image}" if image == "latest"
103
- container_spec["image"] = image if 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"] ||= []
@@ -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
- progress.puts(" #{workload[:name]} " \
24
- "(#{Shell.color("#{workload[:date]} - #{workload[:days]} days ago", :red)})")
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
- workloads = cp.query_workloads("-run-", partial_match: true)["items"]
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("-run-")
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
- step("Deleting run workload '#{workload[:name]}'") do
95
- cp.delete_workload(workload[:name])
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
@@ -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 = "/org/#{config.org}/image/#{latest_image}" if image == "latest"
95
- container_spec["image"] = image if 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
- # Change code here to test.
16
+ # Modify this method to trigger the code you want to test.
18
17
  # You can use `debugger` to debug.
19
- # debugger
20
- # Or print values
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
@@ -34,6 +34,10 @@ class Config
34
34
  "#{app_dir}/.controlplane"
35
35
  end
36
36
 
37
+ def should_app_start_with?(app)
38
+ apps[app.to_sym]&.dig(:match_if_app_name_starts_with) || false
39
+ end
40
+
37
41
  private
38
42
 
39
43
  def ensure_current_config!
@@ -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
- cmd = "cpln image query --org #{org_name} -o yaml --max -1 --prop repository=#{app_name}"
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.current[:match_if_app_name_starts_with] ? "~" : "="
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, partial_match: false)
132
- op = partial_match ? "~" : "="
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, op_type: op)
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: gvc, workload: workload)
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:, op_type:) # rubocop:disable Metrics/MethodLength
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: op_type,
49
+ op: workload_op_type,
50
50
  value: workload
51
51
  }
52
52
  ]
data/lib/cpl/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cpl
4
- VERSION = "1.0.1"
4
+ VERSION = "1.0.3"
5
5
  MIN_CPLN_VERSION = "0.0.71"
6
6
  end
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?("main.rb") || file.end_with?("base.rb")
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.1
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-06-28 00:00:00.000000000 Z
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
data/lib/main.rb DELETED
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "cpl"
4
-
5
- Cpl::Cli.start