cpl 1.3.0 → 2.0.0.rc.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/command_docs.yml +1 -1
- data/.github/workflows/rspec-shared.yml +56 -0
- data/.github/workflows/rspec.yml +19 -31
- data/.github/workflows/rubocop.yml +2 -10
- data/.gitignore +2 -0
- data/.simplecov_spawn.rb +10 -0
- data/CHANGELOG.md +28 -1
- data/CONTRIBUTING.md +32 -2
- data/Gemfile.lock +38 -29
- data/README.md +43 -17
- data/cpl.gemspec +2 -1
- data/docs/commands.md +68 -59
- data/docs/dns.md +6 -0
- data/docs/migrating.md +10 -10
- data/docs/tips.md +15 -3
- data/examples/circleci.yml +3 -3
- data/examples/controlplane.yml +35 -9
- data/lib/command/apply_template.rb +66 -18
- data/lib/command/base.rb +168 -27
- data/lib/command/build_image.rb +4 -9
- data/lib/command/cleanup_stale_apps.rb +1 -3
- data/lib/command/copy_image_from_upstream.rb +0 -7
- data/lib/command/delete.rb +39 -7
- data/lib/command/deploy_image.rb +35 -2
- data/lib/command/exists.rb +1 -1
- data/lib/command/generate.rb +1 -1
- data/lib/command/info.rb +7 -3
- data/lib/command/logs.rb +22 -2
- data/lib/command/maintenance_off.rb +1 -1
- data/lib/command/maintenance_on.rb +1 -1
- data/lib/command/open.rb +2 -2
- data/lib/command/open_console.rb +2 -2
- data/lib/command/promote_app_from_upstream.rb +5 -25
- data/lib/command/ps.rb +1 -1
- data/lib/command/ps_start.rb +2 -1
- data/lib/command/ps_stop.rb +40 -8
- data/lib/command/ps_wait.rb +3 -2
- data/lib/command/run.rb +430 -68
- data/lib/command/setup_app.rb +22 -2
- data/lib/constants/exit_code.rb +7 -0
- data/lib/core/config.rb +11 -3
- data/lib/core/controlplane.rb +126 -47
- data/lib/core/controlplane_api.rb +15 -1
- data/lib/core/controlplane_api_cli.rb +3 -3
- data/lib/core/controlplane_api_direct.rb +33 -5
- data/lib/core/shell.rb +15 -9
- data/lib/cpl/version.rb +1 -1
- data/lib/cpl.rb +50 -9
- data/lib/deprecated_commands.json +2 -1
- data/lib/generator_templates/controlplane.yml +5 -0
- data/lib/generator_templates/templates/{gvc.yml → app.yml} +4 -4
- data/lib/generator_templates/templates/postgres.yml +1 -1
- data/lib/generator_templates/templates/rails.yml +1 -1
- data/script/check_cpln_links +3 -3
- data/templates/app.yml +18 -0
- data/templates/daily-task.yml +3 -2
- data/templates/rails.yml +3 -2
- data/templates/secrets.yml +11 -0
- data/templates/sidekiq.yml +3 -2
- metadata +38 -25
- data/.rspec +0 -1
- data/lib/command/run_cleanup.rb +0 -116
- data/lib/command/run_detached.rb +0 -175
- data/lib/core/scripts.rb +0 -34
- data/templates/gvc.yml +0 -13
- data/templates/identity.yml +0 -2
data/script/check_cpln_links
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env bash
|
2
2
|
|
3
|
-
bad_links=("controlplane.com/shakacode")
|
4
|
-
proper_links=("shakacode.controlplane.com")
|
3
|
+
bad_links=("controlplane.com/shakacode" "https://docs.controlplane.com")
|
4
|
+
proper_links=("shakacode.controlplane.com" "https://shakadocs.controlplane.com")
|
5
5
|
|
6
6
|
bold=$(tput bold)
|
7
7
|
normal=$(tput sgr0)
|
@@ -19,7 +19,7 @@ for ((idx = 0; idx < ${#bad_links[@]}; idx++)); do
|
|
19
19
|
--heading \
|
20
20
|
--color=always -- \
|
21
21
|
"${bad_links[idx]}" \
|
22
|
-
':!script/check_cpln_links')
|
22
|
+
':!script/check_cpln_links' '*.md')
|
23
23
|
|
24
24
|
# Line would become really unwieldly if everything was mushed into the
|
25
25
|
# conditional, so let's ignore this check here.
|
data/templates/app.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
kind: gvc
|
2
|
+
name: {{APP_NAME}}
|
3
|
+
spec:
|
4
|
+
env:
|
5
|
+
- name: MEMCACHE_SERVERS
|
6
|
+
value: memcached.{{APP_NAME}}.cpln.local
|
7
|
+
- name: REDIS_URL
|
8
|
+
value: redis://redis.{{APP_NAME}}.cpln.local:6379
|
9
|
+
- name: DATABASE_URL
|
10
|
+
value: postgres://postgres:password123@postgres.{{APP_NAME}}.cpln.local:5432/{{APP_NAME}}
|
11
|
+
staticPlacement:
|
12
|
+
locationLinks:
|
13
|
+
- {{APP_LOCATION_LINK}}
|
14
|
+
---
|
15
|
+
# Identity is needed to access secrets
|
16
|
+
kind: identity
|
17
|
+
name: {{APP_IDENTITY}}
|
18
|
+
|
data/templates/daily-task.yml
CHANGED
@@ -18,7 +18,7 @@ spec:
|
|
18
18
|
- rails
|
19
19
|
- db:prepare
|
20
20
|
inheritEnv: true
|
21
|
-
image:
|
21
|
+
image: {{APP_IMAGE_LINK}}
|
22
22
|
defaultOptions:
|
23
23
|
autoscaling:
|
24
24
|
minScale: 1
|
@@ -28,4 +28,5 @@ spec:
|
|
28
28
|
external:
|
29
29
|
outboundAllowCIDR:
|
30
30
|
- 0.0.0.0/0
|
31
|
-
|
31
|
+
# Identity is used for binding workload to secrets
|
32
|
+
identityLink: {{APP_IDENTITY_LINK}}
|
data/templates/rails.yml
CHANGED
@@ -7,7 +7,7 @@ spec:
|
|
7
7
|
cpu: 512m
|
8
8
|
memory: 1Gi
|
9
9
|
inheritEnv: true
|
10
|
-
image:
|
10
|
+
image: {{APP_IMAGE_LINK}}
|
11
11
|
ports:
|
12
12
|
- number: 3000
|
13
13
|
protocol: http
|
@@ -23,4 +23,5 @@ spec:
|
|
23
23
|
- 0.0.0.0/0
|
24
24
|
outboundAllowCIDR:
|
25
25
|
- 0.0.0.0/0
|
26
|
-
|
26
|
+
# Identity is used for binding workload to secrets
|
27
|
+
identityLink: {{APP_IDENTITY_LINK}}
|
data/templates/sidekiq.yml
CHANGED
@@ -13,7 +13,7 @@ spec:
|
|
13
13
|
- "-C"
|
14
14
|
- config/sidekiq.yml
|
15
15
|
inheritEnv: true
|
16
|
-
image:
|
16
|
+
image: {{APP_IMAGE_LINK}}
|
17
17
|
ports:
|
18
18
|
- number: 7433
|
19
19
|
protocol: http
|
@@ -34,4 +34,5 @@ spec:
|
|
34
34
|
external:
|
35
35
|
outboundAllowCIDR:
|
36
36
|
- 0.0.0.0/0
|
37
|
-
|
37
|
+
# Identity is used for binding workload to secrets
|
38
|
+
identityLink: {{APP_IDENTITY_LINK}}
|
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:
|
4
|
+
version: 2.0.0.rc.0
|
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: 2024-
|
12
|
+
date: 2024-05-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: debug
|
@@ -39,6 +39,20 @@ dependencies:
|
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: 2.8.1
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: jwt
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 2.8.1
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 2.8.1
|
42
56
|
- !ruby/object:Gem::Dependency
|
43
57
|
name: psych
|
44
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -95,6 +109,20 @@ dependencies:
|
|
95
109
|
- - "~>"
|
96
110
|
- !ruby/object:Gem::Version
|
97
111
|
version: 3.12.0
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: rspec-retry
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - "~>"
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 0.6.2
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - "~>"
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 0.6.2
|
98
126
|
- !ruby/object:Gem::Dependency
|
99
127
|
name: rubocop
|
100
128
|
requirement: !ruby/object:Gem::Requirement
|
@@ -165,20 +193,6 @@ dependencies:
|
|
165
193
|
- - "~>"
|
166
194
|
- !ruby/object:Gem::Version
|
167
195
|
version: 0.9.6
|
168
|
-
- !ruby/object:Gem::Dependency
|
169
|
-
name: vcr
|
170
|
-
requirement: !ruby/object:Gem::Requirement
|
171
|
-
requirements:
|
172
|
-
- - "~>"
|
173
|
-
- !ruby/object:Gem::Version
|
174
|
-
version: 6.1.0
|
175
|
-
type: :development
|
176
|
-
prerelease: false
|
177
|
-
version_requirements: !ruby/object:Gem::Requirement
|
178
|
-
requirements:
|
179
|
-
- - "~>"
|
180
|
-
- !ruby/object:Gem::Version
|
181
|
-
version: 6.1.0
|
182
196
|
- !ruby/object:Gem::Dependency
|
183
197
|
name: webmock
|
184
198
|
requirement: !ruby/object:Gem::Requirement
|
@@ -204,12 +218,13 @@ extra_rdoc_files: []
|
|
204
218
|
files:
|
205
219
|
- ".github/workflows/check_cpln_links.yml"
|
206
220
|
- ".github/workflows/command_docs.yml"
|
221
|
+
- ".github/workflows/rspec-shared.yml"
|
207
222
|
- ".github/workflows/rspec.yml"
|
208
223
|
- ".github/workflows/rubocop.yml"
|
209
224
|
- ".gitignore"
|
210
225
|
- ".overcommit.yml"
|
211
|
-
- ".rspec"
|
212
226
|
- ".rubocop.yml"
|
227
|
+
- ".simplecov_spawn.rb"
|
213
228
|
- CHANGELOG.md
|
214
229
|
- CONTRIBUTING.md
|
215
230
|
- Gemfile
|
@@ -262,18 +277,16 @@ files:
|
|
262
277
|
- lib/command/ps_stop.rb
|
263
278
|
- lib/command/ps_wait.rb
|
264
279
|
- lib/command/run.rb
|
265
|
-
- lib/command/run_cleanup.rb
|
266
|
-
- lib/command/run_detached.rb
|
267
280
|
- lib/command/setup_app.rb
|
268
281
|
- lib/command/test.rb
|
269
282
|
- lib/command/version.rb
|
283
|
+
- lib/constants/exit_code.rb
|
270
284
|
- lib/core/config.rb
|
271
285
|
- lib/core/controlplane.rb
|
272
286
|
- lib/core/controlplane_api.rb
|
273
287
|
- lib/core/controlplane_api_cli.rb
|
274
288
|
- lib/core/controlplane_api_direct.rb
|
275
289
|
- lib/core/helpers.rb
|
276
|
-
- lib/core/scripts.rb
|
277
290
|
- lib/core/shell.rb
|
278
291
|
- lib/cpl.rb
|
279
292
|
- lib/cpl/version.rb
|
@@ -281,7 +294,7 @@ files:
|
|
281
294
|
- lib/generator_templates/Dockerfile
|
282
295
|
- lib/generator_templates/controlplane.yml
|
283
296
|
- lib/generator_templates/entrypoint.sh
|
284
|
-
- lib/generator_templates/templates/
|
297
|
+
- lib/generator_templates/templates/app.yml
|
285
298
|
- lib/generator_templates/templates/postgres.yml
|
286
299
|
- lib/generator_templates/templates/rails.yml
|
287
300
|
- rakelib/create_release.rake
|
@@ -290,14 +303,14 @@ files:
|
|
290
303
|
- script/check_cpln_links
|
291
304
|
- script/rename_command
|
292
305
|
- script/update_command_docs
|
306
|
+
- templates/app.yml
|
293
307
|
- templates/daily-task.yml
|
294
|
-
- templates/gvc.yml
|
295
|
-
- templates/identity.yml
|
296
308
|
- templates/maintenance.yml
|
297
309
|
- templates/memcached.yml
|
298
310
|
- templates/postgres.yml
|
299
311
|
- templates/rails.yml
|
300
312
|
- templates/redis.yml
|
313
|
+
- templates/secrets.yml
|
301
314
|
- templates/sidekiq.yml
|
302
315
|
homepage: https://github.com/shakacode/heroku-to-control-plane
|
303
316
|
licenses:
|
@@ -315,9 +328,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
315
328
|
version: 2.7.0
|
316
329
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
317
330
|
requirements:
|
318
|
-
- - "
|
331
|
+
- - ">"
|
319
332
|
- !ruby/object:Gem::Version
|
320
|
-
version:
|
333
|
+
version: 1.3.1
|
321
334
|
requirements: []
|
322
335
|
rubygems_version: 3.4.21
|
323
336
|
signing_key:
|
data/.rspec
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
--require spec_helper
|
data/lib/command/run_cleanup.rb
DELETED
@@ -1,116 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Command
|
4
|
-
class RunCleanup < Base
|
5
|
-
NAME = "run:cleanup"
|
6
|
-
OPTIONS = [
|
7
|
-
app_option(required: true),
|
8
|
-
skip_confirm_option
|
9
|
-
].freeze
|
10
|
-
DESCRIPTION = "Deletes stale run workloads for an app"
|
11
|
-
LONG_DESCRIPTION = <<~DESC
|
12
|
-
- Deletes stale run workloads for an app
|
13
|
-
- Workloads are considered stale based on how many days since created
|
14
|
-
- `stale_run_workload_created_days` in the `.controlplane/controlplane.yml` file specifies the number of days after created that the workload is considered stale
|
15
|
-
- Works for both interactive workloads (created with `cpl run`) and non-interactive workloads (created with `cpl run:detached`)
|
16
|
-
- Will ask for explicit user confirmation of deletion
|
17
|
-
DESC
|
18
|
-
|
19
|
-
def call # rubocop:disable Metrics/MethodLength
|
20
|
-
return progress.puts("No stale run workloads found.") if stale_run_workloads.empty?
|
21
|
-
|
22
|
-
progress.puts("Stale run workloads:")
|
23
|
-
stale_run_workloads.each do |workload|
|
24
|
-
output = ""
|
25
|
-
output += if config.should_app_start_with?(config.app)
|
26
|
-
" #{workload[:app]} - #{workload[:name]}"
|
27
|
-
else
|
28
|
-
" #{workload[:name]}"
|
29
|
-
end
|
30
|
-
output += " (#{Shell.color("#{workload[:date]} - #{workload[:days]} days ago", :red)})"
|
31
|
-
progress.puts(output)
|
32
|
-
end
|
33
|
-
|
34
|
-
return unless confirm_delete
|
35
|
-
|
36
|
-
progress.puts
|
37
|
-
stale_run_workloads.each do |workload|
|
38
|
-
delete_workload(workload)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def app_matches?(app, app_name, app_options)
|
45
|
-
app == app_name.to_s || (app_options[:match_if_app_name_starts_with] && app.start_with?(app_name.to_s))
|
46
|
-
end
|
47
|
-
|
48
|
-
def find_app_options(app)
|
49
|
-
@app_options ||= {}
|
50
|
-
@app_options[app] ||= config.apps.find do |app_name, app_options|
|
51
|
-
app_matches?(app, app_name, app_options)
|
52
|
-
end&.last
|
53
|
-
end
|
54
|
-
|
55
|
-
def find_workloads(app)
|
56
|
-
app_options = find_app_options(app)
|
57
|
-
return [] if app_options.nil?
|
58
|
-
|
59
|
-
(app_options[:app_workloads] + app_options[:additional_workloads] + [app_options[:one_off_workload]]).uniq
|
60
|
-
end
|
61
|
-
|
62
|
-
def stale_run_workloads # rubocop:disable Metrics/MethodLength
|
63
|
-
@stale_run_workloads ||=
|
64
|
-
begin
|
65
|
-
defined_workloads = find_workloads(config.app)
|
66
|
-
|
67
|
-
run_workloads = []
|
68
|
-
|
69
|
-
now = DateTime.now
|
70
|
-
stale_run_workload_created_days = config[:stale_run_workload_created_days]
|
71
|
-
|
72
|
-
interactive_workloads = cp.query_workloads("-run-", partial_workload_match: true)["items"]
|
73
|
-
non_interactive_workloads = cp.query_workloads("-runner-", partial_workload_match: true)["items"]
|
74
|
-
workloads = interactive_workloads + non_interactive_workloads
|
75
|
-
|
76
|
-
workloads.each do |workload|
|
77
|
-
app_name = workload["links"].find { |link| link["rel"] == "gvc" }["href"].split("/").last
|
78
|
-
workload_name = workload["name"]
|
79
|
-
|
80
|
-
original_workload_name, workload_number = workload_name.split(/-run-|-runner-/)
|
81
|
-
next unless defined_workloads.include?(original_workload_name) && workload_number.match?(/^\d{4}$/)
|
82
|
-
|
83
|
-
created_date = DateTime.parse(workload["created"])
|
84
|
-
diff_in_days = (now - created_date).to_i
|
85
|
-
next unless diff_in_days >= stale_run_workload_created_days
|
86
|
-
|
87
|
-
run_workloads.push({
|
88
|
-
app: app_name,
|
89
|
-
name: workload_name,
|
90
|
-
date: created_date,
|
91
|
-
days: diff_in_days
|
92
|
-
})
|
93
|
-
end
|
94
|
-
|
95
|
-
run_workloads
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def confirm_delete
|
100
|
-
return true if config.options[:yes]
|
101
|
-
|
102
|
-
Shell.confirm("\nAre you sure you want to delete these #{stale_run_workloads.length} run workloads?")
|
103
|
-
end
|
104
|
-
|
105
|
-
def delete_workload(workload)
|
106
|
-
message = if config.should_app_start_with?(config.app)
|
107
|
-
"Deleting run workload '#{workload[:app]} - #{workload[:name]}'"
|
108
|
-
else
|
109
|
-
"Deleting run workload '#{workload[:name]}'"
|
110
|
-
end
|
111
|
-
step(message) do
|
112
|
-
cp.delete_workload(workload[:name], workload[:app])
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
data/lib/command/run_detached.rb
DELETED
@@ -1,175 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Command
|
4
|
-
class RunDetached < Base # rubocop:disable Metrics/ClassLength
|
5
|
-
NAME = "run:detached"
|
6
|
-
USAGE = "run:detached COMMAND"
|
7
|
-
REQUIRES_ARGS = true
|
8
|
-
OPTIONS = [
|
9
|
-
app_option(required: true),
|
10
|
-
image_option,
|
11
|
-
workload_option,
|
12
|
-
location_option,
|
13
|
-
use_local_token_option,
|
14
|
-
clean_on_failure_option
|
15
|
-
].freeze
|
16
|
-
DESCRIPTION = "Runs one-off **_non-interactive_** replicas (close analog of `heroku run:detached`)"
|
17
|
-
LONG_DESCRIPTION = <<~DESC
|
18
|
-
- Runs one-off **_non-interactive_** replicas (close analog of `heroku run:detached`)
|
19
|
-
- Uses `Cron` workload type with log async fetching
|
20
|
-
- Implemented with only async execution methods, more suitable for production tasks
|
21
|
-
- Has alternative log fetch implementation with only JSON-polling and no WebSockets
|
22
|
-
- Less responsive but more stable, useful for CI tasks
|
23
|
-
- Deletes the workload whenever finished with success
|
24
|
-
- Deletes the workload whenever finished with failure by default
|
25
|
-
- Use `--no-clean-on-failure` to disable cleanup to help with debugging failed runs
|
26
|
-
DESC
|
27
|
-
EXAMPLES = <<~EX
|
28
|
-
```sh
|
29
|
-
cpl run:detached rails db:prepare -a $APP_NAME
|
30
|
-
|
31
|
-
# Need to quote COMMAND if setting ENV value or passing args.
|
32
|
-
cpl run:detached -a $APP_NAME -- 'LOG_LEVEL=warn rails db:migrate'
|
33
|
-
|
34
|
-
# Uses a different image (which may not be promoted yet).
|
35
|
-
cpl run:detached -a $APP_NAME --image appimage:123 -- rails db:migrate # Exact image name
|
36
|
-
cpl run:detached -a $APP_NAME --image latest -- rails db:migrate # Latest sequential image
|
37
|
-
|
38
|
-
# Uses a different workload than `one_off_workload` from `.controlplane/controlplane.yml`.
|
39
|
-
cpl run:detached -a $APP_NAME -w other-workload -- rails db:migrate:status
|
40
|
-
|
41
|
-
# Overrides remote CPLN_TOKEN env variable with local token.
|
42
|
-
# Useful when superuser rights are needed in remote container.
|
43
|
-
cpl run:detached -a $APP_NAME --use-local-token -- rails db:migrate:status
|
44
|
-
```
|
45
|
-
EX
|
46
|
-
|
47
|
-
WORKLOAD_SLEEP_CHECK = 2
|
48
|
-
|
49
|
-
attr_reader :location, :workload_to_clone, :workload_clone, :container
|
50
|
-
|
51
|
-
def call
|
52
|
-
@location = config.location
|
53
|
-
@workload_to_clone = config.options["workload"] || config[:one_off_workload]
|
54
|
-
@workload_clone = "#{workload_to_clone}-runner-#{random_four_digits}"
|
55
|
-
|
56
|
-
step("Cloning workload '#{workload_to_clone}' on app '#{config.options[:app]}' to '#{workload_clone}'") do
|
57
|
-
clone_workload
|
58
|
-
end
|
59
|
-
|
60
|
-
wait_for_workload(workload_clone)
|
61
|
-
show_logs_waiting
|
62
|
-
ensure
|
63
|
-
exit(1) if @crashed
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
def clone_workload # rubocop:disable Metrics/MethodLength
|
69
|
-
# Get base specs of workload
|
70
|
-
spec = cp.fetch_workload!(workload_to_clone).fetch("spec")
|
71
|
-
container_spec = spec["containers"].detect { _1["name"] == workload_to_clone } || spec["containers"].first
|
72
|
-
@container = container_spec["name"]
|
73
|
-
|
74
|
-
# remove other containers if any
|
75
|
-
spec["containers"] = [container_spec]
|
76
|
-
|
77
|
-
# Set runner
|
78
|
-
container_spec["command"] = "bash"
|
79
|
-
container_spec["args"] = ["-c", 'eval "$CONTROLPLANE_RUNNER"']
|
80
|
-
|
81
|
-
# Ensure one-off workload will be running
|
82
|
-
spec["defaultOptions"]["suspend"] = false
|
83
|
-
|
84
|
-
# Ensure no scaling
|
85
|
-
spec["defaultOptions"]["autoscaling"]["minScale"] = 1
|
86
|
-
spec["defaultOptions"]["autoscaling"]["maxScale"] = 1
|
87
|
-
spec["defaultOptions"]["capacityAI"] = false
|
88
|
-
|
89
|
-
# Override image if specified
|
90
|
-
image = config.options[:image]
|
91
|
-
image = latest_image if image == "latest"
|
92
|
-
container_spec["image"] = "/org/#{config.org}/image/#{image}" if image
|
93
|
-
|
94
|
-
# Set cron job props
|
95
|
-
spec["type"] = "cron"
|
96
|
-
spec["job"] = { "schedule" => "* * * * *", "restartPolicy" => "Never" }
|
97
|
-
spec["defaultOptions"]["autoscaling"] = {}
|
98
|
-
container_spec.delete("ports")
|
99
|
-
|
100
|
-
container_spec["env"] ||= []
|
101
|
-
container_spec["env"] << { "name" => "CONTROLPLANE_TOKEN", "value" => ControlplaneApiDirect.new.api_token }
|
102
|
-
container_spec["env"] << { "name" => "CONTROLPLANE_RUNNER", "value" => runner_script }
|
103
|
-
|
104
|
-
# Create workload clone
|
105
|
-
cp.apply_hash("kind" => "workload", "name" => workload_clone, "spec" => spec)
|
106
|
-
end
|
107
|
-
|
108
|
-
def runner_script # rubocop:disable Metrics/MethodLength
|
109
|
-
script = "echo '-- STARTED RUNNER SCRIPT --'\n"
|
110
|
-
script += Scripts.helpers_cleanup
|
111
|
-
|
112
|
-
if config.options["use_local_token"]
|
113
|
-
script += <<~SHELL
|
114
|
-
CPLN_TOKEN=$CONTROLPLANE_TOKEN
|
115
|
-
SHELL
|
116
|
-
end
|
117
|
-
|
118
|
-
script += <<~SHELL
|
119
|
-
crashed=0
|
120
|
-
if ! eval "#{args_join(config.args)}"; then
|
121
|
-
crashed=1
|
122
|
-
echo "----- CRASHED -----"
|
123
|
-
fi
|
124
|
-
clean_on_failure=#{config.options[:clean_on_failure] ? 1 : 0}
|
125
|
-
if [ $crashed -eq 0 ] || [ $clean_on_failure -eq 1 ]; then
|
126
|
-
echo "-- FINISHED RUNNER SCRIPT, DELETING WORKLOAD --"
|
127
|
-
sleep 30 # grace time for logs propagation
|
128
|
-
curl ${CPLN_ENDPOINT}${CPLN_WORKLOAD} -H "Authorization: ${CONTROLPLANE_TOKEN}" -X DELETE -s -o /dev/null
|
129
|
-
while true; do sleep 1; done # wait for SIGTERM
|
130
|
-
else
|
131
|
-
echo "-- FINISHED RUNNER SCRIPT --"
|
132
|
-
fi
|
133
|
-
SHELL
|
134
|
-
|
135
|
-
script
|
136
|
-
end
|
137
|
-
|
138
|
-
def show_logs_waiting # rubocop:disable Metrics/MethodLength
|
139
|
-
progress.puts("Scheduled, fetching logs (it's a cron job, so it may take up to a minute to start)...\n\n")
|
140
|
-
begin
|
141
|
-
@finished = false
|
142
|
-
while cp.fetch_workload(workload_clone) && !@finished
|
143
|
-
sleep(WORKLOAD_SLEEP_CHECK)
|
144
|
-
print_uniq_logs
|
145
|
-
end
|
146
|
-
rescue RuntimeError => e
|
147
|
-
progress.puts(Shell.color("ERROR: #{e}", :red))
|
148
|
-
retry
|
149
|
-
end
|
150
|
-
progress.puts("\nFinished workload and logger.")
|
151
|
-
end
|
152
|
-
|
153
|
-
def print_uniq_logs
|
154
|
-
@printed_log_entries ||= []
|
155
|
-
ts = Time.now.to_i
|
156
|
-
entries = normalized_log_entries(from: ts - 60, to: ts)
|
157
|
-
|
158
|
-
(entries - @printed_log_entries).sort.each do |(_ts, val)|
|
159
|
-
@crashed = true if val.match?(/^----- CRASHED -----$/)
|
160
|
-
@finished = true if val.match?(/^-- FINISHED RUNNER SCRIPT(, DELETING WORKLOAD)? --$/)
|
161
|
-
puts val
|
162
|
-
end
|
163
|
-
|
164
|
-
@printed_log_entries = entries # as well truncate old entries if any
|
165
|
-
end
|
166
|
-
|
167
|
-
def normalized_log_entries(from:, to:)
|
168
|
-
log = cp.log_get(workload: workload_clone, from: from, to: to)
|
169
|
-
|
170
|
-
log["data"]["result"]
|
171
|
-
.each_with_object([]) { |obj, result| result.concat(obj["values"]) }
|
172
|
-
.select { |ts, _val| ts[..-10].to_i > from }
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
data/lib/core/scripts.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Scripts
|
4
|
-
module_function
|
5
|
-
|
6
|
-
def assert_replicas(gvc:, workload:, location:)
|
7
|
-
<<~SHELL
|
8
|
-
REPLICAS_QTY=$( \
|
9
|
-
curl ${CPLN_ENDPOINT}/org/shakacode-staging/gvc/#{gvc}/workload/#{workload}/deployment/#{location} \
|
10
|
-
-H "Authorization: ${CONTROLPLANE_TOKEN}" -s | grep -o '"replicas":[0-9]*' | grep -o '[0-9]*')
|
11
|
-
|
12
|
-
if [ "$REPLICAS_QTY" -gt 0 ]; then
|
13
|
-
echo "-- MULTIPLE REPLICAS ATTEMPT: $REPLICAS_QTY --"
|
14
|
-
exit -1
|
15
|
-
fi
|
16
|
-
SHELL
|
17
|
-
end
|
18
|
-
|
19
|
-
def helpers_cleanup
|
20
|
-
<<~SHELL
|
21
|
-
unset CONTROLPLANE_RUNNER
|
22
|
-
SHELL
|
23
|
-
end
|
24
|
-
|
25
|
-
# NOTE: please escape all '/' as '//' (as it is ruby interpolation here as well)
|
26
|
-
def http_dummy_server_ruby
|
27
|
-
'require "socket";s=TCPServer.new(ENV["PORT"] || 80);' \
|
28
|
-
'loop do c=s.accept;c.puts("HTTP/1.1 200 OK\\nContent-Length: 2\\n\\nOk");c.close end'
|
29
|
-
end
|
30
|
-
|
31
|
-
def http_ping_ruby
|
32
|
-
'require "net/http";uri=URI(ENV["CPLN_GLOBAL_ENDPOINT"]);loop do puts(Net::HTTP.get(uri));sleep(5);end'
|
33
|
-
end
|
34
|
-
end
|
data/templates/gvc.yml
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
kind: gvc
|
2
|
-
name: APP_GVC
|
3
|
-
spec:
|
4
|
-
env:
|
5
|
-
- name: MEMCACHE_SERVERS
|
6
|
-
value: memcached.APP_GVC.cpln.local
|
7
|
-
- name: REDIS_URL
|
8
|
-
value: redis://redis.APP_GVC.cpln.local:6379
|
9
|
-
- name: DATABASE_URL
|
10
|
-
value: postgres://postgres:password123@postgres.APP_GVC.cpln.local:5432/APP_GVC
|
11
|
-
staticPlacement:
|
12
|
-
locationLinks:
|
13
|
-
- /org/APP_ORG/location/APP_LOCATION
|
data/templates/identity.yml
DELETED