cpl 1.1.2.rc.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/Gemfile.lock +1 -1
- data/README.md +63 -45
- data/docs/commands.md +9 -0
- data/lib/command/apply_template.rb +28 -53
- data/lib/command/base.rb +12 -0
- data/lib/command/config.rb +5 -0
- data/lib/command/copy_image_from_upstream.rb +5 -2
- data/lib/command/info.rb +5 -2
- data/lib/command/no_command.rb +5 -3
- data/lib/command/open_console.rb +26 -0
- data/lib/core/config.rb +20 -3
- data/lib/core/controlplane.rb +62 -22
- data/lib/core/controlplane_api_direct.rb +2 -0
- data/lib/core/shell.rb +13 -1
- data/lib/cpl/version.rb +1 -1
- data/lib/cpl.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 89a038d31fb497bfc6c0d34451ace68fe554d700f4686413a1919cf6d9bceb09
|
|
4
|
+
data.tar.gz: 3c733b43e703ad7b7798cf599b71f59ea1758ce2c71babd4486670c5ed5f12a3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 325eecc73176b362c4b898632e45040544ecfdda14ecfbbd655caf92d0912aa3e431fbd8ac90c2681d4a2bcff68db0a71f80813601c1cf3b3ca32d44c9ef033f
|
|
7
|
+
data.tar.gz: 0c0c04c6f1d4d57114d50d603fabfbd3470da4f373f48b091991e228306080f47bd3643e5c5c706fe9e3992e661b656b4634c881ee0861b79fbfa1f63a60d8e5
|
data/CHANGELOG.md
CHANGED
|
@@ -15,10 +15,21 @@ Changes since the last non-beta release.
|
|
|
15
15
|
_Please add entries here for your pull requests that are not yet released._
|
|
16
16
|
|
|
17
17
|
## [1.1.2] - 2023-10-17
|
|
18
|
+
|
|
18
19
|
### Fixed
|
|
19
20
|
|
|
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).
|
|
21
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
|
+
|
|
22
33
|
## [1.1.1] - 2023-09-23
|
|
23
34
|
|
|
24
35
|
### Fixed
|
|
@@ -84,7 +95,9 @@ _Please add entries here for your pull requests that are not yet released._
|
|
|
84
95
|
|
|
85
96
|
- Initial release
|
|
86
97
|
|
|
87
|
-
[Unreleased]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.1.
|
|
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
|
|
88
101
|
[1.1.0]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.0.4...v1.1.0
|
|
89
102
|
[1.0.4]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.0.3...v1.0.4
|
|
90
103
|
[1.0.3]: https://github.com/shakacode/heroku-to-control-plane/compare/v1.0.2...v1.0.3
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -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. [
|
|
33
|
-
6. [
|
|
34
|
-
7. [
|
|
35
|
-
8. [
|
|
36
|
-
9. [
|
|
37
|
-
10. [
|
|
38
|
-
11. [
|
|
39
|
-
12. [
|
|
40
|
-
13. [
|
|
41
|
-
14. [Migrating
|
|
42
|
-
15. [
|
|
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,23 +90,31 @@ For the typical Rails app, this means:
|
|
|
89
90
|
|
|
90
91
|
## Installation
|
|
91
92
|
|
|
92
|
-
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 value for `aliases.common.cpln_org` in `.controlplane/controlplane.yml
|
|
93
|
-
1. Install [Node.js](https://nodejs.org/en) (required for Control Plane CLI).
|
|
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
94
|
|
|
95
|
-
|
|
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)).
|
|
96
100
|
|
|
97
|
-
1. Install Control Plane CLI (and configure access) [docs here](https://docs.controlplane.com/quickstart/quick-start-3-cli#getting-started-with-the-cli), `npm install -g @controlplane/cli`. You can update the `cpln` command line with `npm update -g @controlplane/cli`. Then run `cpln login` to ensure access.
|
|
98
101
|
```sh
|
|
102
|
+
# Install CLI
|
|
99
103
|
npm install -g @controlplane/cli
|
|
104
|
+
|
|
105
|
+
# Configure access
|
|
100
106
|
cpln login
|
|
107
|
+
|
|
108
|
+
# Update CLI
|
|
109
|
+
npm update -g @controlplane/cli
|
|
101
110
|
```
|
|
102
111
|
|
|
103
|
-
|
|
112
|
+
5. Run `cpln image docker-login --org <your-org>` to ensure that you have access to the Control Plane Docker registry.
|
|
104
113
|
|
|
105
|
-
|
|
114
|
+
6. Install Heroku to Control Plane `cpl` CLI, either as a [Ruby gem](https://rubygems.org/gems/cpl) or a local clone.
|
|
106
115
|
For information on the latter, see [CONTRIBUTING.md](CONTRIBUTING.md). You may also install `cpl` in your project's Gemfile.
|
|
107
116
|
|
|
108
|
-
|
|
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.
|
|
109
118
|
|
|
110
119
|
**Note:** Do not confuse the `cpl` CLI with the `cpln` CLI. The `cpl` CLI is the Heroku to Control Plane playbook CLI.
|
|
111
120
|
The `cpln` CLI is the Control Plane CLI.
|
|
@@ -116,7 +125,7 @@ Click [here](/docs/migrating.md) to see the steps to migrate.
|
|
|
116
125
|
|
|
117
126
|
## Configuration Files
|
|
118
127
|
|
|
119
|
-
The `cpl` gem is based on several configuration files within a `/.controlplane` top-level directory in your
|
|
128
|
+
The `cpl` gem is based on several configuration files within a `/.controlplane` top-level directory in your project.
|
|
120
129
|
|
|
121
130
|
```
|
|
122
131
|
.controlplane/
|
|
@@ -129,17 +138,18 @@ The `cpl` gem is based on several configuration files within a `/.controlplane`
|
|
|
129
138
|
├─ entrypoint.sh
|
|
130
139
|
```
|
|
131
140
|
|
|
132
|
-
1. `controlplane.yml` describes the overall application. Be sure to have <your-org> as the value for `aliases.common.cpln_org
|
|
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.
|
|
133
142
|
2. `Dockerfile` builds the production application. `entrypoint.sh` is an _example_ entrypoint script for the production application, referenced in your Dockerfile.
|
|
134
143
|
3. `templates` directory contains the templates for the various workloads, such as `rails.yml` and `postgres.yml`.
|
|
135
|
-
4. `templates/gvc.yml` defines your project's GVC (like a Heroku app).
|
|
136
|
-
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
|
|
137
|
-
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.
|
|
138
|
-
7. You can have other files in the `templates` directory, such as `redis.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.
|
|
139
148
|
|
|
140
149
|
Here's a complete example of all supported config keys explained for the `controlplane.yml` file:
|
|
141
|
-
|
|
150
|
+
|
|
142
151
|
### `controlplane.yml`
|
|
152
|
+
|
|
143
153
|
```yaml
|
|
144
154
|
# Keys beginning with "cpln_" correspond to your settings in Control Plane.
|
|
145
155
|
|
|
@@ -234,58 +244,64 @@ apps:
|
|
|
234
244
|
|
|
235
245
|
## Workflow
|
|
236
246
|
|
|
237
|
-
For a live example, see the [react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/readme.md).
|
|
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
|
+
|
|
238
249
|
This example should closely match the below example.
|
|
239
250
|
|
|
240
251
|
Suppose your app is called `tutorial-app`. You can run the following commands.
|
|
241
252
|
|
|
242
253
|
### Setup Commands
|
|
254
|
+
|
|
243
255
|
```sh
|
|
244
256
|
# Provision all infrastructure on Control Plane.
|
|
245
|
-
#
|
|
257
|
+
# `tutorial-app` will be created per definition in .controlplane/controlplane.yml.
|
|
246
258
|
cpl apply-template gvc postgres redis rails -a tutorial-app
|
|
247
259
|
|
|
248
|
-
# Build and push
|
|
249
|
-
# Note, may take many minutes. Be patient.
|
|
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>`.
|
|
250
263
|
cpl build-image -a tutorial-app
|
|
251
264
|
|
|
252
|
-
# Promote image to app after running `cpl build-image command
|
|
253
|
-
# Note, the UX of images may not show the image for up to 5 minutes. However, it's ready.
|
|
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.
|
|
254
267
|
cpl deploy-image -a tutorial-app
|
|
255
268
|
|
|
256
|
-
# See how app is starting up
|
|
269
|
+
# See how the app is starting up.
|
|
257
270
|
cpl logs -a tutorial-app
|
|
258
271
|
|
|
259
|
-
# Open app in browser (once it has started up)
|
|
272
|
+
# Open the app in browser (once it has started up).
|
|
260
273
|
cpl open -a tutorial-app
|
|
261
274
|
```
|
|
262
275
|
|
|
263
|
-
### Promoting
|
|
276
|
+
### Promoting Code Updates
|
|
264
277
|
|
|
265
278
|
After committing code, you will update your deployment of `tutorial-app` with the following commands:
|
|
266
279
|
|
|
267
280
|
```sh
|
|
268
|
-
# Build and push new image with sequential image tagging, e.g. 'tutorial-app:1', then 'tutorial-app:2', etc.
|
|
281
|
+
# Build and push a new image with sequential image tagging, e.g. 'tutorial-app:1', then 'tutorial-app:2', etc.
|
|
269
282
|
cpl build-image -a tutorial-app
|
|
270
283
|
|
|
271
|
-
# Run database migrations (or other release tasks) with latest image,
|
|
272
|
-
# while app is still running on previous image.
|
|
284
|
+
# Run database migrations (or other release tasks) with the latest image,
|
|
285
|
+
# while the app is still running on the previous image.
|
|
273
286
|
# This is analogous to the release phase.
|
|
274
|
-
cpl
|
|
287
|
+
cpl run:detached rails db:migrate -a tutorial-app --image latest
|
|
275
288
|
|
|
276
|
-
# Pomote latest image to app
|
|
289
|
+
# Pomote the latest image to the app.
|
|
277
290
|
cpl deploy-image -a tutorial-app
|
|
278
291
|
```
|
|
279
292
|
|
|
280
293
|
If you needed to push a new image with a specific commit SHA, you can run the following command:
|
|
281
294
|
|
|
282
295
|
```sh
|
|
283
|
-
# Build and push with sequential image tagging and commit SHA, e.g. 'tutorial-app:123_ABCD'
|
|
296
|
+
# Build and push with sequential image tagging and commit SHA, e.g. 'tutorial-app:123_ABCD', etc.
|
|
284
297
|
cpl build-image -a tutorial-app --commit ABCD
|
|
285
298
|
```
|
|
286
299
|
|
|
287
300
|
### Real World
|
|
288
|
-
|
|
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.
|
|
289
305
|
|
|
290
306
|
## Environment
|
|
291
307
|
|
|
@@ -307,25 +323,27 @@ It is also possible to set up a Secret store (of type `Dictionary`), which we ca
|
|
|
307
323
|
Policy to access the secret.
|
|
308
324
|
|
|
309
325
|
In `templates/gvc.yml`:
|
|
326
|
+
|
|
310
327
|
```yaml
|
|
311
328
|
spec:
|
|
312
329
|
env:
|
|
313
330
|
- name: MY_GLOBAL_VAR
|
|
314
|
-
value:
|
|
331
|
+
value: "value"
|
|
315
332
|
- name: MY_SECRET_GLOBAL_VAR
|
|
316
|
-
value:
|
|
333
|
+
value: "cpln://secret/MY_SECRET_STORE_NAME/MY_SECRET_GLOBAL_VAR"
|
|
317
334
|
```
|
|
318
335
|
|
|
319
336
|
In `templates/rails.yml`:
|
|
337
|
+
|
|
320
338
|
```yaml
|
|
321
339
|
spec:
|
|
322
340
|
containers:
|
|
323
341
|
- name: rails
|
|
324
342
|
env:
|
|
325
343
|
- name: MY_LOCAL_VAR
|
|
326
|
-
value:
|
|
344
|
+
value: "value"
|
|
327
345
|
- name: MY_SECRET_LOCAL_VAR
|
|
328
|
-
value:
|
|
346
|
+
value: "cpln://secret/MY_SECRET_STORE_NAME/MY_SECRET_LOCAL_VAR"
|
|
329
347
|
inheritEnv: true # To enable global env inheritance.
|
|
330
348
|
```
|
|
331
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
|
-
@
|
|
42
|
-
@
|
|
43
|
-
@
|
|
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
|
|
62
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
print_skipped_workloads
|
|
72
|
+
print_created_items
|
|
73
|
+
print_failed_templates
|
|
74
|
+
print_skipped_templates
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
private
|
|
@@ -134,62 +134,37 @@ module Command
|
|
|
134
134
|
cp.apply_template(data)
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
-
def report_success(
|
|
138
|
-
|
|
139
|
-
@app_status = :success
|
|
140
|
-
else
|
|
141
|
-
@created_workloads.push(template)
|
|
142
|
-
end
|
|
137
|
+
def report_success(item)
|
|
138
|
+
@created_items.push(item)
|
|
143
139
|
end
|
|
144
140
|
|
|
145
141
|
def report_failure(template)
|
|
146
|
-
|
|
147
|
-
@app_status = :failure
|
|
148
|
-
else
|
|
149
|
-
@failed_workloads.push(template)
|
|
150
|
-
end
|
|
142
|
+
@failed_templates.push(template)
|
|
151
143
|
end
|
|
152
144
|
|
|
153
145
|
def report_skipped(template)
|
|
154
|
-
|
|
155
|
-
@app_status = :skipped
|
|
156
|
-
else
|
|
157
|
-
@skipped_workloads.push(template)
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
def print_app_status
|
|
162
|
-
return if @app_status == :existing
|
|
163
|
-
|
|
164
|
-
case @app_status
|
|
165
|
-
when :success
|
|
166
|
-
progress.puts("\n#{Shell.color("Created app '#{config.app}'.", :green)}")
|
|
167
|
-
when :failure
|
|
168
|
-
progress.puts("\n#{Shell.color("Failed to create app '#{config.app}'.", :red)}")
|
|
169
|
-
when :skipped
|
|
170
|
-
progress.puts("\n#{Shell.color("Skipped app '#{config.app}' (already exists).", :blue)}")
|
|
171
|
-
end
|
|
146
|
+
@skipped_templates.push(template)
|
|
172
147
|
end
|
|
173
148
|
|
|
174
|
-
def
|
|
175
|
-
return unless @
|
|
149
|
+
def print_created_items
|
|
150
|
+
return unless @created_items.any?
|
|
176
151
|
|
|
177
|
-
|
|
178
|
-
progress.puts("\n#{Shell.color('Created
|
|
152
|
+
created = @created_items.map { |item| " - [#{item[:kind]}] #{item[:name]}" }.join("\n")
|
|
153
|
+
progress.puts("\n#{Shell.color('Created items:', :green)}\n#{created}")
|
|
179
154
|
end
|
|
180
155
|
|
|
181
|
-
def
|
|
182
|
-
return unless @
|
|
156
|
+
def print_failed_templates
|
|
157
|
+
return unless @failed_templates.any?
|
|
183
158
|
|
|
184
|
-
|
|
185
|
-
progress.puts("\n#{Shell.color('Failed to
|
|
159
|
+
failed = @failed_templates.map { |template| " - #{template}" }.join("\n")
|
|
160
|
+
progress.puts("\n#{Shell.color('Failed to apply templates:', :red)}\n#{failed}")
|
|
186
161
|
end
|
|
187
162
|
|
|
188
|
-
def
|
|
189
|
-
return unless @
|
|
163
|
+
def print_skipped_templates
|
|
164
|
+
return unless @skipped_templates.any?
|
|
190
165
|
|
|
191
|
-
|
|
192
|
-
progress.puts("\n#{Shell.color('Skipped
|
|
166
|
+
skipped = @skipped_templates.map { |template| " - #{template}" }.join("\n")
|
|
167
|
+
progress.puts("\n#{Shell.color('Skipped templates (already exist):', :blue)}\n#{skipped}")
|
|
193
168
|
end
|
|
194
169
|
end
|
|
195
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
|
data/lib/command/config.rb
CHANGED
|
@@ -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
|
-
|
|
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)
|
|
97
|
+
result.push(org) if org && !result.include?(org)
|
|
95
98
|
end
|
|
96
99
|
end
|
|
97
100
|
|
data/lib/command/no_command.rb
CHANGED
|
@@ -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/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
|
data/lib/core/controlplane.rb
CHANGED
|
@@ -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.
|
|
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.
|
|
33
|
+
cmd += " > /dev/null" if Shell.should_hide_output?
|
|
34
34
|
perform!(cmd)
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -61,25 +61,25 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
|
61
61
|
|
|
62
62
|
def image_login(org_name = config.org)
|
|
63
63
|
cmd = "cpln image docker-login --org #{org_name}"
|
|
64
|
-
cmd += " > /dev/null 2>&1" if Shell.
|
|
64
|
+
cmd += " > /dev/null 2>&1" if Shell.should_hide_output?
|
|
65
65
|
perform!(cmd)
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def image_pull(image)
|
|
69
69
|
cmd = "docker pull #{image}"
|
|
70
|
-
cmd += " > /dev/null" if Shell.
|
|
70
|
+
cmd += " > /dev/null" if Shell.should_hide_output?
|
|
71
71
|
perform!(cmd)
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def image_tag(old_tag, new_tag)
|
|
75
75
|
cmd = "docker tag #{old_tag} #{new_tag}"
|
|
76
|
-
cmd += " > /dev/null" if Shell.
|
|
76
|
+
cmd += " > /dev/null" if Shell.should_hide_output?
|
|
77
77
|
perform!(cmd)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def image_push(image)
|
|
81
81
|
cmd = "docker push #{image}"
|
|
82
|
-
cmd += " > /dev/null" if Shell.
|
|
82
|
+
cmd += " > /dev/null" if Shell.should_hide_output?
|
|
83
83
|
perform!(cmd)
|
|
84
84
|
end
|
|
85
85
|
|
|
@@ -148,7 +148,11 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
|
148
148
|
end
|
|
149
149
|
|
|
150
150
|
def workload_get_replicas_safely(workload, location:)
|
|
151
|
-
cmd = "cpln workload get-replicas #{workload} #{gvc_org} --location #{location} -o yaml
|
|
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
|
+
|
|
152
156
|
result = `#{cmd}`
|
|
153
157
|
$CHILD_STATUS.success? ? YAML.safe_load(result) : nil
|
|
154
158
|
end
|
|
@@ -180,7 +184,7 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
|
180
184
|
def workload_set_image_ref(workload, container:, image:)
|
|
181
185
|
cmd = "cpln workload update #{workload} #{gvc_org}"
|
|
182
186
|
cmd += " --set spec.containers.#{container}.image=/org/#{config.org}/image/#{image}"
|
|
183
|
-
cmd += " > /dev/null" if Shell.
|
|
187
|
+
cmd += " > /dev/null" if Shell.should_hide_output?
|
|
184
188
|
perform!(cmd)
|
|
185
189
|
end
|
|
186
190
|
|
|
@@ -208,7 +212,7 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
|
208
212
|
|
|
209
213
|
def workload_force_redeployment(workload)
|
|
210
214
|
cmd = "cpln workload force-redeployment #{workload} #{gvc_org}"
|
|
211
|
-
cmd += " > /dev/null" if Shell.
|
|
215
|
+
cmd += " > /dev/null" if Shell.should_hide_output?
|
|
212
216
|
perform!(cmd)
|
|
213
217
|
end
|
|
214
218
|
|
|
@@ -280,41 +284,77 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
|
280
284
|
Tempfile.create do |f|
|
|
281
285
|
f.write(data)
|
|
282
286
|
f.rewind
|
|
283
|
-
cmd = "cpln apply #{gvc_org} --file #{f.path}
|
|
287
|
+
cmd = "cpln apply #{gvc_org} --file #{f.path}"
|
|
284
288
|
if Shell.tmp_stderr
|
|
285
|
-
cmd += " 2> #{Shell.tmp_stderr.path}"
|
|
286
|
-
|
|
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
|
|
287
295
|
else
|
|
288
|
-
|
|
296
|
+
Shell.debug("CMD", cmd)
|
|
297
|
+
|
|
298
|
+
result = `#{cmd}`
|
|
299
|
+
$CHILD_STATUS.success? ? parse_apply_result(result) : exit(false)
|
|
289
300
|
end
|
|
290
301
|
end
|
|
291
302
|
end
|
|
292
303
|
|
|
293
|
-
def apply_hash(data)
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
|
301
328
|
else
|
|
302
|
-
|
|
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 })
|
|
303
335
|
end
|
|
304
336
|
end
|
|
337
|
+
|
|
338
|
+
items
|
|
305
339
|
end
|
|
306
340
|
|
|
307
341
|
private
|
|
308
342
|
|
|
309
343
|
def perform(cmd)
|
|
344
|
+
Shell.debug("CMD", cmd)
|
|
345
|
+
|
|
310
346
|
system(cmd)
|
|
311
347
|
end
|
|
312
348
|
|
|
313
349
|
def perform!(cmd)
|
|
350
|
+
Shell.debug("CMD", cmd)
|
|
351
|
+
|
|
314
352
|
system(cmd) || exit(false)
|
|
315
353
|
end
|
|
316
354
|
|
|
317
355
|
def perform_yaml(cmd)
|
|
356
|
+
Shell.debug("CMD", cmd)
|
|
357
|
+
|
|
318
358
|
result = `#{cmd}`
|
|
319
359
|
$CHILD_STATUS.success? ? YAML.safe_load(result) : exit(false)
|
|
320
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
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.2
|
|
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-10-
|
|
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
|
|
@@ -303,9 +304,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
303
304
|
version: 2.7.0
|
|
304
305
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
305
306
|
requirements:
|
|
306
|
-
- - "
|
|
307
|
+
- - ">="
|
|
307
308
|
- !ruby/object:Gem::Version
|
|
308
|
-
version:
|
|
309
|
+
version: '0'
|
|
309
310
|
requirements: []
|
|
310
311
|
rubygems_version: 3.4.12
|
|
311
312
|
signing_key:
|