cpl 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +6 -5
- 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: 0be2530a33ac1f8a6b71d6d90eca0026b1048faf76b3e2f88cf13572f7e50a92
|
4
|
+
data.tar.gz: c15e191f5d8e984aece73bf5954ba977ee1c3013851447592ef4f2e96c1e2732
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9fe01da2ec88fdb25ba56b9ca78267f8cca42b73f219576decc4af5c0aa695168d08b32692bcdcabc7274ab1f5e9f587d3d7b09d18817c262f011fc328ee7409
|
7
|
+
data.tar.gz: a88847f98eaa1426eeabe23919cd1dbbf31275491e61ba825eebb44ae991b8eba087ffa1c61986e9f576026dbac59f4f118f00abcbfdfb273997ba95c1b27cdc
|
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.0)
|
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
|
[![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)
|
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,19 +7,20 @@ 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
13
|
|
14
14
|
def call
|
15
15
|
image = latest_image
|
16
16
|
|
17
17
|
config[:app_workloads].each do |workload|
|
18
18
|
cp.fetch_workload!(workload).dig("spec", "containers").each do |container|
|
19
|
-
next unless container["image"].match?(%r{^/org/#{config
|
19
|
+
next unless container["image"].match?(%r{^/org/#{config.org}/image/#{config.app}:})
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
step("Deploying image '#{image}' for workload '#{container['name']}'") do
|
22
|
+
cp.workload_set_image_ref(workload, container: container["name"], image: image)
|
23
|
+
end
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
data/lib/command/env.rb
CHANGED
@@ -7,9 +7,9 @@ module Command
|
|
7
7
|
app_option(required: true)
|
8
8
|
].freeze
|
9
9
|
DESCRIPTION = "Displays app-specific environment variables"
|
10
|
-
LONG_DESCRIPTION = <<~
|
10
|
+
LONG_DESCRIPTION = <<~DESC
|
11
11
|
- Displays app-specific environment variables
|
12
|
-
|
12
|
+
DESC
|
13
13
|
|
14
14
|
def call
|
15
15
|
cp.fetch_gvc!.dig("spec", "env").map do |prop|
|