cpl 0.4.1 → 0.5.1
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/.overcommit.yml +10 -0
- data/Gemfile.lock +10 -3
- data/README.md +6 -0
- data/cpl.gemspec +1 -0
- data/docs/commands.md +51 -3
- data/googlee2da545df05d92f9.html +1 -0
- data/lib/command/base.rb +65 -7
- data/lib/command/build_image.rb +6 -5
- data/lib/command/cleanup_old_images.rb +8 -7
- data/lib/command/cleanup_stale_apps.rb +11 -9
- data/lib/command/config.rb +30 -15
- data/lib/command/copy_image_from_upstream.rb +110 -0
- data/lib/command/delete.rb +10 -12
- data/lib/command/deploy_image.rb +18 -7
- data/lib/command/env.rb +2 -2
- data/lib/command/exists.rb +4 -4
- data/lib/command/info.rb +233 -0
- data/lib/command/latest_image.rb +2 -2
- data/lib/command/logs.rb +4 -4
- data/lib/command/no_command.rb +3 -3
- data/lib/command/open.rb +4 -4
- data/lib/command/promote_app_from_upstream.rb +58 -0
- data/lib/command/ps.rb +10 -13
- data/lib/command/ps_restart.rb +9 -6
- data/lib/command/ps_start.rb +7 -6
- data/lib/command/ps_stop.rb +7 -6
- data/lib/command/run.rb +5 -5
- data/lib/command/run_detached.rb +7 -5
- data/lib/command/setup.rb +71 -13
- data/lib/command/test.rb +2 -2
- data/lib/command/version.rb +2 -2
- data/lib/core/config.rb +26 -19
- data/lib/core/controlplane.rb +77 -11
- data/lib/core/controlplane_api.rb +12 -0
- data/lib/core/controlplane_api_cli.rb +1 -1
- data/lib/core/controlplane_api_direct.rb +2 -2
- data/lib/core/shell.rb +25 -3
- data/lib/cpl/version.rb +1 -1
- data/lib/cpl.rb +19 -10
- data/lib/deprecated_commands.json +6 -0
- data/script/add_command +37 -0
- data/script/generate_commands_docs +5 -5
- data/script/rename_command +43 -0
- metadata +24 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5989a3c887eb311dcfb8bffe88e8519172b21efd21e715c363e7a166b73dbc3
|
4
|
+
data.tar.gz: b9fc9896cdd99a38b89389866f0f0825cb3944099e4579f4641ee221f2302a92
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c73027a84228c2371999bdff122d3bab5a3970d5f11c54f55a4b47a0fae77f83903e8d5702ecdd6baa5ac9fe6346f8e433f395a6fc3e22ee3a7225ed661038c3
|
7
|
+
data.tar.gz: a2734ae8f9969be0d523dd547783284774ad461fa1dee36174a5c6bc6b653ec7a020c2c07301e31c787d5d4f7b04c4a6d983fe1e544d2d20410a54f19896d7ff
|
data/.overcommit.yml
ADDED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
cpl (0.
|
4
|
+
cpl (0.5.1)
|
5
5
|
debug (~> 1.7.1)
|
6
6
|
dotenv (~> 2.8.1)
|
7
7
|
psych (~> 5.1.0)
|
@@ -11,16 +11,22 @@ GEM
|
|
11
11
|
remote: https://rubygems.org/
|
12
12
|
specs:
|
13
13
|
ast (2.4.2)
|
14
|
-
|
14
|
+
childprocess (4.1.0)
|
15
|
+
debug (1.7.2)
|
15
16
|
irb (>= 1.5.0)
|
16
17
|
reline (>= 0.3.1)
|
17
18
|
diff-lcs (1.5.0)
|
18
19
|
docile (1.4.0)
|
19
20
|
dotenv (2.8.1)
|
21
|
+
iniparse (1.5.0)
|
20
22
|
io-console (0.6.0)
|
21
23
|
irb (1.6.3)
|
22
24
|
reline (>= 0.3.0)
|
23
25
|
json (2.6.3)
|
26
|
+
overcommit (0.60.0)
|
27
|
+
childprocess (>= 0.6.3, < 5)
|
28
|
+
iniparse (~> 1.4)
|
29
|
+
rexml (~> 3.2)
|
24
30
|
parallel (1.22.1)
|
25
31
|
parser (3.2.0.0)
|
26
32
|
ast (~> 2.4.1)
|
@@ -29,7 +35,7 @@ GEM
|
|
29
35
|
rainbow (3.1.1)
|
30
36
|
rake (13.0.6)
|
31
37
|
regexp_parser (2.6.2)
|
32
|
-
reline (0.3.
|
38
|
+
reline (0.3.3)
|
33
39
|
io-console (~> 0.5)
|
34
40
|
rexml (3.2.5)
|
35
41
|
rspec (3.12.0)
|
@@ -81,6 +87,7 @@ PLATFORMS
|
|
81
87
|
|
82
88
|
DEPENDENCIES
|
83
89
|
cpl!
|
90
|
+
overcommit (~> 0.60.0)
|
84
91
|
rake (~> 13.0)
|
85
92
|
rspec (~> 3.12.0)
|
86
93
|
rubocop (~> 1.45.0)
|
data/README.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Heroku to Control Plane `cpl` CLI
|
2
2
|
|
3
|
+
<meta name="author" content="Justin Gordon and Sergey Tarasov">
|
4
|
+
<meta name="description" content="Instructions on how to migrate from Heroku to Control Plane and a CLI called cpl to make it easier.">
|
5
|
+
<meta name="copyright" content="ShakaCode, 2023">
|
6
|
+
<meta name="keywords" content="Control Plane, Heroku, Kubernetes, K8, Infrastructure">
|
7
|
+
<meta name="google-site-verification" content="dIV4nMplcYl6YOKOaZMqgvdKXhLJ4cdYY6pS6e_YrPU" />
|
8
|
+
|
3
9
|
_A playbook for migrating from [Heroku](https://heroku.com) to [Control Plane](https://controlplane.com)_
|
4
10
|
|
5
11
|
[](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rspec.yml)
|
data/cpl.gemspec
CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.add_dependency "psych", "~> 5.1.0"
|
21
21
|
spec.add_dependency "thor", "~> 1.2.1"
|
22
22
|
|
23
|
+
spec.add_development_dependency "overcommit", "~> 0.60.0"
|
23
24
|
spec.add_development_dependency "rspec", "~> 3.12.0"
|
24
25
|
spec.add_development_dependency "rubocop", "~> 1.45.0"
|
25
26
|
spec.add_development_dependency "rubocop-rake", "~> 0.6.0"
|
data/docs/commands.md
CHANGED
@@ -46,16 +46,31 @@ cpl cleanup-stale-apps -a $APP_NAME
|
|
46
46
|
|
47
47
|
### `config`
|
48
48
|
|
49
|
-
- Displays
|
49
|
+
- Displays config for each app or a specific app
|
50
50
|
|
51
51
|
```sh
|
52
|
-
# Shows the
|
52
|
+
# Shows the config for each app.
|
53
53
|
cpl config
|
54
54
|
|
55
|
-
# Shows
|
55
|
+
# Shows the config for a specific app.
|
56
56
|
cpl config -a $APP_NAME
|
57
57
|
```
|
58
58
|
|
59
|
+
### `copy-image-from-upstream`
|
60
|
+
|
61
|
+
- Copies an image (by default the latest) from a source org to the current org
|
62
|
+
- The source org must be specified through `upstream` in the `.controlplane/controlplane.yml` file
|
63
|
+
- Additionally, the token for the source org must be provided through `--upstream-token` or `-t`
|
64
|
+
- A `cpln` profile will be temporarily created to pull the image from the source org
|
65
|
+
|
66
|
+
```sh
|
67
|
+
# Copies the latest image from the source org to the current org.
|
68
|
+
cpl copy-image-from-upstream -a $APP_NAME --upstream-token $UPSTREAM_TOKEN
|
69
|
+
|
70
|
+
# Copies a specific image from the source org to the current org.
|
71
|
+
cpl copy-image-from-upstream -a $APP_NAME --upstream-token $UPSTREAM_TOKEN --image appimage:123
|
72
|
+
```
|
73
|
+
|
59
74
|
### `delete`
|
60
75
|
|
61
76
|
- Deletes the whole app (GVC with all workloads and all images)
|
@@ -89,6 +104,25 @@ cpl env -a $APP_NAME
|
|
89
104
|
if [ cpl exists -a $APP_NAME ]; ...
|
90
105
|
```
|
91
106
|
|
107
|
+
### `info`
|
108
|
+
|
109
|
+
- Displays the diff between defined/available apps/workloads (apps equal GVCs)
|
110
|
+
- Apps that are defined but not available are displayed in red
|
111
|
+
- Apps that are available but not defined are displayed in green
|
112
|
+
- Apps that are both defined and available are displayed in white
|
113
|
+
- The diff is based on what's defined in the `.controlplane/controlplane.yml` file
|
114
|
+
|
115
|
+
```sh
|
116
|
+
# Shows diff for all apps in all orgs.
|
117
|
+
cpl info
|
118
|
+
|
119
|
+
# Shows diff for all apps in a specific org.
|
120
|
+
cpl info -o $ORG_NAME
|
121
|
+
|
122
|
+
# Shows diff for a specific app.
|
123
|
+
cpl info -a $APP_NAME
|
124
|
+
```
|
125
|
+
|
92
126
|
### `latest-image`
|
93
127
|
|
94
128
|
- Displays the latest image name
|
@@ -121,6 +155,18 @@ cpl open -a $APP_NAME
|
|
121
155
|
cpl open -a $APP_NAME -w $WORKLOAD_NAME
|
122
156
|
```
|
123
157
|
|
158
|
+
### `promote-app-from-upstream`
|
159
|
+
|
160
|
+
- Copies the latest image from upstream, runs a release script (optional), and deploys the image
|
161
|
+
- It performs the following steps:
|
162
|
+
- Runs `cpl copy-image-from-upstream` to copy the latest image from upstream
|
163
|
+
- Runs a release script if specified through `release_script` in the `.controlplane/controlplane.yml` file
|
164
|
+
- Runs `cpl deploy-image` to deploy the image
|
165
|
+
|
166
|
+
```sh
|
167
|
+
cpl promote-app-from-upstream -a $APP_NAME -t $UPSTREAM_TOKEN
|
168
|
+
```
|
169
|
+
|
124
170
|
### `ps`
|
125
171
|
|
126
172
|
- Shows running replicas in app
|
@@ -204,6 +250,8 @@ cpl run rails db:migrate -a $APP_NAME --image latest # Latest sequential i
|
|
204
250
|
|
205
251
|
```sh
|
206
252
|
cpl run:detached rails db:prepare -a $APP_NAME
|
253
|
+
|
254
|
+
# Need to quote COMMAND if setting ENV value or passing args to command to run
|
207
255
|
cpl run:detached 'LOG_LEVEL=warn rails db:migrate' -a $APP_NAME
|
208
256
|
|
209
257
|
# Uses some other image.
|
@@ -0,0 +1 @@
|
|
1
|
+
google-site-verification: googlee2da545df05d92f9.html
|
data/lib/command/base.rb
CHANGED
@@ -38,6 +38,19 @@ module Command
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
def self.org_option(required: false)
|
42
|
+
{
|
43
|
+
name: :org,
|
44
|
+
params: {
|
45
|
+
aliases: ["-o"],
|
46
|
+
banner: "ORG_NAME",
|
47
|
+
desc: "Organization name",
|
48
|
+
type: :string,
|
49
|
+
required: required
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
41
54
|
def self.app_option(required: false)
|
42
55
|
{
|
43
56
|
name: :app,
|
@@ -90,6 +103,19 @@ module Command
|
|
90
103
|
}
|
91
104
|
end
|
92
105
|
|
106
|
+
def self.upstream_token_option(required: false)
|
107
|
+
{
|
108
|
+
name: :upstream_token,
|
109
|
+
params: {
|
110
|
+
aliases: ["-t"],
|
111
|
+
banner: "UPSTREAM_TOKEN",
|
112
|
+
desc: "Upstream token",
|
113
|
+
type: :string,
|
114
|
+
required: required
|
115
|
+
}
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
93
119
|
def self.skip_confirm_option(required: false)
|
94
120
|
{
|
95
121
|
name: :yes,
|
@@ -163,17 +189,19 @@ module Command
|
|
163
189
|
end
|
164
190
|
end
|
165
191
|
|
166
|
-
def latest_image
|
167
|
-
@latest_image ||=
|
192
|
+
def latest_image(app = config.app, org = config.org)
|
193
|
+
@latest_image ||= {}
|
194
|
+
@latest_image[app] ||=
|
168
195
|
begin
|
169
|
-
items = cp.image_query["items"]
|
170
|
-
latest_image_from(items)
|
196
|
+
items = cp.image_query(app, org)["items"]
|
197
|
+
latest_image_from(items, app_name: app)
|
171
198
|
end
|
172
199
|
end
|
173
200
|
|
174
|
-
def latest_image_next
|
175
|
-
@latest_image_next ||=
|
176
|
-
|
201
|
+
def latest_image_next(app = config.app, org = config.org)
|
202
|
+
@latest_image_next ||= {}
|
203
|
+
@latest_image_next[app] ||= begin
|
204
|
+
image = latest_image(app, org).split(":").first
|
177
205
|
image += ":#{extract_image_number(latest_image) + 1}"
|
178
206
|
image += "_#{config.options[:commit]}" if config.options[:commit]
|
179
207
|
image
|
@@ -190,10 +218,40 @@ module Command
|
|
190
218
|
$stderr
|
191
219
|
end
|
192
220
|
|
221
|
+
def step(message, abort_on_error: true) # rubocop:disable Metrics/MethodLength
|
222
|
+
progress.print("#{message}...")
|
223
|
+
|
224
|
+
Shell.use_tmp_stderr do
|
225
|
+
success = false
|
226
|
+
|
227
|
+
begin
|
228
|
+
success = yield
|
229
|
+
rescue RuntimeError => e
|
230
|
+
message = e.message
|
231
|
+
if abort_on_error
|
232
|
+
progress.puts(" #{Shell.color('failed!', :red)}\n\n")
|
233
|
+
Shell.abort(message)
|
234
|
+
else
|
235
|
+
Shell.write_to_tmp_stderr(message)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
if success
|
240
|
+
progress.puts(" #{Shell.color('done!', :green)}")
|
241
|
+
else
|
242
|
+
progress.puts(" #{Shell.color('failed!', :red)}\n\n#{Shell.read_from_tmp_stderr}\n\n")
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
193
247
|
def cp
|
194
248
|
@cp ||= Controlplane.new(config)
|
195
249
|
end
|
196
250
|
|
251
|
+
def perform(cmd)
|
252
|
+
system(cmd) || exit(false)
|
253
|
+
end
|
254
|
+
|
197
255
|
private
|
198
256
|
|
199
257
|
# returns 0 if no prior image
|
data/lib/command/build_image.rb
CHANGED
@@ -8,18 +8,19 @@ module Command
|
|
8
8
|
commit_option
|
9
9
|
].freeze
|
10
10
|
DESCRIPTION = "Builds and pushes the image to Control Plane"
|
11
|
-
LONG_DESCRIPTION = <<~
|
11
|
+
LONG_DESCRIPTION = <<~DESC
|
12
12
|
- Builds and pushes the image to Control Plane
|
13
13
|
- Automatically assigns image numbers, e.g., `app:1`, `app:2`, etc.
|
14
14
|
- Uses `.controlplane/Dockerfile`
|
15
|
-
|
15
|
+
DESC
|
16
16
|
|
17
17
|
def call
|
18
18
|
ensure_docker_running!
|
19
19
|
|
20
20
|
dockerfile = config.current[:dockerfile] || "Dockerfile"
|
21
21
|
dockerfile = "#{config.app_cpln_dir}/#{dockerfile}"
|
22
|
-
|
22
|
+
|
23
|
+
progress.puts("Building image from Dockerfile '#{dockerfile}'...\n\n")
|
23
24
|
|
24
25
|
cp.image_build(latest_image_next, dockerfile: dockerfile)
|
25
26
|
end
|
@@ -28,9 +29,9 @@ module Command
|
|
28
29
|
|
29
30
|
def ensure_docker_running!
|
30
31
|
`docker version > /dev/null 2>&1`
|
31
|
-
return if
|
32
|
+
return if $CHILD_STATUS.success?
|
32
33
|
|
33
|
-
|
34
|
+
raise "Can't run Docker. Please make sure that it's installed and started, then try again."
|
34
35
|
end
|
35
36
|
end
|
36
37
|
end
|
@@ -8,19 +8,19 @@ module Command
|
|
8
8
|
skip_confirm_option
|
9
9
|
].freeze
|
10
10
|
DESCRIPTION = "Deletes all images for an app that are older than the specified amount of days"
|
11
|
-
LONG_DESCRIPTION = <<~
|
11
|
+
LONG_DESCRIPTION = <<~DESC
|
12
12
|
- Deletes all images for an app that are older than the specified amount of days
|
13
13
|
- Specify the amount of days through `old_image_retention_days` in the `.controlplane/controlplane.yml` file
|
14
14
|
- Will ask for explicit user confirmation
|
15
15
|
- Does not affect the latest image, regardless of how old it is
|
16
|
-
|
16
|
+
DESC
|
17
17
|
|
18
18
|
def call
|
19
|
-
return progress.puts
|
19
|
+
return progress.puts("No old images found.") if old_images.empty?
|
20
20
|
|
21
|
-
progress.puts
|
21
|
+
progress.puts("Old images:")
|
22
22
|
old_images.each do |image|
|
23
|
-
progress.puts
|
23
|
+
progress.puts(" - #{image[:name]} (#{Shell.color((image[:date]).to_s, :red)})")
|
24
24
|
end
|
25
25
|
|
26
26
|
return unless confirm_delete
|
@@ -68,8 +68,9 @@ module Command
|
|
68
68
|
|
69
69
|
def delete_images
|
70
70
|
old_images.each do |image|
|
71
|
-
|
72
|
-
|
71
|
+
step("Deleting image '#{image[:name]}'") do
|
72
|
+
cp.image_delete(image[:name])
|
73
|
+
end
|
73
74
|
end
|
74
75
|
end
|
75
76
|
end
|
@@ -10,20 +10,20 @@ module Command
|
|
10
10
|
skip_confirm_option
|
11
11
|
].freeze
|
12
12
|
DESCRIPTION = "Deletes the whole app (GVC with all workloads and all images) for all stale apps"
|
13
|
-
LONG_DESCRIPTION = <<~
|
13
|
+
LONG_DESCRIPTION = <<~DESC
|
14
14
|
- Deletes the whole app (GVC with all workloads and all images) for all stale apps
|
15
15
|
- Stale apps are identified based on the creation date of the latest image
|
16
16
|
- Specify the amount of days after an app should be considered stale through `stale_app_image_deployed_days` in the `.controlplane/controlplane.yml` file
|
17
17
|
- If `match_if_app_name_starts_with` is `true` in the `.controlplane/controlplane.yml` file, it will delete all stale apps that start with the name
|
18
18
|
- Will ask for explicit user confirmation
|
19
|
-
|
19
|
+
DESC
|
20
20
|
|
21
21
|
def call # rubocop:disable Metrics/MethodLength
|
22
|
-
return progress.puts
|
22
|
+
return progress.puts("No stale apps found.") if stale_apps.empty?
|
23
23
|
|
24
|
-
progress.puts
|
24
|
+
progress.puts("Stale apps:")
|
25
25
|
stale_apps.each do |app|
|
26
|
-
progress.puts
|
26
|
+
progress.puts(" #{app[:name]} (#{Shell.color((app[:date]).to_s, :red)})")
|
27
27
|
end
|
28
28
|
|
29
29
|
return unless confirm_delete
|
@@ -74,14 +74,16 @@ module Command
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def delete_gvc(app)
|
77
|
-
|
78
|
-
|
77
|
+
step("Deleting app '#{app[:name]}'") do
|
78
|
+
cp.gvc_delete(app[:name])
|
79
|
+
end
|
79
80
|
end
|
80
81
|
|
81
82
|
def delete_images(app)
|
82
83
|
app[:images].each do |image|
|
83
|
-
|
84
|
-
|
84
|
+
step("Deleting image '#{image}'") do
|
85
|
+
cp.image_delete(image)
|
86
|
+
end
|
85
87
|
end
|
86
88
|
end
|
87
89
|
end
|
data/lib/command/config.rb
CHANGED
@@ -6,28 +6,43 @@ module Command
|
|
6
6
|
OPTIONS = [
|
7
7
|
app_option
|
8
8
|
].freeze
|
9
|
-
DESCRIPTION = "Displays
|
10
|
-
LONG_DESCRIPTION = <<~
|
11
|
-
- Displays
|
12
|
-
|
13
|
-
EXAMPLES = <<~
|
9
|
+
DESCRIPTION = "Displays config for each app or a specific app"
|
10
|
+
LONG_DESCRIPTION = <<~DESC
|
11
|
+
- Displays config for each app or a specific app
|
12
|
+
DESC
|
13
|
+
EXAMPLES = <<~EX
|
14
14
|
```sh
|
15
|
-
# Shows the
|
15
|
+
# Shows the config for each app.
|
16
16
|
cpl config
|
17
17
|
|
18
|
-
# Shows
|
18
|
+
# Shows the config for a specific app.
|
19
19
|
cpl config -a $APP_NAME
|
20
20
|
```
|
21
|
-
|
21
|
+
EX
|
22
22
|
|
23
|
-
def call
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
def call # rubocop:disable Metrics/MethodLength
|
24
|
+
if config.app
|
25
|
+
puts "#{Shell.color("Current config (app '#{config.app}')", :blue)}:"
|
26
|
+
puts pretty_print(config.current)
|
27
|
+
puts
|
28
|
+
else
|
29
|
+
config.apps.each do |app_name, app_options|
|
30
|
+
puts "#{Shell.color("Config for app '#{app_name}'", :blue)}:"
|
31
|
+
puts pretty_print(app_options)
|
32
|
+
puts
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
27
38
|
|
28
|
-
|
29
|
-
|
30
|
-
|
39
|
+
def pretty_print(hash)
|
40
|
+
hash.transform_keys(&:to_s)
|
41
|
+
.to_yaml(indentation: 2)[4..]
|
42
|
+
# Adds an indentation of 2 to the beginning of each line
|
43
|
+
.gsub(/^(\s*)/, " \\1")
|
44
|
+
# Adds an indentation of 2 before the '-' in array items
|
45
|
+
.gsub(/^(\s*)-\s/, "\\1 - ")
|
31
46
|
end
|
32
47
|
end
|
33
48
|
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Command
|
4
|
+
class CopyImageFromUpstream < Base
|
5
|
+
NAME = "copy-image-from-upstream"
|
6
|
+
OPTIONS = [
|
7
|
+
app_option(required: true),
|
8
|
+
upstream_token_option(required: true),
|
9
|
+
image_option
|
10
|
+
].freeze
|
11
|
+
DESCRIPTION = "Copies an image (by default the latest) from a source org to the current org"
|
12
|
+
LONG_DESCRIPTION = <<~DESC
|
13
|
+
- Copies an image (by default the latest) from a source org to the current org
|
14
|
+
- The source org must be specified through `upstream` in the `.controlplane/controlplane.yml` file
|
15
|
+
- Additionally, the token for the source org must be provided through `--upstream-token` or `-t`
|
16
|
+
- A `cpln` profile will be temporarily created to pull the image from the source org
|
17
|
+
DESC
|
18
|
+
EXAMPLES = <<~EX
|
19
|
+
```sh
|
20
|
+
# Copies the latest image from the source org to the current org.
|
21
|
+
cpl copy-image-from-upstream -a $APP_NAME --upstream-token $UPSTREAM_TOKEN
|
22
|
+
|
23
|
+
# Copies a specific image from the source org to the current org.
|
24
|
+
cpl copy-image-from-upstream -a $APP_NAME --upstream-token $UPSTREAM_TOKEN --image appimage:123
|
25
|
+
```
|
26
|
+
EX
|
27
|
+
|
28
|
+
def call # rubocop:disable Metrics/MethodLength
|
29
|
+
ensure_docker_running!
|
30
|
+
|
31
|
+
@upstream = config[:upstream]
|
32
|
+
@upstream_org = config.apps[@upstream.to_sym][:cpln_org] || config.apps[@upstream.to_sym][:org]
|
33
|
+
ensure_upstream_org!
|
34
|
+
|
35
|
+
create_upstream_profile
|
36
|
+
fetch_upstream_image_url
|
37
|
+
fetch_app_image_url
|
38
|
+
pull_image_from_upstream
|
39
|
+
push_image_to_app
|
40
|
+
ensure
|
41
|
+
delete_upstream_profile
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def ensure_docker_running!
|
47
|
+
`docker version > /dev/null 2>&1`
|
48
|
+
return if $CHILD_STATUS.success?
|
49
|
+
|
50
|
+
raise "Can't run Docker. Please make sure that it's installed and started, then try again."
|
51
|
+
end
|
52
|
+
|
53
|
+
def ensure_upstream_org!
|
54
|
+
raise "Can't find option 'cpln_org' for app '#{@upstream}' in 'controlplane.yml'." unless @upstream_org
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_upstream_profile
|
58
|
+
step("Creating upstream profile") do
|
59
|
+
loop do
|
60
|
+
@upstream_profile = "upstream-#{rand(1000..9999)}"
|
61
|
+
break unless cp.profile_exists?(@upstream_profile)
|
62
|
+
end
|
63
|
+
|
64
|
+
cp.profile_create(@upstream_profile, config.options[:upstream_token])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def fetch_upstream_image_url
|
69
|
+
step("Fetching upstream image URL") do
|
70
|
+
cp.profile_switch(@upstream_profile)
|
71
|
+
upstream_image = config.options[:image]
|
72
|
+
upstream_image = latest_image(@upstream, @upstream_org) if !upstream_image || upstream_image == "latest"
|
73
|
+
@upstream_image_url = "#{@upstream_org}.registry.cpln.io/#{upstream_image}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def fetch_app_image_url
|
78
|
+
step("Fetching app image URL") do
|
79
|
+
cp.profile_switch("default")
|
80
|
+
app_image = latest_image_next(config.app, config.org)
|
81
|
+
@app_image_url = "#{config.org}.registry.cpln.io/#{app_image}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def pull_image_from_upstream
|
86
|
+
step("Pulling image from '#{@upstream_image_url}'") do
|
87
|
+
cp.profile_switch(@upstream_profile)
|
88
|
+
cp.image_login(@upstream_org)
|
89
|
+
cp.image_pull(@upstream_image_url)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def push_image_to_app
|
94
|
+
step("Pushing image to '#{@app_image_url}'") do
|
95
|
+
cp.profile_switch("default")
|
96
|
+
cp.image_login(config.org)
|
97
|
+
cp.image_tag(@upstream_image_url, @app_image_url)
|
98
|
+
cp.image_push(@app_image_url)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def delete_upstream_profile
|
103
|
+
return unless @upstream_profile
|
104
|
+
|
105
|
+
step("Deleting upstream profile") do
|
106
|
+
cp.profile_delete(@upstream_profile)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/command/delete.rb
CHANGED
@@ -8,10 +8,10 @@ module Command
|
|
8
8
|
skip_confirm_option
|
9
9
|
].freeze
|
10
10
|
DESCRIPTION = "Deletes the whole app (GVC with all workloads and all images)"
|
11
|
-
LONG_DESCRIPTION = <<~
|
11
|
+
LONG_DESCRIPTION = <<~DESC
|
12
12
|
- Deletes the whole app (GVC with all workloads and all images)
|
13
13
|
- Will ask for explicit user confirmation
|
14
|
-
|
14
|
+
DESC
|
15
15
|
|
16
16
|
def call
|
17
17
|
return unless confirm_delete
|
@@ -33,25 +33,23 @@ module Command
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def delete_gvc
|
36
|
-
progress.puts "
|
36
|
+
return progress.puts("App '#{config.app}' does not exist.") if cp.fetch_gvc.nil?
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
progress.puts config.app
|
38
|
+
step("Deleting app '#{config.app}'") do
|
39
|
+
cp.gvc_delete
|
40
|
+
end
|
42
41
|
end
|
43
42
|
|
44
43
|
def delete_images
|
45
|
-
progress.puts "- Deleting image(s):"
|
46
|
-
|
47
44
|
images = cp.image_query["items"]
|
48
45
|
.filter_map { |item| item["name"] if item["name"].start_with?("#{config.app}:") }
|
49
46
|
|
50
|
-
return progress.puts "
|
47
|
+
return progress.puts("No images to delete.") unless images.any?
|
51
48
|
|
52
49
|
images.each do |image|
|
53
|
-
|
54
|
-
|
50
|
+
step("Deleting image '#{image}'") do
|
51
|
+
cp.image_delete(image)
|
52
|
+
end
|
55
53
|
end
|
56
54
|
end
|
57
55
|
end
|
data/lib/command/deploy_image.rb
CHANGED
@@ -7,21 +7,32 @@ module Command
|
|
7
7
|
app_option(required: true)
|
8
8
|
].freeze
|
9
9
|
DESCRIPTION = "Deploys the latest image to app workloads"
|
10
|
-
LONG_DESCRIPTION = <<~
|
10
|
+
LONG_DESCRIPTION = <<~DESC
|
11
11
|
- Deploys the latest image to app workloads
|
12
|
-
|
12
|
+
DESC
|
13
|
+
|
14
|
+
def call # rubocop:disable Metrics/MethodLength
|
15
|
+
deployed_endpoints = {}
|
13
16
|
|
14
|
-
def call
|
15
17
|
image = latest_image
|
16
18
|
|
17
19
|
config[:app_workloads].each do |workload|
|
18
|
-
cp.fetch_workload!(workload)
|
19
|
-
|
20
|
+
workload_data = cp.fetch_workload!(workload)
|
21
|
+
workload_data.dig("spec", "containers").each do |container|
|
22
|
+
next unless container["image"].match?(%r{^/org/#{config.org}/image/#{config.app}:})
|
20
23
|
|
21
|
-
|
22
|
-
|
24
|
+
container_name = container["name"]
|
25
|
+
step("Deploying image '#{image}' for workload '#{container_name}'") do
|
26
|
+
cp.workload_set_image_ref(workload, container: container_name, image: image)
|
27
|
+
deployed_endpoints[container_name] = workload_data.dig("status", "endpoint")
|
28
|
+
end
|
23
29
|
end
|
24
30
|
end
|
31
|
+
|
32
|
+
progress.puts("\nDeployed endpoints:")
|
33
|
+
deployed_endpoints.each do |workload, endpoint|
|
34
|
+
progress.puts(" - #{workload}: #{endpoint}")
|
35
|
+
end
|
25
36
|
end
|
26
37
|
end
|
27
38
|
end
|