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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2392653f1a75e0dc398590cc5c0e9d89d4769a50af3432b2861e01ff794e8cda
4
- data.tar.gz: 13c33cc6465394b90b1fd8e94f711c98135bfdcb25756db7d3b7b0c8bfc819fa
3
+ metadata.gz: 3fb78a893e74bb1d23dad21fe62a08ce903bd87cbcc85b0cb3affb9a6b47a185
4
+ data.tar.gz: df6aec1b0be09b95ad65c61ee8c92fc4e44084985907920624868909adc78cc6
5
5
  SHA512:
6
- metadata.gz: bc2b5f2555b8f329ad8d5b2be9f5762e6f265f499baaa035e65c24257b47c26459842f2611ee8d352af65b3dcabf4f0e6570007c59f5ca543f3edb382ed04747
7
- data.tar.gz: 904028e00550311c8b9327fb3a17a8a00c75b4806e81ca8359d5b3f6ae55b19f8a89efbc3ccc087e4f62e2243930587df1f693ebbbac9470377b0e6905e0dd19
6
+ metadata.gz: 961373f6642feac2ecf0bbea2f36ae13c8d70fe64d049b21d1e998b39edd9563772da1a1f4b5038a69e54bbabf2e6c6c6149a6410f3bb5b8be1f61c592619b5d
7
+ data.tar.gz: dcb5099f234926d9b84697f5c4e58556daeee3d245d4a8f97d092ab1b95547d250ef72908ef9f81cac252f19c0a5d8ca19c4eb3c822c20346e06cb402d97c413
@@ -1,4 +1,4 @@
1
- name: CI
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
- rubygems: latest
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
@@ -1,5 +1,9 @@
1
- ## [Unreleased]
1
+ # Changelog
2
2
 
3
- ## [0.1.0] - 2023-02-08
3
+ ## 0.1.1 - 2023-03-09
4
+
5
+ - Fixed issue with default gems for older Ruby versions (#14)
6
+
7
+ ## 0.1.0 - 2023-02-19
4
8
 
5
9
  - Initial release
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.1.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.2)
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.4.6
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 via a local clone clone of this repo. We may publish it later as a Ruby gem or Node package.
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
- 4. Install this repo locally and alias `cpl` command globally for easier access, e.g.:
88
+ ## Tips
84
89
 
85
- ```sh
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
- # Promote latest image.
116
- cpl promote-image -a myapp
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 | `cpl` or `cpln` |
304
- | ---------------------------------------------------------------------------------------------------------------- | --------------- |
305
- | `[heroku ps](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-ps-type-type)` | `cpl ps` |
306
- | `[heroku config](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-config)` | ? |
307
- | `[heroku maintenance](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-maintenance)` | ? |
308
- | `[heroku logs](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-logs)` | `cpl logs` |
309
- | `[heroku pg](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-pg-database)` | ? |
310
- | `[heroku pipelines:promote](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-pipelines-promote)` | `cpl promote` |
311
- | `[heroku psql](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-psql-database)` | ? |
312
- | `[heroku redis](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-redis-database)` | ? |
313
- | `[heroku releases](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-releases)` | ? |
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
@@ -9,3 +9,8 @@ RSpec::Core::RakeTask.new(:spec)
9
9
  RuboCop::RakeTask.new
10
10
 
11
11
  task default: %i[spec rubocop]
12
+
13
+ desc "Updates commands.md file"
14
+ task :command_docs do
15
+ sh "./script/generate_commands_docs"
16
+ end
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
+ ```
@@ -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: Promote image
34
- command: cpl promote-image -a ${APP_NAME}
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: Promote image
86
- command: cpl promote-image -a ${APP_NAME}
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.all_options_key_name
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[:name] }
100
- result["--#{option[:name]}"] = option[:name]
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 latest_image # rubocop:disable Metrics/MethodLength
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
- matching_items = items.filter_map do |item|
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