cpl 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/{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
|
+
[![RSpec](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rspec.yml/badge.svg)](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rspec.yml)
|
6
|
+
[![Rubocop](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rubocop.yml/badge.svg)](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rubocop.yml)
|
7
|
+
|
8
|
+
[![Gem](https://badge.fury.io/rb/cpl.svg)](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
|