cpl 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/{ci.yml → rspec.yml} +2 -25
- data/.github/workflows/rubocop.yml +29 -0
- data/CHANGELOG.md +6 -2
- data/CONTRIBUTING.md +16 -0
- data/Gemfile.lock +4 -16
- data/README.md +46 -25
- data/Rakefile +5 -0
- data/cpl.gemspec +0 -6
- data/docs/commands.md +40 -8
- data/examples/circleci.yml +4 -4
- data/lib/command/base.rb +43 -14
- data/lib/command/cleanup_old_images.rb +76 -0
- data/lib/command/cleanup_stale_apps.rb +88 -0
- data/lib/command/delete.rb +13 -5
- data/lib/command/{promote_image.rb → deploy_image.rb} +5 -5
- data/lib/command/env.rb +1 -1
- data/lib/command/no_command.rb +19 -0
- data/lib/command/open.rb +1 -1
- data/lib/command/run.rb +1 -1
- data/lib/command/run_detached.rb +1 -1
- data/lib/command/setup.rb +5 -2
- data/lib/command/test.rb +0 -2
- data/lib/command/version.rb +16 -0
- data/lib/core/config.rb +49 -16
- data/lib/core/controlplane.rb +27 -4
- data/lib/core/controlplane_api_direct.rb +2 -1
- data/lib/core/shell.rb +31 -0
- data/lib/cpl/version.rb +1 -1
- data/lib/cpl.rb +5 -3
- data/rakelib/create_release.rake +80 -0
- data/script/generate_commands_docs +3 -1
- metadata +13 -90
- /data/{postgres.md → docs/postgres.md} +0 -0
- /data/{redis.md → docs/redis.md} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3fb78a893e74bb1d23dad21fe62a08ce903bd87cbcc85b0cb3affb9a6b47a185
|
4
|
+
data.tar.gz: df6aec1b0be09b95ad65c61ee8c92fc4e44084985907920624868909adc78cc6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 961373f6642feac2ecf0bbea2f36ae13c8d70fe64d049b21d1e998b39edd9563772da1a1f4b5038a69e54bbabf2e6c6c6149a6410f3bb5b8be1f61c592619b5d
|
7
|
+
data.tar.gz: dcb5099f234926d9b84697f5c4e58556daeee3d245d4a8f97d092ab1b95547d250ef72908ef9f81cac252f19c0a5d8ca19c4eb3c822c20346e06cb402d97c413
|
@@ -1,4 +1,4 @@
|
|
1
|
-
name:
|
1
|
+
name: RSpec
|
2
2
|
|
3
3
|
on:
|
4
4
|
push:
|
@@ -7,29 +7,7 @@ on:
|
|
7
7
|
pull_request:
|
8
8
|
|
9
9
|
jobs:
|
10
|
-
rubocop:
|
11
|
-
runs-on: ubuntu-latest
|
12
|
-
name: Rubocop
|
13
|
-
strategy:
|
14
|
-
matrix:
|
15
|
-
ruby:
|
16
|
-
- "2.7"
|
17
|
-
- "3.0"
|
18
|
-
steps:
|
19
|
-
- name: Checkout code
|
20
|
-
uses: actions/checkout@v3
|
21
|
-
- name: Set up Ruby
|
22
|
-
uses: ruby/setup-ruby@v1
|
23
|
-
with:
|
24
|
-
ruby-version: ${{ matrix.ruby }}
|
25
|
-
rubygems: latest
|
26
|
-
- name: Install dependencies
|
27
|
-
run: bundle install
|
28
|
-
- name: Analyze code
|
29
|
-
run: bundle exec rubocop
|
30
|
-
|
31
10
|
rspec:
|
32
|
-
needs: rubocop
|
33
11
|
runs-on: ubuntu-latest
|
34
12
|
name: RSpec
|
35
13
|
env:
|
@@ -46,8 +24,7 @@ jobs:
|
|
46
24
|
uses: ruby/setup-ruby@v1
|
47
25
|
with:
|
48
26
|
ruby-version: ${{ matrix.ruby }}
|
49
|
-
|
50
|
-
bundler-cache: false
|
27
|
+
bundler-cache: true
|
51
28
|
- name: Install dependencies
|
52
29
|
run: bundle install
|
53
30
|
- name: Run tests
|
@@ -0,0 +1,29 @@
|
|
1
|
+
name: Rubocop
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- main
|
7
|
+
pull_request:
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
rubocop:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
name: Rubocop
|
13
|
+
strategy:
|
14
|
+
matrix:
|
15
|
+
ruby:
|
16
|
+
- "2.7"
|
17
|
+
- "3.0"
|
18
|
+
steps:
|
19
|
+
- name: Checkout code
|
20
|
+
uses: actions/checkout@v3
|
21
|
+
- name: Set up Ruby
|
22
|
+
uses: ruby/setup-ruby@v1
|
23
|
+
with:
|
24
|
+
ruby-version: ${{ matrix.ruby }}
|
25
|
+
bundler-cache: true
|
26
|
+
- name: Install dependencies
|
27
|
+
run: bundle install
|
28
|
+
- name: Analyze code
|
29
|
+
run: bundle exec rubocop
|
data/CHANGELOG.md
CHANGED
data/CONTRIBUTING.md
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
# Contributing
|
2
2
|
|
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.:
|
5
|
+
|
6
|
+
```sh
|
7
|
+
git clone https://github.com/shakacode/heroku-to-control-plane
|
8
|
+
|
9
|
+
# Create an alias in some local shell startup script, e.g., `.profile`, `.bashrc`, etc.
|
10
|
+
alias cpl="~/projects/heroku-to-control-plane/cpl"
|
11
|
+
```
|
12
|
+
|
13
|
+
Or set the path of the Ruby gem in your Gemfile.
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'cpl', path: '~/projects/heroku-to-control-plane'
|
17
|
+
```
|
18
|
+
|
3
19
|
## Linting
|
4
20
|
Be sure to run `rubocop -a` before committing code.
|
5
21
|
|
data/Gemfile.lock
CHANGED
@@ -1,23 +1,16 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cpl (0.
|
5
|
-
cgi (~> 0.3.6)
|
4
|
+
cpl (0.3.0)
|
6
5
|
debug (~> 1.7.1)
|
7
6
|
dotenv (~> 2.8.1)
|
8
|
-
json (~> 2.6.3)
|
9
|
-
net-http (~> 0.3.2)
|
10
|
-
pathname (~> 0.2.1)
|
11
7
|
psych (~> 5.1.0)
|
12
|
-
tempfile (~> 0.1.3)
|
13
8
|
thor (~> 1.2.1)
|
14
|
-
yaml (~> 0.2.1)
|
15
9
|
|
16
10
|
GEM
|
17
11
|
remote: https://rubygems.org/
|
18
12
|
specs:
|
19
13
|
ast (2.4.2)
|
20
|
-
cgi (0.3.6)
|
21
14
|
debug (1.7.1)
|
22
15
|
irb (>= 1.5.0)
|
23
16
|
reline (>= 0.3.1)
|
@@ -25,15 +18,12 @@ GEM
|
|
25
18
|
docile (1.4.0)
|
26
19
|
dotenv (2.8.1)
|
27
20
|
io-console (0.6.0)
|
28
|
-
irb (1.6.
|
21
|
+
irb (1.6.3)
|
29
22
|
reline (>= 0.3.0)
|
30
23
|
json (2.6.3)
|
31
|
-
net-http (0.3.2)
|
32
|
-
uri
|
33
24
|
parallel (1.22.1)
|
34
25
|
parser (3.2.0.0)
|
35
26
|
ast (~> 2.4.1)
|
36
|
-
pathname (0.2.1)
|
37
27
|
psych (5.1.0)
|
38
28
|
stringio
|
39
29
|
rainbow (3.1.1)
|
@@ -82,13 +72,11 @@ GEM
|
|
82
72
|
simplecov-html (0.12.3)
|
83
73
|
simplecov_json_formatter (0.1.4)
|
84
74
|
stringio (3.0.5)
|
85
|
-
tempfile (0.1.3)
|
86
75
|
thor (1.2.1)
|
87
76
|
unicode-display_width (2.4.2)
|
88
|
-
uri (0.12.0)
|
89
|
-
yaml (0.2.1)
|
90
77
|
|
91
78
|
PLATFORMS
|
79
|
+
ruby
|
92
80
|
x86_64-linux
|
93
81
|
|
94
82
|
DEPENDENCIES
|
@@ -101,4 +89,4 @@ DEPENDENCIES
|
|
101
89
|
simplecov (~> 0.22.0)
|
102
90
|
|
103
91
|
BUNDLED WITH
|
104
|
-
2.
|
92
|
+
2.3.26
|
data/README.md
CHANGED
@@ -1,7 +1,12 @@
|
|
1
|
-
# Heroku to Control Plane
|
1
|
+
# Heroku to Control Plane `cpl` CLI
|
2
2
|
|
3
3
|
_A playbook for migrating from [Heroku](https://heroku.com) to [Control Plane](https://controlplane.com)_
|
4
4
|
|
5
|
+
[](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rspec.yml)
|
6
|
+
[](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rubocop.yml)
|
7
|
+
|
8
|
+
[](https://badge.fury.io/rb/cpl)
|
9
|
+
|
5
10
|
This playbook shows how to move "Heroku apps" to "Control Plane workloads" via an open-source `cpl` CLI on top of Control Plane's `cpln` CLI.
|
6
11
|
|
7
12
|
Heroku provides a UX and CLI that enables easy publishing of Ruby on Rails and other apps. This ease of use comes via many "Heroku" abstractions and naming conventions.
|
@@ -20,8 +25,8 @@ To simplify migration to and usage of Control Plane for Heroku users, this repos
|
|
20
25
|
9. [CLI commands reference](#cli-commands-reference)
|
21
26
|
10. [Mapping of Heroku Commands to `cpl` and `cpln`](#mapping-of-heroku-commands-to-cpl-and-cpln)
|
22
27
|
11. [Examples](#examples)
|
23
|
-
12. [Migrating Postgres database from Heroku infrastructure](/postgres.md)
|
24
|
-
13. [Migrating Redis database from Heroku infrastructure](/redis.md)
|
28
|
+
12. [Migrating Postgres database from Heroku infrastructure](/docs/postgres.md)
|
29
|
+
13. [Migrating Redis database from Heroku infrastructure](/docs/redis.md)
|
25
30
|
|
26
31
|
## Key features
|
27
32
|
|
@@ -69,25 +74,20 @@ For the typical Rails app, this means:
|
|
69
74
|
|
70
75
|
## Installation
|
71
76
|
|
72
|
-
**Note:** `cpl` CLI is configured
|
77
|
+
**Note:** `cpl` CLI is configured either as a Ruby gem, [`cpl`](https://rubygems.org/gems/cpl) install or a local clone. For information on the latter, see [CONTRIBUTING.md](CONTRIBUTING.md).
|
73
78
|
|
74
79
|
1. Install `node` (required for Control Plane CLI).
|
75
80
|
2. Install `ruby` (required for these helpers).
|
76
|
-
3. Install Control Plane CLI (adds `cpln` command) and configure credentials
|
81
|
+
3. Install Control Plane CLI (adds `cpln` command) and configure credentials by running command `cpln login`.
|
77
82
|
|
78
83
|
```sh
|
79
84
|
npm install -g @controlplane/cli
|
80
85
|
cpln login
|
81
86
|
```
|
82
87
|
|
83
|
-
|
88
|
+
## Tips
|
84
89
|
|
85
|
-
|
86
|
-
git clone https://github.com/shakacode/heroku-to-control-plane
|
87
|
-
|
88
|
-
# Create an alias in some local shell startup script, e.g., `.profile`, `.bashrc`, etc.
|
89
|
-
alias cpl="~/projects/heroku-to-control-plane/cpl"
|
90
|
-
```
|
90
|
+
Do not confuse the `cpl` CLI with the `cpln` CLI. The `cpl` CLI is the Heroku to Control Plane playbook CLI. The `cpln` CLI is the Control Plane CLI.
|
91
91
|
|
92
92
|
- For each Git project that you want to deploy to Control Plane, copy project-specific configs to a `.controlplane` directory at the top of your project. `cpl` will pick those up depending on which project
|
93
93
|
folder tree it runs. Thus, this automates running several projects with different configs without explicitly switching configs.
|
@@ -101,6 +101,8 @@ alias cpl="~/projects/heroku-to-control-plane/cpl"
|
|
101
101
|
1. `myapp` is an app name defined in the `.controlplane/controlplane.yml` file, such as `ror-tutorial` in [this `controlplane.yml` file](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/controlplane.yml).
|
102
102
|
2. Other files in the `.controlplane/templates` directory are used by the `cpl setup` command.
|
103
103
|
|
104
|
+
### Initial Setup and Deployment
|
105
|
+
|
104
106
|
```sh
|
105
107
|
# Provision infrastructure (one-time-only for new apps) using templates.
|
106
108
|
# Note how the arguments correspond to files in the `.controlplane/templates` directory.
|
@@ -112,13 +114,32 @@ cpl build-image -a myapp --commit 456
|
|
112
114
|
# Prepare database.
|
113
115
|
cpl run:detached rails db:prepare -a myapp --image latest
|
114
116
|
|
115
|
-
#
|
116
|
-
cpl
|
117
|
+
# Deploy latest image.
|
118
|
+
cpl deploy-image -a myapp
|
117
119
|
|
118
120
|
# Open app in browser.
|
119
121
|
cpl open -a myapp
|
120
122
|
```
|
121
123
|
|
124
|
+
### Promoting code upgrades
|
125
|
+
|
126
|
+
```sh
|
127
|
+
# Build and push new image with sequential image tagging, e.g. 'ror-tutorial_123'
|
128
|
+
cpl build-image -a ror-tutorial
|
129
|
+
|
130
|
+
# OR
|
131
|
+
# Build and push with sequential image tagging and commit SHA, e.g. 'ror-tutorial_123_ABCD'
|
132
|
+
cpl build-image -a ror-tutorial --commit ABCD
|
133
|
+
|
134
|
+
# Run database migrations (or other release tasks) with latest image,
|
135
|
+
# while app is still running on previous image.
|
136
|
+
# This is analogous to the release phase.
|
137
|
+
cpl run:detached rails db:migrate -a ror-tutorial --image latest
|
138
|
+
|
139
|
+
# Deploy latest image to app
|
140
|
+
cpl deploy-image -a ror-tutorial
|
141
|
+
```
|
142
|
+
|
122
143
|
## Example project modifications for Control Plane
|
123
144
|
|
124
145
|
_See this for a complete example._
|
@@ -300,17 +321,17 @@ cpl --help
|
|
300
321
|
|
301
322
|
**`[WIP]`**
|
302
323
|
|
303
|
-
| Heroku Command
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
324
|
+
| Heroku Command | `cpl` or `cpln` |
|
325
|
+
| -------------------------------------------------------------------------------------------------------------- | --------------- |
|
326
|
+
| [heroku ps](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-ps-type-type) | `cpl ps` |
|
327
|
+
| [heroku config](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-config) | ? |
|
328
|
+
| [heroku maintenance](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-maintenance) | ? |
|
329
|
+
| [heroku logs](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-logs) | `cpl logs` |
|
330
|
+
| [heroku pg](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-pg-database) | ? |
|
331
|
+
| [heroku pipelines:promote](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-pipelines-promote) | `cpl promote` |
|
332
|
+
| [heroku psql](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-psql-database) | ? |
|
333
|
+
| [heroku redis](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-redis-database) | ? |
|
334
|
+
| [heroku releases](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-releases) | ? |
|
314
335
|
|
315
336
|
## Examples
|
316
337
|
|
data/Rakefile
CHANGED
data/cpl.gemspec
CHANGED
@@ -15,16 +15,10 @@ Gem::Specification.new do |spec|
|
|
15
15
|
|
16
16
|
spec.required_ruby_version = ">= 2.7.0"
|
17
17
|
|
18
|
-
spec.add_dependency "cgi", "~> 0.3.6"
|
19
18
|
spec.add_dependency "debug", "~> 1.7.1"
|
20
19
|
spec.add_dependency "dotenv", "~> 2.8.1"
|
21
|
-
spec.add_dependency "json", "~> 2.6.3"
|
22
|
-
spec.add_dependency "net-http", "~> 0.3.2"
|
23
|
-
spec.add_dependency "pathname", "~> 0.2.1"
|
24
20
|
spec.add_dependency "psych", "~> 5.1.0"
|
25
|
-
spec.add_dependency "tempfile", "~> 0.1.3"
|
26
21
|
spec.add_dependency "thor", "~> 1.2.1"
|
27
|
-
spec.add_dependency "yaml", "~> 0.2.1"
|
28
22
|
|
29
23
|
spec.add_development_dependency "rspec", "~> 3.12.0"
|
30
24
|
spec.add_development_dependency "rubocop", "~> 1.45.0"
|
data/docs/commands.md
CHANGED
@@ -21,6 +21,29 @@ This `-a` option is used in most of the commands and will pick all other app con
|
|
21
21
|
cpl build-image -a $APP_NAME
|
22
22
|
```
|
23
23
|
|
24
|
+
### `cleanup-old-images`
|
25
|
+
|
26
|
+
- Deletes all images for an app that are older than the specified amount of days
|
27
|
+
- Specify the amount of days through `old_image_retention_days` in the `.controlplane/controlplane.yml` file
|
28
|
+
- Will ask for explicit user confirmation
|
29
|
+
- Does not affect the latest image, regardless of how old it is
|
30
|
+
|
31
|
+
```sh
|
32
|
+
cpl cleanup-old-images -a $APP_NAME
|
33
|
+
```
|
34
|
+
|
35
|
+
### `cleanup-stale-apps`
|
36
|
+
|
37
|
+
- Deletes the whole app (GVC with all workloads and all images) for all stale apps
|
38
|
+
- Stale apps are identified based on the creation date of the latest image
|
39
|
+
- Specify the amount of days after an app should be considered stale through `stale_app_image_deployed_days` in the `.controlplane/controlplane.yml` file
|
40
|
+
- If `match_if_app_name_starts_with` is `true` in the `.controlplane/controlplane.yml` file, it will delete all stale apps that start with the name
|
41
|
+
- Will ask for explicit user confirmation
|
42
|
+
|
43
|
+
```sh
|
44
|
+
cpl cleanup-stale-apps -a $APP_NAME
|
45
|
+
```
|
46
|
+
|
24
47
|
### `config`
|
25
48
|
|
26
49
|
- Displays current configs (global and app-specific)
|
@@ -42,6 +65,14 @@ cpl config -a $APP_NAME
|
|
42
65
|
cpl delete -a $APP_NAME
|
43
66
|
```
|
44
67
|
|
68
|
+
### `deploy-image`
|
69
|
+
|
70
|
+
- Deploys the latest image to app workloads
|
71
|
+
|
72
|
+
```sh
|
73
|
+
cpl deploy-image -a $APP_NAME
|
74
|
+
```
|
75
|
+
|
45
76
|
### `env`
|
46
77
|
|
47
78
|
- Displays app-specific environment variables
|
@@ -90,14 +121,6 @@ cpl open -a $APP_NAME
|
|
90
121
|
cpl open -a $APP_NAME -w $WORKLOAD_NAME
|
91
122
|
```
|
92
123
|
|
93
|
-
### `promote-image`
|
94
|
-
|
95
|
-
- Promotes the latest image to app workloads
|
96
|
-
|
97
|
-
```sh
|
98
|
-
cpl promote-image -a $APP_NAME
|
99
|
-
```
|
100
|
-
|
101
124
|
### `ps`
|
102
125
|
|
103
126
|
- Shows running replicas in app
|
@@ -217,3 +240,12 @@ cpl setup redis -a $APP_NAME
|
|
217
240
|
# Applies several templates (practically creating full app).
|
218
241
|
cpl setup gvc postgres redis rails -a $APP_NAME
|
219
242
|
```
|
243
|
+
|
244
|
+
### `version`
|
245
|
+
|
246
|
+
- Displays the current version of the CLI
|
247
|
+
- Can also be done with `cpl --version` or `cpl -v`
|
248
|
+
|
249
|
+
```sh
|
250
|
+
cpl version
|
251
|
+
```
|
data/examples/circleci.yml
CHANGED
@@ -30,8 +30,8 @@ build-staging:
|
|
30
30
|
name: Database tasks
|
31
31
|
command: cpl run:detached rails db:migrate -a ${APP_NAME} --image latest
|
32
32
|
- run:
|
33
|
-
name:
|
34
|
-
command: cpl
|
33
|
+
name: Deploy image
|
34
|
+
command: cpl deploy-image -a ${APP_NAME}
|
35
35
|
|
36
36
|
# Example config for review app:
|
37
37
|
# - triggers manually if needed
|
@@ -82,8 +82,8 @@ build-review-app:
|
|
82
82
|
cpl run:detached 'LOG_LEVEL=warn rails db:migrate' -a ${APP_NAME} --image latest
|
83
83
|
fi
|
84
84
|
- run:
|
85
|
-
name:
|
86
|
-
command: cpl
|
85
|
+
name: Deploy image
|
86
|
+
command: cpl deploy-image -a ${APP_NAME}
|
87
87
|
|
88
88
|
review-app:
|
89
89
|
jobs:
|
data/lib/command/base.rb
CHANGED
@@ -90,14 +90,40 @@ module Command
|
|
90
90
|
}
|
91
91
|
end
|
92
92
|
|
93
|
+
def self.skip_confirm_option(required: false)
|
94
|
+
{
|
95
|
+
name: :yes,
|
96
|
+
params: {
|
97
|
+
aliases: ["-y"],
|
98
|
+
banner: "SKIP_CONFIRM",
|
99
|
+
desc: "Skip confirmation",
|
100
|
+
type: :boolean,
|
101
|
+
required: required
|
102
|
+
}
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.version_option(required: false)
|
107
|
+
{
|
108
|
+
name: :version,
|
109
|
+
params: {
|
110
|
+
aliases: ["-v"],
|
111
|
+
banner: "VERSION",
|
112
|
+
desc: "Displays the current version of the CLI",
|
113
|
+
type: :boolean,
|
114
|
+
required: required
|
115
|
+
}
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
93
119
|
def self.all_options
|
94
120
|
methods.grep(/_option$/).map { |method| send(method.to_s) }
|
95
121
|
end
|
96
122
|
|
97
|
-
def self.
|
123
|
+
def self.all_options_by_key_name
|
98
124
|
all_options.each_with_object({}) do |option, result|
|
99
|
-
option[:params][:aliases].each { |current_alias| result[current_alias.to_s] = option
|
100
|
-
result["--#{option[:name]}"] = option
|
125
|
+
option[:params][:aliases].each { |current_alias| result[current_alias.to_s] = option }
|
126
|
+
result["--#{option[:name]}"] = option
|
101
127
|
end
|
102
128
|
end
|
103
129
|
|
@@ -125,20 +151,23 @@ module Command
|
|
125
151
|
cp.workload_delete(workload, no_raise: true)
|
126
152
|
end
|
127
153
|
|
128
|
-
def
|
154
|
+
def latest_image_from(items, app_name: config.app, name_only: true)
|
155
|
+
matching_items = items.filter { |item| item["name"].start_with?("#{app_name}:") }
|
156
|
+
|
157
|
+
# Or special string to indicate no image available
|
158
|
+
if matching_items.empty?
|
159
|
+
"#{app_name}:#{NO_IMAGE_AVAILABLE}"
|
160
|
+
else
|
161
|
+
latest_item = matching_items.max_by { |item| extract_image_number(item["name"]) }
|
162
|
+
name_only ? latest_item["name"] : latest_item
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def latest_image
|
129
167
|
@latest_image ||=
|
130
168
|
begin
|
131
169
|
items = cp.image_query["items"]
|
132
|
-
|
133
|
-
item["name"] if item["name"].start_with?("#{config.app}:")
|
134
|
-
end
|
135
|
-
|
136
|
-
# Or special string to indicate no image available
|
137
|
-
if matching_items.empty?
|
138
|
-
"#{config.app}:#{NO_IMAGE_AVAILABLE}"
|
139
|
-
else
|
140
|
-
matching_items.max_by { |item| extract_image_number(item) }
|
141
|
-
end
|
170
|
+
latest_image_from(items)
|
142
171
|
end
|
143
172
|
end
|
144
173
|
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Command
|
4
|
+
class CleanupOldImages < Base
|
5
|
+
NAME = "cleanup-old-images"
|
6
|
+
OPTIONS = [
|
7
|
+
app_option(required: true),
|
8
|
+
skip_confirm_option
|
9
|
+
].freeze
|
10
|
+
DESCRIPTION = "Deletes all images for an app that are older than the specified amount of days"
|
11
|
+
LONG_DESCRIPTION = <<~HEREDOC
|
12
|
+
- Deletes all images for an app that are older than the specified amount of days
|
13
|
+
- Specify the amount of days through `old_image_retention_days` in the `.controlplane/controlplane.yml` file
|
14
|
+
- Will ask for explicit user confirmation
|
15
|
+
- Does not affect the latest image, regardless of how old it is
|
16
|
+
HEREDOC
|
17
|
+
|
18
|
+
def call
|
19
|
+
return progress.puts "No old images found" if old_images.empty?
|
20
|
+
|
21
|
+
progress.puts "Old images:"
|
22
|
+
old_images.each do |image|
|
23
|
+
progress.puts " #{image[:name]} (#{Shell.color((image[:date]).to_s, :red)})"
|
24
|
+
end
|
25
|
+
|
26
|
+
return unless confirm_delete
|
27
|
+
|
28
|
+
progress.puts
|
29
|
+
delete_images
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def old_images # rubocop:disable Metrics/MethodLength
|
35
|
+
@old_images ||=
|
36
|
+
begin
|
37
|
+
result_images = []
|
38
|
+
|
39
|
+
now = DateTime.now
|
40
|
+
old_image_retention_days = config[:old_image_retention_days]
|
41
|
+
|
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
|
+
})
|
57
|
+
end
|
58
|
+
|
59
|
+
result_images
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def confirm_delete
|
64
|
+
return true if config.options[:yes]
|
65
|
+
|
66
|
+
Shell.confirm("\nAre you sure you want to delete these #{old_images.length} images?")
|
67
|
+
end
|
68
|
+
|
69
|
+
def delete_images
|
70
|
+
old_images.each do |image|
|
71
|
+
cp.image_delete(image[:name])
|
72
|
+
progress.puts "#{image[:name]} deleted"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "date"
|
4
|
+
|
5
|
+
module Command
|
6
|
+
class CleanupStaleApps < Base
|
7
|
+
NAME = "cleanup-stale-apps"
|
8
|
+
OPTIONS = [
|
9
|
+
app_option(required: true),
|
10
|
+
skip_confirm_option
|
11
|
+
].freeze
|
12
|
+
DESCRIPTION = "Deletes the whole app (GVC with all workloads and all images) for all stale apps"
|
13
|
+
LONG_DESCRIPTION = <<~HEREDOC
|
14
|
+
- Deletes the whole app (GVC with all workloads and all images) for all stale apps
|
15
|
+
- Stale apps are identified based on the creation date of the latest image
|
16
|
+
- Specify the amount of days after an app should be considered stale through `stale_app_image_deployed_days` in the `.controlplane/controlplane.yml` file
|
17
|
+
- If `match_if_app_name_starts_with` is `true` in the `.controlplane/controlplane.yml` file, it will delete all stale apps that start with the name
|
18
|
+
- Will ask for explicit user confirmation
|
19
|
+
HEREDOC
|
20
|
+
|
21
|
+
def call # rubocop:disable Metrics/MethodLength
|
22
|
+
return progress.puts "No stale apps found" if stale_apps.empty?
|
23
|
+
|
24
|
+
progress.puts "Stale apps:"
|
25
|
+
stale_apps.each do |app|
|
26
|
+
progress.puts " #{app[:name]} (#{Shell.color((app[:date]).to_s, :red)})"
|
27
|
+
end
|
28
|
+
|
29
|
+
return unless confirm_delete
|
30
|
+
|
31
|
+
progress.puts
|
32
|
+
stale_apps.each do |app|
|
33
|
+
delete_gvc(app)
|
34
|
+
delete_images(app)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def stale_apps # rubocop:disable Metrics/MethodLength
|
41
|
+
@stale_apps ||=
|
42
|
+
begin
|
43
|
+
apps = []
|
44
|
+
|
45
|
+
now = DateTime.now
|
46
|
+
stale_app_image_deployed_days = config[:stale_app_image_deployed_days]
|
47
|
+
|
48
|
+
gvcs = cp.gvc_query(config.app)["items"]
|
49
|
+
gvcs.each do |gvc|
|
50
|
+
app_name = gvc["name"]
|
51
|
+
|
52
|
+
images = cp.image_query(app_name)["items"].filter { |item| item["name"].start_with?("#{app_name}:") }
|
53
|
+
image = latest_image_from(images, app_name: app_name, name_only: false)
|
54
|
+
|
55
|
+
created_date = DateTime.parse(image["created"])
|
56
|
+
diff_in_days = (now - created_date).to_i
|
57
|
+
next unless diff_in_days >= stale_app_image_deployed_days
|
58
|
+
|
59
|
+
apps.push({
|
60
|
+
name: app_name,
|
61
|
+
date: created_date,
|
62
|
+
images: images.map { |current_image| current_image["name"] }
|
63
|
+
})
|
64
|
+
end
|
65
|
+
|
66
|
+
apps
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def confirm_delete
|
71
|
+
return true if config.options[:yes]
|
72
|
+
|
73
|
+
Shell.confirm("\nAre you sure you want to delete these #{stale_apps.length} apps?")
|
74
|
+
end
|
75
|
+
|
76
|
+
def delete_gvc(app)
|
77
|
+
cp.gvc_delete(app[:name])
|
78
|
+
progress.puts "#{app[:name]} deleted"
|
79
|
+
end
|
80
|
+
|
81
|
+
def delete_images(app)
|
82
|
+
app[:images].each do |image|
|
83
|
+
cp.image_delete(image)
|
84
|
+
progress.puts "#{image} deleted"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|