cpl 0.1.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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +60 -0
  3. data/.gitignore +14 -0
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +16 -0
  6. data/CHANGELOG.md +5 -0
  7. data/CONTRIBUTING.md +12 -0
  8. data/Gemfile +7 -0
  9. data/Gemfile.lock +104 -0
  10. data/LICENSE +21 -0
  11. data/README.md +318 -0
  12. data/Rakefile +11 -0
  13. data/bin/cpl +6 -0
  14. data/cpl +15 -0
  15. data/cpl.gemspec +42 -0
  16. data/docs/commands.md +219 -0
  17. data/docs/troubleshooting.md +6 -0
  18. data/examples/circleci.yml +106 -0
  19. data/examples/controlplane.yml +44 -0
  20. data/lib/command/base.rb +177 -0
  21. data/lib/command/build_image.rb +25 -0
  22. data/lib/command/config.rb +33 -0
  23. data/lib/command/delete.rb +50 -0
  24. data/lib/command/env.rb +21 -0
  25. data/lib/command/exists.rb +23 -0
  26. data/lib/command/latest_image.rb +18 -0
  27. data/lib/command/logs.rb +29 -0
  28. data/lib/command/open.rb +33 -0
  29. data/lib/command/promote_image.rb +27 -0
  30. data/lib/command/ps.rb +40 -0
  31. data/lib/command/ps_restart.rb +34 -0
  32. data/lib/command/ps_start.rb +34 -0
  33. data/lib/command/ps_stop.rb +34 -0
  34. data/lib/command/run.rb +106 -0
  35. data/lib/command/run_detached.rb +148 -0
  36. data/lib/command/setup.rb +59 -0
  37. data/lib/command/test.rb +26 -0
  38. data/lib/core/config.rb +81 -0
  39. data/lib/core/controlplane.rb +128 -0
  40. data/lib/core/controlplane_api.rb +51 -0
  41. data/lib/core/controlplane_api_cli.rb +10 -0
  42. data/lib/core/controlplane_api_direct.rb +42 -0
  43. data/lib/core/scripts.rb +34 -0
  44. data/lib/cpl/version.rb +5 -0
  45. data/lib/cpl.rb +139 -0
  46. data/lib/main.rb +5 -0
  47. data/postgres.md +436 -0
  48. data/redis.md +112 -0
  49. data/script/generate_commands_docs +60 -0
  50. data/templates/gvc.yml +13 -0
  51. data/templates/identity.yml +2 -0
  52. data/templates/memcached.yml +23 -0
  53. data/templates/postgres.yml +31 -0
  54. data/templates/rails.yml +25 -0
  55. data/templates/redis.yml +20 -0
  56. data/templates/sidekiq.yml +28 -0
  57. metadata +312 -0
data/docs/commands.md ADDED
@@ -0,0 +1,219 @@
1
+ <!-- NOTE: This file is automatically generated by running `script/generate_commands_docs`. Do NOT edit it manually. -->
2
+
3
+ ### Common Options
4
+
5
+ ```
6
+ -a XXX, --app XXX app ref on Control Plane (GVC)
7
+ ```
8
+
9
+ This `-a` option is used in most of the commands and will pick all other app configurations from the project-specific
10
+ `.controlplane/controlplane.yml` file.
11
+
12
+ ### Commands
13
+
14
+ ### `build-image`
15
+
16
+ - Builds and pushes the image to Control Plane
17
+ - Automatically assigns image numbers, e.g., `app:1`, `app:2`, etc.
18
+ - Uses `.controlplane/Dockerfile`
19
+
20
+ ```sh
21
+ cpl build-image -a $APP_NAME
22
+ ```
23
+
24
+ ### `config`
25
+
26
+ - Displays current configs (global and app-specific)
27
+
28
+ ```sh
29
+ # Shows the global config.
30
+ cpl config
31
+
32
+ # Shows both global and app-specific configs.
33
+ cpl config -a $APP_NAME
34
+ ```
35
+
36
+ ### `delete`
37
+
38
+ - Deletes the whole app (GVC with all workloads and all images)
39
+ - Will ask for explicit user confirmation
40
+
41
+ ```sh
42
+ cpl delete -a $APP_NAME
43
+ ```
44
+
45
+ ### `env`
46
+
47
+ - Displays app-specific environment variables
48
+
49
+ ```sh
50
+ cpl env -a $APP_NAME
51
+ ```
52
+
53
+ ### `exists`
54
+
55
+ - Shell-checks if an application (GVC) exists, useful in scripts, e.g.:
56
+
57
+ ```sh
58
+ if [ cpl exists -a $APP_NAME ]; ...
59
+ ```
60
+
61
+ ### `latest-image`
62
+
63
+ - Displays the latest image name
64
+
65
+ ```sh
66
+ cpl latest-image -a $APP_NAME
67
+ ```
68
+
69
+ ### `logs`
70
+
71
+ - Light wrapper to display tailed raw logs for app/workload syntax
72
+
73
+ ```sh
74
+ # Displays logs for the default workload (`one_off_workload`).
75
+ cpl logs -a $APP_NAME
76
+
77
+ # Displays logs for a specific workload.
78
+ cpl logs -a $APP_NAME -w $WORKLOAD_NAME
79
+ ```
80
+
81
+ ### `open`
82
+
83
+ - Opens the app endpoint URL in the default browser
84
+
85
+ ```sh
86
+ # Opens the endpoint of the default workload (`one_off_workload`).
87
+ cpl open -a $APP_NAME
88
+
89
+ # Opens the endpoint of a specific workload.
90
+ cpl open -a $APP_NAME -w $WORKLOAD_NAME
91
+ ```
92
+
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
+ ### `ps`
102
+
103
+ - Shows running replicas in app
104
+
105
+ ```sh
106
+ # Shows running replicas in app, for all workloads.
107
+ cpl ps -a $APP_NAME
108
+
109
+ # Shows running replicas in app, for a specific workload.
110
+ cpl ps -a $APP_NAME -w $WORKLOAD_NAME
111
+ ```
112
+
113
+ ### `ps:restart`
114
+
115
+ - Forces redeploy of workloads in app
116
+
117
+ ```sh
118
+ # Forces redeploy of all workloads in app.
119
+ cpl ps:restart -a $APP_NAME
120
+
121
+ # Forces redeploy of a specific workload in app.
122
+ cpl ps:restart -a $APP_NAME -w $WORKLOAD_NAME
123
+ ```
124
+
125
+ ### `ps:start`
126
+
127
+ - Starts workloads in app
128
+
129
+ ```sh
130
+ # Starts all workloads in app.
131
+ cpl ps:start -a $APP_NAME
132
+
133
+ # Starts a specific workload in app.
134
+ cpl ps:start -a $APP_NAME -w $WORKLOAD_NAME
135
+ ```
136
+
137
+ ### `ps:stop`
138
+
139
+ - Stops workloads in app
140
+
141
+ ```sh
142
+ # Stops all workloads in app.
143
+ cpl ps:stop -a $APP_NAME
144
+
145
+ # Stops a specific workload in app.
146
+ cpl ps:stop -a $APP_NAME -w $WORKLOAD_NAME
147
+ ```
148
+
149
+ ### `run`
150
+
151
+ - Runs one-off **_interactive_** replicas (analog of `heroku run`)
152
+ - Uses `Standard` workload type and `cpln exec` as the execution method, with CLI streaming
153
+ - May not work correctly with tasks that last over 5 minutes (there's a Control Plane scaling bug at the moment)
154
+
155
+ > **IMPORTANT:** Useful for development where it's needed for interaction, and where network connection drops and
156
+ > task crashing are tolerable. For production tasks, it's better to use `cpl run:detached`.
157
+
158
+ ```sh
159
+ # Opens shell (bash by default).
160
+ cpl run -a $APP_NAME
161
+
162
+ # Runs command, displays output, and exits shell.
163
+ cpl run ls / -a $APP_NAME
164
+ cpl run rails db:migrate:status -a $APP_NAME
165
+
166
+ # Runs command and keeps shell open.
167
+ cpl run rails c -a $APP_NAME
168
+
169
+ # Uses a different image (which may not be promoted yet).
170
+ cpl run rails db:migrate -a $APP_NAME --image appimage:123 # Exact image name
171
+ cpl run rails db:migrate -a $APP_NAME --image latest # Latest sequential image
172
+ ```
173
+
174
+ ### `run:detached`
175
+
176
+ - Runs one-off **_non-interactive_** replicas (close analog of `heroku run:detached`)
177
+ - Uses `Cron` workload type with log async fetching
178
+ - Implemented with only async execution methods, more suitable for production tasks
179
+ - Has alternative log fetch implementation with only JSON-polling and no WebSockets
180
+ - Less responsive but more stable, useful for CI tasks
181
+
182
+ ```sh
183
+ cpl run:detached rails db:prepare -a $APP_NAME
184
+ cpl run:detached 'LOG_LEVEL=warn rails db:migrate' -a $APP_NAME
185
+
186
+ # Uses some other image.
187
+ cpl run:detached rails db:migrate -a $APP_NAME --image /some/full/image/path
188
+
189
+ # Uses latest app image (which may not be promoted yet).
190
+ cpl run:detached rails db:migrate -a $APP_NAME --image latest
191
+
192
+ # Uses a different image (which may not be promoted yet).
193
+ cpl run:detached rails db:migrate -a $APP_NAME --image appimage:123 # Exact image name
194
+ cpl run:detached rails db:migrate -a $APP_NAME --image latest # Latest sequential image
195
+ ```
196
+
197
+ ### `setup`
198
+
199
+ - Applies application-specific configs from templates (e.g., for every review-app)
200
+ - Publishes (creates or updates) those at Control Plane infrastructure
201
+ - Picks templates from the `.controlplane/templates` directory
202
+ - Templates are ordinary Control Plane templates but with variable preprocessing
203
+
204
+ **Preprocessed template variables:**
205
+
206
+ ```
207
+ APP_GVC - basically GVC or app name
208
+ APP_LOCATION - default location
209
+ APP_ORG - organization
210
+ APP_IMAGE - will use latest app image
211
+ ```
212
+
213
+ ```sh
214
+ # Applies single template.
215
+ cpl setup redis -a $APP_NAME
216
+
217
+ # Applies several templates (practically creating full app).
218
+ cpl setup gvc postgres redis rails -a $APP_NAME
219
+ ```
@@ -0,0 +1,6 @@
1
+ # Troubleshooting
2
+
3
+
4
+ ## App Web Page Shows `upstream request timeout`
5
+
6
+ If you get a blank screen showing the message `upstream request timeout` on your app after running `cpl open -a my-app-name`, check out the application logs. Your image has been promoted and your app crashing when starting.
@@ -0,0 +1,106 @@
1
+ # Example config for staging app:
2
+ # - triggers on push to master
3
+ # - static app name
4
+ # - no resource provisioning
5
+ # - no database setup, only migration
6
+ build-staging:
7
+ docker:
8
+ - image: cimg/ruby:3.1-node
9
+ resource_class: large
10
+ environment:
11
+ CPLN_ORG: myorg
12
+ APP_NAME: myapp-staging
13
+ steps:
14
+ - checkout
15
+ - setup_remote_docker:
16
+ docker_layer_caching: true
17
+ - run:
18
+ name: Install Control Plane tools
19
+ command: |
20
+ sudo npm install -g @controlplane/cli && cpln --version
21
+ cpln profile create default --token ${CPLN_TOKEN} --org ${CPLN_ORG} --gvc ${APP_NAME}
22
+ cpln image docker-login
23
+
24
+ git clone https://github.com/shakacode/heroku-to-control-plane ~/heroku-to-control-plane
25
+ sudo ln -s ~/heroku-to-control-plane/cpl /usr/local/bin/cpl
26
+ - run:
27
+ name: Containerize and push image
28
+ command: cpl build-image -a ${APP_NAME}
29
+ - run:
30
+ name: Database tasks
31
+ command: cpl run:detached rails db:migrate -a ${APP_NAME} --image latest
32
+ - run:
33
+ name: Promote image
34
+ command: cpl promote-image -a ${APP_NAME}
35
+
36
+ # Example config for review app:
37
+ # - triggers manually if needed
38
+ # - dynamic app name based on PR number
39
+ # - resources provisioning for new apps
40
+ # - initial database setup or migration
41
+ build-review-app:
42
+ docker:
43
+ - image: cimg/ruby:3.1-node
44
+ resource_class: large
45
+ environment:
46
+ CPLN_ORG: my-org-name
47
+ steps:
48
+ - checkout
49
+ - setup_remote_docker:
50
+ docker_layer_caching: true
51
+ - run:
52
+ name: Setup environment
53
+ command: |
54
+ PR_NUM=$(echo $CIRCLE_PULL_REQUEST | grep -Eo '[0-9]+$')
55
+ echo "export APP_NAME=hichee-review-$PR_NUM" >> $BASH_ENV
56
+ - run:
57
+ name: Install Control Plane tools
58
+ command: |
59
+ sudo npm install -g @controlplane/cli && cpln --version
60
+ cpln profile create default --token ${CPLN_TOKEN} --org ${CPLN_ORG} --gvc ${APP_NAME}
61
+ cpln image docker-login
62
+
63
+ git clone https://github.com/shakacode/heroku-to-control-plane ~/heroku-to-control-plane
64
+ sudo ln -s ~/heroku-to-control-plane/cpl /usr/local/bin/cpl
65
+ - run:
66
+ name: Provision review app if needed
67
+ command: |
68
+ if ! cpl exist -a ${APP_NAME}; then
69
+ cpl setup gvc postgres redis memcached rails sidekiq -a ${APP_NAME}
70
+ echo "export NEW_APP=true" >> $BASH_ENV
71
+ fi
72
+ - run:
73
+ name: Containerize and push image
74
+ command: |
75
+ cpl build-image -a ${APP_NAME} --commit ${CIRCLE_SHA1::7}
76
+ - run:
77
+ name: Database tasks
78
+ command: |
79
+ if [ -n "${NEW_APP}" ]; then
80
+ cpl run:detached 'LOG_LEVEL=warn rails db:reset' -a ${APP_NAME} --image latest
81
+ else
82
+ cpl run:detached 'LOG_LEVEL=warn rails db:migrate' -a ${APP_NAME} --image latest
83
+ fi
84
+ - run:
85
+ name: Promote image
86
+ command: cpl promote-image -a ${APP_NAME}
87
+
88
+ review-app:
89
+ jobs:
90
+ - start:
91
+ filters:
92
+ branches:
93
+ ignore: master
94
+ type: approval
95
+ - build-review-app:
96
+ filters:
97
+ branches:
98
+ ignore: master
99
+ requires:
100
+ - start
101
+ staging:
102
+ jobs:
103
+ - build-staging:
104
+ filters:
105
+ branches:
106
+ only: master
@@ -0,0 +1,44 @@
1
+ aliases:
2
+ common: &common
3
+ # Org name for staging. (customize to your needs)
4
+ # Production apps will use a different Control Plane org, specified below, for security.
5
+ # keys beginning with CPLN correspond to your settings in Control Plane
6
+ cpln_org: my-org-staging
7
+
8
+ # Example apps use only location. CPLN offers the ability to use multiple locations.
9
+ # TODO -- allow specfication of multiple locations
10
+ default_location: aws-us-east-2
11
+
12
+ # Configure the workload name used as a template for one-off scripts, like a Heroku one-off dyno.
13
+ one_off_workload: rails
14
+
15
+ # Workloads that are application itself and are using application docker image
16
+ app_workloads:
17
+ - rails
18
+ - sidekiq
19
+
20
+ # Additional "service type" workloads, using non-application docker images
21
+ additional_workloads:
22
+ - redis
23
+ - postgres
24
+ - memcached
25
+
26
+ apps:
27
+ my-app-staging:
28
+ # Use the values from the common section above
29
+ <<: *common
30
+ my-app-review:
31
+ <<: *common
32
+ # if match_if_app_name_starts_with == true, then use this config for app names beginning,
33
+ # like my-app-review-pr123 or my-app-review-anything-goes
34
+ match_if_app_name_starts_with: true
35
+ my-app-production:
36
+ <<: *common
37
+ # Use a different org for production
38
+ cpln_org: my-org-production
39
+ # Allows running command 'cpl pipeline-promote my-app-staging' to promote the staging app to production
40
+ upstream: my-app-staging
41
+ my-app-other:
42
+ <<: *common
43
+ # you can specify different dockerfile relative to .controlplane folder, default is just 'Dockerfile'
44
+ dockerfile: ../some_other/Dockerfile
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class Base # rubocop:disable Metrics/ClassLength
5
+ attr_reader :config
6
+
7
+ # Used to call the command (`cpl NAME`)
8
+ # NAME = ""
9
+ # Displayed when running `cpl help` or `cpl help NAME` (defaults to `NAME`)
10
+ USAGE = ""
11
+ # Throws error if `true` and no arguments are passed to the command
12
+ # or if `false` and arguments are passed to the command
13
+ REQUIRES_ARGS = false
14
+ # Default arguments if none are passed to the command
15
+ DEFAULT_ARGS = [].freeze
16
+ # Options for the command (use option methods below)
17
+ OPTIONS = [].freeze
18
+ # Displayed when running `cpl help`
19
+ # DESCRIPTION = ""
20
+ # Displayed when running `cpl help NAME`
21
+ # LONG_DESCRIPTION = ""
22
+ # Displayed along with `LONG_DESCRIPTION` when running `cpl help NAME`
23
+ EXAMPLES = ""
24
+ # If `true`, hides the command from `cpl help`
25
+ HIDE = false
26
+
27
+ NO_IMAGE_AVAILABLE = "NO_IMAGE_AVAILABLE"
28
+
29
+ def initialize(config)
30
+ @config = config
31
+ end
32
+
33
+ def self.all_commands
34
+ Dir["#{__dir__}/*.rb"].each_with_object({}) do |file, result|
35
+ filename = File.basename(file, ".rb")
36
+ classname = File.read(file).match(/^\s+class (\w+) < Base($| .*$)/)&.captures&.first
37
+ result[filename.to_sym] = Object.const_get("::Command::#{classname}") if classname
38
+ end
39
+ end
40
+
41
+ def self.app_option(required: false)
42
+ {
43
+ name: :app,
44
+ params: {
45
+ aliases: ["-a"],
46
+ banner: "APP_NAME",
47
+ desc: "Application name",
48
+ type: :string,
49
+ required: required
50
+ }
51
+ }
52
+ end
53
+
54
+ def self.workload_option(required: false)
55
+ {
56
+ name: :workload,
57
+ params: {
58
+ aliases: ["-w"],
59
+ banner: "WORKLOAD_NAME",
60
+ desc: "Workload name",
61
+ type: :string,
62
+ required: required
63
+ }
64
+ }
65
+ end
66
+
67
+ def self.image_option(required: false)
68
+ {
69
+ name: :image,
70
+ params: {
71
+ aliases: ["-i"],
72
+ banner: "IMAGE_NAME",
73
+ desc: "Image name",
74
+ type: :string,
75
+ required: required
76
+ }
77
+ }
78
+ end
79
+
80
+ def self.commit_option(required: false)
81
+ {
82
+ name: :commit,
83
+ params: {
84
+ aliases: ["-c"],
85
+ banner: "COMMIT_HASH",
86
+ desc: "Commit hash",
87
+ type: :string,
88
+ required: required
89
+ }
90
+ }
91
+ end
92
+
93
+ def self.all_options
94
+ methods.grep(/_option$/).map { |method| send(method.to_s) }
95
+ end
96
+
97
+ def self.all_options_key_name
98
+ 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]
101
+ end
102
+ end
103
+
104
+ def wait_for(title)
105
+ progress.print "- Waiting for #{title}"
106
+ until yield
107
+ progress.print(".")
108
+ sleep(1)
109
+ end
110
+ progress.puts
111
+ end
112
+
113
+ def wait_for_workload(workload)
114
+ wait_for("workload to start") { cp.workload_get(workload) }
115
+ end
116
+
117
+ def wait_for_replica(workload, location)
118
+ wait_for("replica") do
119
+ cp.workload_get_replicas(workload, location: location)&.dig("items", 0)
120
+ end
121
+ end
122
+
123
+ def ensure_workload_deleted(workload)
124
+ progress.puts "- Ensure workload is deleted"
125
+ cp.workload_delete(workload, no_raise: true)
126
+ end
127
+
128
+ def latest_image # rubocop:disable Metrics/MethodLength
129
+ @latest_image ||=
130
+ begin
131
+ 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
142
+ end
143
+ end
144
+
145
+ def latest_image_next
146
+ @latest_image_next ||= begin
147
+ image = latest_image.split(":").first
148
+ image += ":#{extract_image_number(latest_image) + 1}"
149
+ image += "_#{config.options[:commit]}" if config.options[:commit]
150
+ image
151
+ end
152
+ end
153
+
154
+ # NOTE: use simplified variant atm, as shelljoin do different escaping
155
+ # TODO: most probably need better logic for escaping various quotes
156
+ def args_join(args)
157
+ args.join(" ")
158
+ end
159
+
160
+ def progress
161
+ $stderr
162
+ end
163
+
164
+ def cp
165
+ @cp ||= Controlplane.new(config)
166
+ end
167
+
168
+ private
169
+
170
+ # returns 0 if no prior image
171
+ def extract_image_number(image_name)
172
+ return 0 if image_name.end_with?(NO_IMAGE_AVAILABLE)
173
+
174
+ image_name.match(/:(\d+)/)&.captures&.first.to_i
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class BuildImage < Base
5
+ NAME = "build-image"
6
+ OPTIONS = [
7
+ app_option(required: true),
8
+ commit_option
9
+ ].freeze
10
+ DESCRIPTION = "Builds and pushes the image to Control Plane"
11
+ LONG_DESCRIPTION = <<~HEREDOC
12
+ - Builds and pushes the image to Control Plane
13
+ - Automatically assigns image numbers, e.g., `app:1`, `app:2`, etc.
14
+ - Uses `.controlplane/Dockerfile`
15
+ HEREDOC
16
+
17
+ def call
18
+ dockerfile = config.current[:dockerfile] || "Dockerfile"
19
+ dockerfile = "#{config.app_cpln_dir}/#{dockerfile}"
20
+ progress.puts "- Building dockerfile: #{dockerfile}"
21
+
22
+ cp.image_build(latest_image_next, dockerfile: dockerfile)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class Config < Base
5
+ NAME = "config"
6
+ OPTIONS = [
7
+ app_option
8
+ ].freeze
9
+ DESCRIPTION = "Displays current configs (global and app-specific)"
10
+ LONG_DESCRIPTION = <<~HEREDOC
11
+ - Displays current configs (global and app-specific)
12
+ HEREDOC
13
+ EXAMPLES = <<~HEREDOC
14
+ ```sh
15
+ # Shows the global config.
16
+ cpl config
17
+
18
+ # Shows both global and app-specific configs.
19
+ cpl config -a $APP_NAME
20
+ ```
21
+ HEREDOC
22
+
23
+ def call
24
+ puts "-- Options"
25
+ puts config.options.to_hash.to_yaml[4..]
26
+ puts
27
+
28
+ puts "-- Current config (app: #{config.app})"
29
+ puts config.app ? config.current.to_yaml[4..] : "Please specify app to get app config"
30
+ puts
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class Delete < Base
5
+ NAME = "delete"
6
+ OPTIONS = [
7
+ app_option(required: true)
8
+ ].freeze
9
+ DESCRIPTION = "Deletes the whole app (GVC with all workloads and all images)"
10
+ LONG_DESCRIPTION = <<~HEREDOC
11
+ - Deletes the whole app (GVC with all workloads and all images)
12
+ - Will ask for explicit user confirmation
13
+ HEREDOC
14
+
15
+ def call
16
+ progress.puts "Type 'delete' to delete #{config.app} and images"
17
+ progress.print "> "
18
+
19
+ return progress.puts "Not confirmed" unless $stdin.gets.chomp == "delete"
20
+
21
+ delete_gvc
22
+ delete_images
23
+ end
24
+
25
+ private
26
+
27
+ def delete_gvc
28
+ progress.puts "- Deleting gvc:"
29
+
30
+ return progress.puts "none" unless cp.gvc_get
31
+
32
+ cp.gvc_delete
33
+ progress.puts config.app
34
+ end
35
+
36
+ def delete_images
37
+ progress.puts "- Deleting image(s):"
38
+
39
+ images = cp.image_query["items"]
40
+ .filter_map { |item| item["name"] if item["name"].start_with?("#{config.app}:") }
41
+
42
+ return progress.puts "none" unless images
43
+
44
+ images.each do |image|
45
+ cp.image_delete(image)
46
+ progress.puts image
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class Env < Base
5
+ NAME = "env"
6
+ OPTIONS = [
7
+ app_option(required: true)
8
+ ].freeze
9
+ DESCRIPTION = "Displays app-specific environment variables"
10
+ LONG_DESCRIPTION = <<~HEREDOC
11
+ - Displays app-specific environment variables
12
+ HEREDOC
13
+
14
+ def call
15
+ cp.gvc_get.dig("spec", "env").map do |prop|
16
+ # NOTE: atm no special chars handling, consider adding if needed
17
+ puts "#{prop['name']}=#{prop['value']}"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class Exists < Base
5
+ NAME = "exists"
6
+ OPTIONS = [
7
+ app_option(required: true)
8
+ ].freeze
9
+ DESCRIPTION = "Shell-checks if an application (GVC) exists, useful in scripts"
10
+ LONG_DESCRIPTION = <<~HEREDOC
11
+ - Shell-checks if an application (GVC) exists, useful in scripts, e.g.:
12
+ HEREDOC
13
+ EXAMPLES = <<~HEREDOC
14
+ ```sh
15
+ if [ cpl exists -a $APP_NAME ]; ...
16
+ ```
17
+ HEREDOC
18
+
19
+ def call
20
+ exit(!cp.gvc_get.nil?)
21
+ end
22
+ end
23
+ end