cpl 1.1.1 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea3952c523b5ee8b9815befa82df1ecaee53a323a37f081344660d27f3003371
4
- data.tar.gz: e346e1f6327ab009d0b801b651350355ad74cb9abbf9d69bb04fa46be00c4ea3
3
+ metadata.gz: 89a038d31fb497bfc6c0d34451ace68fe554d700f4686413a1919cf6d9bceb09
4
+ data.tar.gz: 3c733b43e703ad7b7798cf599b71f59ea1758ce2c71babd4486670c5ed5f12a3
5
5
  SHA512:
6
- metadata.gz: d1b0bfc22da8b9209720e8a262739dcbd738722713165370203e471f52e2aedd52087196dd5b476f05d0dc5f744cdbebe861c1840c2aa32f95339cdafa175807
7
- data.tar.gz: c067f243e3aabb72c271e02c7dd937355bf01e98645f4d163982143ad8a5e19cccdb19574dbe799e2eb7ac215a6f9e89db67f26e7e234252d227f5972b122331
6
+ metadata.gz: 325eecc73176b362c4b898632e45040544ecfdda14ecfbbd655caf92d0912aa3e431fbd8ac90c2681d4a2bcff68db0a71f80813601c1cf3b3ca32d44c9ef033f
7
+ data.tar.gz: 0c0c04c6f1d4d57114d50d603fabfbd3470da4f373f48b091991e228306080f47bd3643e5c5c706fe9e3992e661b656b4634c881ee0861b79fbfa1f63a60d8e5
data/CHANGELOG.md CHANGED
@@ -14,6 +14,24 @@ Changes since the last non-beta release.
14
14
 
15
15
  _Please add entries here for your pull requests that are not yet released._
16
16
 
17
+ ## [1.1.2] - 2023-10-17
18
+
19
+ ### Fixed
20
+
21
+ - Fixed failed build on MacOS by adding platform flag and fixed multiple files in yaml document for template. [PR 81](https://github.com/shakacode/heroku-to-control-plane/pull/81) by [justin808](https://github.com/justin808).
22
+
23
+ ### Added
24
+
25
+ - Added `open-console` command to open the app console on Control Plane. [PR 83](https://github.com/shakacode/heroku-to-control-plane/pull/83) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
26
+ - Added option to set the org with a `CPLN_ORG` env var. [PR 83](https://github.com/shakacode/heroku-to-control-plane/pull/83) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
27
+ - Added `--verbose` option to all commands for more detailed logs. [PR 83](https://github.com/shakacode/heroku-to-control-plane/pull/83) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
28
+
29
+ ### Changed
30
+
31
+ - Calling `cpl` with no command now shows the help menu. [PR 83](https://github.com/shakacode/heroku-to-control-plane/pull/83) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
32
+
33
+ ## [1.1.1] - 2023-09-23
34
+
17
35
  ### Fixed
18
36
 
19
37
  - Fixed issue where API token is not reset when switching profile. [PR 77](https://github.com/shakacode/heroku-to-control-plane/pull/77) by [Rafael Gomes](https://github.com/rafaelgomesxyz).
@@ -77,7 +95,9 @@ _Please add entries here for your pull requests that are not yet released._
77
95
 
78
96
  - Initial release
79
97
 
80
- [Unreleased]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.1.0...HEAD
98
+ [Unreleased]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.1.2...HEAD
99
+ [1.1.2]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.1.1...v1.1.2
100
+ [1.1.1]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.1.0...v1.1.1
81
101
  [1.1.0]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.0.4...v1.1.0
82
102
  [1.0.4]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.0.3...v1.0.4
83
103
  [1.0.3]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.0.2...v1.0.3
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cpl (1.1.1)
4
+ cpl (1.1.2)
5
5
  debug (~> 1.7.1)
6
6
  dotenv (~> 2.8.1)
7
7
  psych (~> 5.1.0)
@@ -25,7 +25,7 @@ GEM
25
25
  hashdiff (1.0.1)
26
26
  iniparse (1.5.0)
27
27
  io-console (0.6.0)
28
- irb (1.8.1)
28
+ irb (1.8.3)
29
29
  rdoc
30
30
  reline (>= 0.3.8)
31
31
  json (2.6.3)
@@ -36,7 +36,7 @@ GEM
36
36
  parallel (1.22.1)
37
37
  parser (3.2.0.0)
38
38
  ast (~> 2.4.1)
39
- psych (5.1.0)
39
+ psych (5.1.1.1)
40
40
  stringio
41
41
  public_suffix (5.0.1)
42
42
  rainbow (3.1.1)
@@ -44,7 +44,7 @@ GEM
44
44
  rdoc (6.5.0)
45
45
  psych (>= 4.0.0)
46
46
  regexp_parser (2.6.2)
47
- reline (0.3.8)
47
+ reline (0.3.9)
48
48
  io-console (~> 0.5)
49
49
  rexml (3.2.5)
50
50
  rspec (3.12.0)
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  <meta name="keywords" content="Control Plane, Heroku, Kubernetes, K8, Infrastructure">
7
7
  <meta name="google-site-verification" content="dIV4nMplcYl6YOKOaZMqgvdKXhLJ4cdYY6pS6e_YrPU" />
8
8
 
9
- _A playbook for migrating from [Heroku](https://heroku.com) to [Control Plane](https://controlplane.com)_
9
+ _A gem that provides **Heroku Flow** functionality on Control Plane, including docs for migrating from [Heroku](https://heroku.com) to [Control Plane](https://controlplane.com)_
10
10
 
11
11
  [![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)
12
12
  [![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)
@@ -29,17 +29,18 @@ a **helper CLI** based on templates to save lots of day-to-day typing (and human
29
29
  2. [Concept Mapping](#concept-mapping)
30
30
  3. [Installation](#installation)
31
31
  4. [Steps to Migrate](#steps-to-migrate)
32
- 5. [Config](#config)
33
- 6. [Environment](#environment)
34
- 7. [Database](#database)
35
- 8. [In-memory Databases](#in-memory-databases)
36
- 9. [Scheduled Jobs](#scheduled-jobs)
37
- 10. [CLI Commands Reference](#cli-commands-reference)
38
- 11. [Mapping of Heroku Commands to `cpl` and `cpln`](#mapping-of-heroku-commands-to-cpl-and-cpln)
39
- 12. [Examples](#examples)
40
- 13. [Migrating Postgres Database from Heroku Infrastructure](/docs/postgres.md)
41
- 14. [Migrating Redis Database from Heroku Infrastructure](/docs/redis.md)
42
- 15. [Tips](/docs/tips.md)
32
+ 5. [Configuration Files](#configuration-files)
33
+ 6. [Workflow](#workflow)
34
+ 7. [Environment](#environment)
35
+ 8. [Database](#database)
36
+ 9. [In-memory Databases](#in-memory-databases)
37
+ 10. [Scheduled Jobs](#scheduled-jobs)
38
+ 11. [CLI Commands Reference](#cli-commands-reference)
39
+ 12. [Mapping of Heroku Commands to `cpl` and `cpln`](#mapping-of-heroku-commands-to-cpl-and-cpln)
40
+ 13. [Examples](#examples)
41
+ 14. [Migrating Postgres Database from Heroku Infrastructure](/docs/postgres.md)
42
+ 15. [Migrating Redis Database from Heroku Infrastructure](/docs/redis.md)
43
+ 16. [Tips](/docs/tips.md)
43
44
 
44
45
  ## Key Features
45
46
 
@@ -89,21 +90,31 @@ For the typical Rails app, this means:
89
90
 
90
91
  ## Installation
91
92
 
92
- 1. Install [Node.js](https://nodejs.org/en) (required for Control Plane CLI).
93
- 2. Install [Ruby](https://www.ruby-lang.org/en/) (required for these helpers).
94
- 3. Install Control Plane CLI (adds `cpln` command) and configure credentials.
93
+ 1. Ensure your [Control Plane](https://controlplane.com) account is set up. Set up an `organization` <your-org> for testing in that account and modify the value for `aliases.common.cpln_org` in `.controlplane/controlplane.yml`, or you can also set it with the `CPLN_ORG` environment variable. If you need an organization, please [contact Shakacode](mailto:controlplane@shakacode.com).
94
+
95
+ 2. Install [Node.js](https://nodejs.org/en) (required for Control Plane CLI).
96
+
97
+ 3. Install [Ruby](https://www.ruby-lang.org/en/) (required for these helpers).
98
+
99
+ 4. Install Control Plane CLI, and configure access ([docs here](https://docs.controlplane.com/quickstart/quick-start-3-cli#getting-started-with-the-cli)).
95
100
 
96
101
  ```sh
102
+ # Install CLI
97
103
  npm install -g @controlplane/cli
104
+
105
+ # Configure access
98
106
  cpln login
107
+
108
+ # Update CLI
109
+ npm update -g @controlplane/cli
99
110
  ```
100
111
 
101
- 4. Install Heroku to Control Plane `cpl` CLI, either as a [Ruby gem](https://rubygems.org/gems/cpl) or a local clone.
102
- For information on the latter, see [CONTRIBUTING.md](CONTRIBUTING.md).
112
+ 5. Run `cpln image docker-login --org <your-org>` to ensure that you have access to the Control Plane Docker registry.
103
113
 
104
- ```sh
105
- gem install cpl
106
- ```
114
+ 6. Install Heroku to Control Plane `cpl` CLI, either as a [Ruby gem](https://rubygems.org/gems/cpl) or a local clone.
115
+ For information on the latter, see [CONTRIBUTING.md](CONTRIBUTING.md). You may also install `cpl` in your project's Gemfile.
116
+
117
+ 7. You can use [this Dockerfile](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/Dockerfile) as an example for your project. Ensure that you have Docker running.
107
118
 
108
119
  **Note:** Do not confuse the `cpl` CLI with the `cpln` CLI. The `cpl` CLI is the Heroku to Control Plane playbook CLI.
109
120
  The `cpln` CLI is the Control Plane CLI.
@@ -112,9 +123,32 @@ The `cpln` CLI is the Control Plane CLI.
112
123
 
113
124
  Click [here](/docs/migrating.md) to see the steps to migrate.
114
125
 
115
- ## Config
126
+ ## Configuration Files
127
+
128
+ The `cpl` gem is based on several configuration files within a `/.controlplane` top-level directory in your project.
129
+
130
+ ```
131
+ .controlplane/
132
+ ├─ templates/
133
+ │ ├─ gvc.yml
134
+ │ ├─ postgres.yml
135
+ │ ├─ rails.yml
136
+ ├─ controlplane.yml
137
+ ├─ Dockerfile
138
+ ├─ entrypoint.sh
139
+ ```
140
+
141
+ 1. `controlplane.yml` describes the overall application. Be sure to have <your-org> as the value for `aliases.common.cpln_org`, or set it with the `CPLN_ORG` environment variable.
142
+ 2. `Dockerfile` builds the production application. `entrypoint.sh` is an _example_ entrypoint script for the production application, referenced in your Dockerfile.
143
+ 3. `templates` directory contains the templates for the various workloads, such as `rails.yml` and `postgres.yml`.
144
+ 4. `templates/gvc.yml` defines your project's GVC (like a Heroku app). More importantly, it contains ENV values for the app.
145
+ 5. `templates/rails.yml` defines your Rails workload. It may inherit ENV values from the parent GVC, which is populated from the `templates/gvc.yml`. This file also configures scaling, sizing, firewalls, and other workload-specific values.
146
+ 6. For other workloads (like lines in a Heroku `Procfile`), you create additional template files. For example, you can base a `templates/sidekiq.yml` on the `templates/rails.yml` file.
147
+ 7. You can have other files in the `templates` directory, such as `redis.yml` and `postgres.yml`, which could setup Redis and Postgres for a testing application.
148
+
149
+ Here's a complete example of all supported config keys explained for the `controlplane.yml` file:
116
150
 
117
- Here's a complete example of all supported config keys explained:
151
+ ### `controlplane.yml`
118
152
 
119
153
  ```yaml
120
154
  # Keys beginning with "cpln_" correspond to your settings in Control Plane.
@@ -208,6 +242,67 @@ apps:
208
242
  dockerfile: ../some_other/Dockerfile
209
243
  ```
210
244
 
245
+ ## Workflow
246
+
247
+ For a live example, see the [react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/readme.md) repository.
248
+
249
+ This example should closely match the below example.
250
+
251
+ Suppose your app is called `tutorial-app`. You can run the following commands.
252
+
253
+ ### Setup Commands
254
+
255
+ ```sh
256
+ # Provision all infrastructure on Control Plane.
257
+ # `tutorial-app` will be created per definition in .controlplane/controlplane.yml.
258
+ cpl apply-template gvc postgres redis rails -a tutorial-app
259
+
260
+ # Build and push the Docker image to the Control Plane repository.
261
+ # Note, it may take many minutes. Be patient.
262
+ # Check for error messages, such as forgetting to run `cpln image docker-login --org <your-org>`.
263
+ cpl build-image -a tutorial-app
264
+
265
+ # Promote the image to the app after running the `cpl build-image` command.
266
+ # Note, the UX of the images may not show the image for up to 5 minutes. However, it's ready.
267
+ cpl deploy-image -a tutorial-app
268
+
269
+ # See how the app is starting up.
270
+ cpl logs -a tutorial-app
271
+
272
+ # Open the app in browser (once it has started up).
273
+ cpl open -a tutorial-app
274
+ ```
275
+
276
+ ### Promoting Code Updates
277
+
278
+ After committing code, you will update your deployment of `tutorial-app` with the following commands:
279
+
280
+ ```sh
281
+ # Build and push a new image with sequential image tagging, e.g. 'tutorial-app:1', then 'tutorial-app:2', etc.
282
+ cpl build-image -a tutorial-app
283
+
284
+ # Run database migrations (or other release tasks) with the latest image,
285
+ # while the app is still running on the previous image.
286
+ # This is analogous to the release phase.
287
+ cpl run:detached rails db:migrate -a tutorial-app --image latest
288
+
289
+ # Pomote the latest image to the app.
290
+ cpl deploy-image -a tutorial-app
291
+ ```
292
+
293
+ If you needed to push a new image with a specific commit SHA, you can run the following command:
294
+
295
+ ```sh
296
+ # Build and push with sequential image tagging and commit SHA, e.g. 'tutorial-app:123_ABCD', etc.
297
+ cpl build-image -a tutorial-app --commit ABCD
298
+ ```
299
+
300
+ ### Real World
301
+
302
+ Most companies will configure their CI system to handle the above steps. Please [contact Shakacode](mailto:controlplane@shakacode.com) for examples of how to do this.
303
+
304
+ You can also join our [**Slack channel**](https://reactrails.slack.com/join/shared_invite/enQtNjY3NTczMjczNzYxLTlmYjdiZmY3MTVlMzU2YWE0OWM0MzNiZDI0MzdkZGFiZTFkYTFkOGVjODBmOWEyYWQ3MzA2NGE1YWJjNmVlMGE) for ShakaCode open source projects.
305
+
211
306
  ## Environment
212
307
 
213
308
  There are two main places where we can set up environment variables in Control Plane:
@@ -227,24 +322,28 @@ It is also possible to set up a Secret store (of type `Dictionary`), which we ca
227
322
  `cpln://secret/MY_SECRET_STORE_NAME/MY_SECRET_VAR_NAME`. In such a case, we must set up an app Identity and proper
228
323
  Policy to access the secret.
229
324
 
325
+ In `templates/gvc.yml`:
326
+
230
327
  ```yaml
231
- # In `templates/gvc.yml`:
232
328
  spec:
233
329
  env:
234
330
  - name: MY_GLOBAL_VAR
235
- value: 'value'
331
+ value: "value"
236
332
  - name: MY_SECRET_GLOBAL_VAR
237
- value: 'cpln://secret/MY_SECRET_STORE_NAME/MY_SECRET_GLOBAL_VAR'
333
+ value: "cpln://secret/MY_SECRET_STORE_NAME/MY_SECRET_GLOBAL_VAR"
334
+ ```
238
335
 
239
- # In `templates/rails.yml`:
336
+ In `templates/rails.yml`:
337
+
338
+ ```yaml
240
339
  spec:
241
340
  containers:
242
341
  - name: rails
243
342
  env:
244
343
  - name: MY_LOCAL_VAR
245
- value: 'value'
344
+ value: "value"
246
345
  - name: MY_SECRET_LOCAL_VAR
247
- value: 'cpln://secret/MY_SECRET_STORE_NAME/MY_SECRET_LOCAL_VAR'
346
+ value: "cpln://secret/MY_SECRET_STORE_NAME/MY_SECRET_LOCAL_VAR"
248
347
  inheritEnv: true # To enable global env inheritance.
249
348
  ```
250
349
 
data/docs/commands.md CHANGED
@@ -227,6 +227,15 @@ cpl open -a $APP_NAME
227
227
  cpl open -a $APP_NAME -w $WORKLOAD_NAME
228
228
  ```
229
229
 
230
+ ### `open-console`
231
+
232
+ - Opens the app console on Control Plane in the default browser
233
+ - Can also go directly to a workload page if `--workload` is provided
234
+
235
+ ```sh
236
+ cpl open-console -a $APP_NAME
237
+ ```
238
+
230
239
  ### `promote-app-from-upstream`
231
240
 
232
241
  - Copies the latest image from upstream, runs a release script (optional), and deploys the image
@@ -35,13 +35,12 @@ module Command
35
35
  ```
36
36
  EX
37
37
 
38
- def call # rubocop:disable Metrics/MethodLength
38
+ def call # rubocop:disable Metrics/MethodLength, Metrics/PerceivedComplexity
39
39
  ensure_templates!
40
40
 
41
- @app_status = :existing
42
- @created_workloads = []
43
- @failed_workloads = []
44
- @skipped_workloads = []
41
+ @created_items = []
42
+ @failed_templates = []
43
+ @skipped_templates = []
45
44
 
46
45
  @asked_for_confirmation = false
47
46
 
@@ -57,9 +56,11 @@ module Command
57
56
 
58
57
  pending_templates.each do |template, filename|
59
58
  step("Applying template '#{template}'", abort_on_error: false) do
60
- apply_template(filename)
61
- if $CHILD_STATUS.success?
62
- report_success(template)
59
+ items = apply_template(filename)
60
+ if items
61
+ items.each do |item|
62
+ report_success(item)
63
+ end
63
64
  else
64
65
  report_failure(template)
65
66
  end
@@ -68,10 +69,9 @@ module Command
68
69
  end
69
70
  end
70
71
 
71
- print_app_status
72
- print_created_workloads
73
- print_failed_workloads
74
- print_skipped_workloads
72
+ print_created_items
73
+ print_failed_templates
74
+ print_skipped_templates
75
75
  end
76
76
 
77
77
  private
@@ -130,65 +130,41 @@ module Command
130
130
  .gsub("APP_ORG", config.org)
131
131
  .gsub("APP_IMAGE", latest_image)
132
132
 
133
- cp.apply(YAML.safe_load(data))
133
+ # Don't read in YAML.safe_load as that doesn't handle multiple documents
134
+ cp.apply_template(data)
134
135
  end
135
136
 
136
- def report_success(template)
137
- if template == "gvc"
138
- @app_status = :success
139
- else
140
- @created_workloads.push(template)
141
- end
137
+ def report_success(item)
138
+ @created_items.push(item)
142
139
  end
143
140
 
144
141
  def report_failure(template)
145
- if template == "gvc"
146
- @app_status = :failure
147
- else
148
- @failed_workloads.push(template)
149
- end
142
+ @failed_templates.push(template)
150
143
  end
151
144
 
152
145
  def report_skipped(template)
153
- if template == "gvc"
154
- @app_status = :skipped
155
- else
156
- @skipped_workloads.push(template)
157
- end
158
- end
159
-
160
- def print_app_status
161
- return if @app_status == :existing
162
-
163
- case @app_status
164
- when :success
165
- progress.puts("\n#{Shell.color("Created app '#{config.app}'.", :green)}")
166
- when :failure
167
- progress.puts("\n#{Shell.color("Failed to create app '#{config.app}'.", :red)}")
168
- when :skipped
169
- progress.puts("\n#{Shell.color("Skipped app '#{config.app}' (already exists).", :blue)}")
170
- end
146
+ @skipped_templates.push(template)
171
147
  end
172
148
 
173
- def print_created_workloads
174
- return unless @created_workloads.any?
149
+ def print_created_items
150
+ return unless @created_items.any?
175
151
 
176
- workloads = @created_workloads.map { |template| " - #{template}" }.join("\n")
177
- progress.puts("\n#{Shell.color('Created workloads:', :green)}\n#{workloads}")
152
+ created = @created_items.map { |item| " - [#{item[:kind]}] #{item[:name]}" }.join("\n")
153
+ progress.puts("\n#{Shell.color('Created items:', :green)}\n#{created}")
178
154
  end
179
155
 
180
- def print_failed_workloads
181
- return unless @failed_workloads.any?
156
+ def print_failed_templates
157
+ return unless @failed_templates.any?
182
158
 
183
- workloads = @failed_workloads.map { |template| " - #{template}" }.join("\n")
184
- progress.puts("\n#{Shell.color('Failed to create workloads:', :red)}\n#{workloads}")
159
+ failed = @failed_templates.map { |template| " - #{template}" }.join("\n")
160
+ progress.puts("\n#{Shell.color('Failed to apply templates:', :red)}\n#{failed}")
185
161
  end
186
162
 
187
- def print_skipped_workloads
188
- return unless @skipped_workloads.any?
163
+ def print_skipped_templates
164
+ return unless @skipped_templates.any?
189
165
 
190
- workloads = @skipped_workloads.map { |template| " - #{template}" }.join("\n")
191
- progress.puts("\n#{Shell.color('Skipped workloads (already exist):', :blue)}\n#{workloads}")
166
+ skipped = @skipped_templates.map { |template| " - #{template}" }.join("\n")
167
+ progress.puts("\n#{Shell.color('Skipped templates (already exist):', :blue)}\n#{skipped}")
192
168
  end
193
169
  end
194
170
  end
data/lib/command/base.rb CHANGED
@@ -176,6 +176,18 @@ module Command
176
176
  }
177
177
  end
178
178
 
179
+ def self.verbose_option(required: false)
180
+ {
181
+ name: :verbose,
182
+ params: {
183
+ aliases: ["-d"],
184
+ desc: "Shows detailed logs",
185
+ type: :boolean,
186
+ required: required
187
+ }
188
+ }
189
+ end
190
+
179
191
  def self.all_options
180
192
  methods.grep(/_option$/).map { |method| send(method.to_s) }
181
193
  end
@@ -21,6 +21,11 @@ module Command
21
21
  EX
22
22
 
23
23
  def call # rubocop:disable Metrics/MethodLength
24
+ if config.org_comes_from_env
25
+ puts Shell.color("Org comes from CPLN_ORG env var", :yellow)
26
+ puts
27
+ end
28
+
24
29
  if config.app
25
30
  puts "#{Shell.color("Current config (app '#{config.app}')", :blue)}:"
26
31
  puts pretty_print(config.current)
@@ -29,7 +29,7 @@ module Command
29
29
  ensure_docker_running!
30
30
 
31
31
  @upstream = config[:upstream]
32
- @upstream_org = config.apps[@upstream.to_sym][:cpln_org]
32
+ @upstream_org = config.apps[@upstream.to_sym][:cpln_org] || ENV.fetch("CPLN_ORG_UPSTREAM", nil)
33
33
  ensure_upstream_org!
34
34
 
35
35
  create_upstream_profile
@@ -51,7 +51,10 @@ module Command
51
51
  end
52
52
 
53
53
  def ensure_upstream_org!
54
- raise "Can't find option 'cpln_org' for app '#{@upstream}' in 'controlplane.yml'." unless @upstream_org
54
+ return if @upstream_org
55
+
56
+ raise "Can't find option 'cpln_org' for app '#{@upstream}' in 'controlplane.yml', " \
57
+ "and CPLN_ORG_UPSTREAM env var is not set."
55
58
  end
56
59
 
57
60
  def create_upstream_profile
data/lib/command/info.rb CHANGED
@@ -81,17 +81,20 @@ module Command
81
81
  end
82
82
  end
83
83
 
84
- def orgs # rubocop:disable Metrics/MethodLength
84
+ def orgs # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
85
85
  result = []
86
86
 
87
87
  if config.options[:org]
88
88
  result.push(config.options[:org])
89
89
  else
90
+ org_from_env = ENV.fetch("CPLN_ORG", nil)
91
+ result.push(org_from_env) if org_from_env
92
+
90
93
  config.apps.each do |app_name, app_options|
91
94
  next if config.app && !app_matches?(config.app, app_name, app_options)
92
95
 
93
96
  org = app_options[:cpln_org]
94
- result.push(org) unless result.include?(org)
97
+ result.push(org) if org && !result.include?(org)
95
98
  end
96
99
  end
97
100
 
@@ -11,9 +11,11 @@ module Command
11
11
  HIDE = true
12
12
 
13
13
  def call
14
- return unless config.options[:version]
15
-
16
- Cpl::Cli.start(["version"])
14
+ if config.options[:version]
15
+ Cpl::Cli.start(["version"])
16
+ else
17
+ Cpl::Cli.start(["help"])
18
+ end
17
19
  end
18
20
  end
19
21
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Command
4
+ class OpenConsole < Base
5
+ NAME = "open-console"
6
+ OPTIONS = [
7
+ app_option(required: true),
8
+ workload_option
9
+ ].freeze
10
+ DESCRIPTION = "Opens the app console on Control Plane in the default browser"
11
+ LONG_DESCRIPTION = <<~DESC
12
+ - Opens the app console on Control Plane in the default browser
13
+ - Can also go directly to a workload page if `--workload` is provided
14
+ DESC
15
+
16
+ def call
17
+ workload = config.options[:workload]
18
+ url = "https://console.cpln.io/console/org/#{config.org}/gvc/#{config.app}"
19
+ url += "/workload/#{workload}" if workload
20
+ url += "/-info"
21
+ opener = `which xdg-open open`.split("\n").grep_v("not found").first
22
+
23
+ exec %(#{opener} "#{url}")
24
+ end
25
+ end
26
+ end
data/lib/command/run.rb CHANGED
@@ -111,7 +111,7 @@ module Command
111
111
  end
112
112
 
113
113
  # Create workload clone
114
- cp.apply("kind" => "workload", "name" => one_off, "spec" => spec)
114
+ cp.apply_hash("kind" => "workload", "name" => one_off, "spec" => spec)
115
115
  end
116
116
 
117
117
  def runner_script # rubocop:disable Metrics/MethodLength
@@ -104,7 +104,7 @@ module Command
104
104
  container_spec["env"] << { "name" => "CONTROLPLANE_RUNNER", "value" => runner_script }
105
105
 
106
106
  # Create workload clone
107
- cp.apply("kind" => "workload", "name" => one_off, "spec" => spec)
107
+ cp.apply_hash("kind" => "workload", "name" => one_off, "spec" => spec)
108
108
  end
109
109
 
110
110
  def runner_script # rubocop:disable Metrics/MethodLength
data/lib/core/config.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Config
3
+ class Config # rubocop:disable Metrics/ClassLength
4
4
  attr_reader :config, :current,
5
- :org, :app, :apps, :app_dir,
5
+ :org, :org_comes_from_env, :app, :apps, :app_dir,
6
6
  # command line options
7
7
  :args, :options
8
8
 
@@ -12,10 +12,13 @@ class Config
12
12
  @args = args
13
13
  @options = options
14
14
  @org = options[:org]
15
+ @org_comes_from_env = false
15
16
  @app = options[:app]
16
17
 
17
18
  load_app_config
18
19
  load_apps
20
+
21
+ Shell.verbose_mode(options[:verbose])
19
22
  end
20
23
 
21
24
  def [](key)
@@ -48,6 +51,13 @@ class Config
48
51
  raise "Can't find app '#{app_name}' in 'controlplane.yml'." unless current
49
52
  end
50
53
 
54
+ def ensure_current_config_org!(app_name)
55
+ return if @org
56
+
57
+ raise "Can't find option 'cpln_org' for app '#{app_name}' in 'controlplane.yml', " \
58
+ "and CPLN_ORG env var is not set."
59
+ end
60
+
51
61
  def ensure_config!
52
62
  raise "'controlplane.yml' is empty." unless config
53
63
  end
@@ -66,8 +76,15 @@ class Config
66
76
 
67
77
  def pick_current_config(app_name, app_options)
68
78
  @current = app_options
69
- @org = self[:cpln_org]
70
79
  ensure_current_config_app!(app_name)
80
+
81
+ if current.key?(:cpln_org)
82
+ @org = current.fetch(:cpln_org)
83
+ else
84
+ @org = ENV.fetch("CPLN_ORG", nil)
85
+ @org_comes_from_env = true
86
+ end
87
+ ensure_current_config_org!(app_name)
71
88
  end
72
89
 
73
90
  def load_apps # rubocop:disable Metrics/MethodLength
@@ -24,13 +24,13 @@ class Controlplane # rubocop:disable Metrics/ClassLength
24
24
 
25
25
  def profile_create(profile, token)
26
26
  cmd = "cpln profile create #{profile} --token #{token}"
27
- cmd += " > /dev/null" if Shell.tmp_stderr
27
+ cmd += " > /dev/null" if Shell.should_hide_output?
28
28
  perform!(cmd)
29
29
  end
30
30
 
31
31
  def profile_delete(profile)
32
32
  cmd = "cpln profile delete #{profile}"
33
- cmd += " > /dev/null" if Shell.tmp_stderr
33
+ cmd += " > /dev/null" if Shell.should_hide_output?
34
34
  perform!(cmd)
35
35
  end
36
36
 
@@ -44,7 +44,10 @@ class Controlplane # rubocop:disable Metrics/ClassLength
44
44
  end
45
45
 
46
46
  def image_build(image, dockerfile:, build_args: [], push: true)
47
- cmd = "docker build -t #{image} -f #{dockerfile}"
47
+ # https://docs.controlplane.com/guides/push-image#step-2
48
+ # Might need to use `docker buildx build` if compatiblitity issues arise
49
+ cmd = "docker build --platform=linux/amd64 -t #{image} -f #{dockerfile}"
50
+
48
51
  build_args.each { |build_arg| cmd += " --build-arg #{build_arg}" }
49
52
  cmd += " #{config.app_dir}"
50
53
  perform!(cmd)
@@ -58,25 +61,25 @@ class Controlplane # rubocop:disable Metrics/ClassLength
58
61
 
59
62
  def image_login(org_name = config.org)
60
63
  cmd = "cpln image docker-login --org #{org_name}"
61
- cmd += " > /dev/null 2>&1" if Shell.tmp_stderr
64
+ cmd += " > /dev/null 2>&1" if Shell.should_hide_output?
62
65
  perform!(cmd)
63
66
  end
64
67
 
65
68
  def image_pull(image)
66
69
  cmd = "docker pull #{image}"
67
- cmd += " > /dev/null" if Shell.tmp_stderr
70
+ cmd += " > /dev/null" if Shell.should_hide_output?
68
71
  perform!(cmd)
69
72
  end
70
73
 
71
74
  def image_tag(old_tag, new_tag)
72
75
  cmd = "docker tag #{old_tag} #{new_tag}"
73
- cmd += " > /dev/null" if Shell.tmp_stderr
76
+ cmd += " > /dev/null" if Shell.should_hide_output?
74
77
  perform!(cmd)
75
78
  end
76
79
 
77
80
  def image_push(image)
78
81
  cmd = "docker push #{image}"
79
- cmd += " > /dev/null" if Shell.tmp_stderr
82
+ cmd += " > /dev/null" if Shell.should_hide_output?
80
83
  perform!(cmd)
81
84
  end
82
85
 
@@ -145,7 +148,11 @@ class Controlplane # rubocop:disable Metrics/ClassLength
145
148
  end
146
149
 
147
150
  def workload_get_replicas_safely(workload, location:)
148
- cmd = "cpln workload get-replicas #{workload} #{gvc_org} --location #{location} -o yaml 2> /dev/null"
151
+ cmd = "cpln workload get-replicas #{workload} #{gvc_org} --location #{location} -o yaml"
152
+ cmd += " 2> /dev/null" if Shell.should_hide_output?
153
+
154
+ Shell.debug("CMD", cmd)
155
+
149
156
  result = `#{cmd}`
150
157
  $CHILD_STATUS.success? ? YAML.safe_load(result) : nil
151
158
  end
@@ -177,7 +184,7 @@ class Controlplane # rubocop:disable Metrics/ClassLength
177
184
  def workload_set_image_ref(workload, container:, image:)
178
185
  cmd = "cpln workload update #{workload} #{gvc_org}"
179
186
  cmd += " --set spec.containers.#{container}.image=/org/#{config.org}/image/#{image}"
180
- cmd += " > /dev/null" if Shell.tmp_stderr
187
+ cmd += " > /dev/null" if Shell.should_hide_output?
181
188
  perform!(cmd)
182
189
  end
183
190
 
@@ -205,7 +212,7 @@ class Controlplane # rubocop:disable Metrics/ClassLength
205
212
 
206
213
  def workload_force_redeployment(workload)
207
214
  cmd = "cpln workload force-redeployment #{workload} #{gvc_org}"
208
- cmd += " > /dev/null" if Shell.tmp_stderr
215
+ cmd += " > /dev/null" if Shell.should_hide_output?
209
216
  perform!(cmd)
210
217
  end
211
218
 
@@ -273,32 +280,81 @@ class Controlplane # rubocop:disable Metrics/ClassLength
273
280
  end
274
281
 
275
282
  # apply
276
-
277
- def apply(data) # rubocop:disable Metrics/MethodLength
283
+ def apply_template(data) # rubocop:disable Metrics/MethodLength
278
284
  Tempfile.create do |f|
279
- f.write(data.to_yaml)
285
+ f.write(data)
280
286
  f.rewind
281
- cmd = "cpln apply #{gvc_org} --file #{f.path} > /dev/null"
287
+ cmd = "cpln apply #{gvc_org} --file #{f.path}"
282
288
  if Shell.tmp_stderr
283
- cmd += " 2> #{Shell.tmp_stderr.path}"
284
- perform(cmd)
289
+ cmd += " 2> #{Shell.tmp_stderr.path}" if Shell.should_hide_output?
290
+
291
+ Shell.debug("CMD", cmd)
292
+
293
+ result = `#{cmd}`
294
+ $CHILD_STATUS.success? ? parse_apply_result(result) : false
295
+ else
296
+ Shell.debug("CMD", cmd)
297
+
298
+ result = `#{cmd}`
299
+ $CHILD_STATUS.success? ? parse_apply_result(result) : exit(false)
300
+ end
301
+ end
302
+ end
303
+
304
+ def apply_hash(data)
305
+ apply_template(data.to_yaml)
306
+ end
307
+
308
+ def parse_apply_result(result) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
309
+ items = []
310
+
311
+ lines = result.split("\n")
312
+ lines.each do |line|
313
+ # The line can be in one of these formats:
314
+ # - "Created /org/shakacode-open-source-examples/gvc/my-app-staging"
315
+ # - "Created /org/shakacode-open-source-examples/gvc/my-app-staging/workload/redis"
316
+ # - "Updated gvc 'tutorial-app-test-1'"
317
+ # - "Updated workload 'redis'"
318
+ if line.start_with?("Created")
319
+ matches = line.match(%r{Created\s/org/[^/]+/gvc/([^/]+)($|(/([^/]+)/([^/]+)$))})&.captures
320
+ next unless matches
321
+
322
+ app, _, __, kind, name = matches
323
+ if kind
324
+ items.push({ kind: kind, name: name })
325
+ else
326
+ items.push({ kind: "app", name: app })
327
+ end
285
328
  else
286
- perform!(cmd)
329
+ matches = line.match(/Updated\s([^\s]+)\s'([^\s]+)'$/)&.captures
330
+ next unless matches
331
+
332
+ kind, name = matches
333
+ kind = "app" if kind == "gvc"
334
+ items.push({ kind: kind, name: name })
287
335
  end
288
336
  end
337
+
338
+ items
289
339
  end
290
340
 
291
341
  private
292
342
 
293
343
  def perform(cmd)
344
+ Shell.debug("CMD", cmd)
345
+
294
346
  system(cmd)
295
347
  end
296
348
 
297
349
  def perform!(cmd)
350
+ Shell.debug("CMD", cmd)
351
+
298
352
  system(cmd) || exit(false)
299
353
  end
300
354
 
301
355
  def perform_yaml(cmd)
356
+ Shell.debug("CMD", cmd)
357
+
302
358
  result = `#{cmd}`
303
359
  $CHILD_STATUS.success? ? YAML.safe_load(result) : exit(false)
304
360
  end
@@ -24,6 +24,8 @@ class ControlplaneApiDirect
24
24
  request["Authorization"] = api_token
25
25
  request.body = body.to_json if body
26
26
 
27
+ Shell.debug(method.upcase, "#{uri} #{body&.to_json}")
28
+
27
29
  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(request) }
28
30
 
29
31
  case response
data/lib/core/shell.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  class Shell
4
4
  class << self
5
- attr_reader :tmp_stderr
5
+ attr_reader :tmp_stderr, :verbose
6
6
  end
7
7
 
8
8
  def self.shell
@@ -50,4 +50,16 @@ class Shell
50
50
  def self.abort(message)
51
51
  Kernel.abort(color("ERROR: #{message}", :red))
52
52
  end
53
+
54
+ def self.verbose_mode(verbose)
55
+ @verbose = verbose
56
+ end
57
+
58
+ def self.debug(prefix, message)
59
+ stderr.puts("\n[#{color(prefix, :red)}] #{message}") if verbose
60
+ end
61
+
62
+ def self.should_hide_output?
63
+ tmp_stderr && !verbose
64
+ end
53
65
  end
data/lib/cpl/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cpl
4
- VERSION = "1.1.1"
4
+ VERSION = "1.1.2"
5
5
  MIN_CPLN_VERSION = "0.0.71"
6
6
  end
data/lib/cpl.rb CHANGED
@@ -141,7 +141,7 @@ module Cpl
141
141
  usage = command_class::USAGE.empty? ? name : command_class::USAGE
142
142
  requires_args = command_class::REQUIRES_ARGS
143
143
  default_args = command_class::DEFAULT_ARGS
144
- command_options = command_class::OPTIONS
144
+ command_options = command_class::OPTIONS + [::Command::Base.verbose_option]
145
145
  description = command_class::DESCRIPTION
146
146
  long_description = command_class::LONG_DESCRIPTION
147
147
  examples = command_class::EXAMPLES
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cpl
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Gordon
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-09-21 00:00:00.000000000 Z
12
+ date: 2023-10-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: debug
@@ -251,6 +251,7 @@ files:
251
251
  - lib/command/maintenance_set_page.rb
252
252
  - lib/command/no_command.rb
253
253
  - lib/command/open.rb
254
+ - lib/command/open_console.rb
254
255
  - lib/command/promote_app_from_upstream.rb
255
256
  - lib/command/ps.rb
256
257
  - lib/command/ps_restart.rb