cpl 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -0
- data/Gemfile.lock +17 -2
- data/README.md +67 -20
- data/Rakefile +2 -2
- data/cpl.gemspec +3 -0
- data/docs/commands.md +72 -5
- data/examples/circleci.yml +1 -1
- data/examples/controlplane.yml +3 -0
- data/lib/command/base.rb +55 -16
- data/lib/command/build_image.rb +15 -3
- data/lib/command/copy_image_from_upstream.rb +1 -1
- data/lib/command/info.rb +9 -5
- data/lib/command/maintenance.rb +37 -0
- data/lib/command/maintenance_off.rb +58 -0
- data/lib/command/maintenance_on.rb +58 -0
- data/lib/command/maintenance_set_page.rb +34 -0
- data/lib/command/no_command.rb +1 -1
- data/lib/command/promote_app_from_upstream.rb +2 -2
- data/lib/command/ps_start.rb +22 -5
- data/lib/command/ps_stop.rb +22 -5
- data/lib/command/run.rb +23 -5
- data/lib/command/run_cleanup.rb +99 -0
- data/lib/command/run_detached.rb +6 -3
- data/lib/command/setup_app.rb +3 -3
- data/lib/core/config.rb +39 -32
- data/lib/core/controlplane.rb +72 -16
- data/lib/core/controlplane_api.rb +39 -0
- data/lib/core/controlplane_api_direct.rb +13 -3
- data/lib/cpl/version.rb +1 -1
- data/templates/daily-task.yml +30 -0
- data/templates/maintenance.yml +24 -0
- metadata +51 -2
data/lib/core/controlplane.rb
CHANGED
@@ -35,10 +35,13 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
35
35
|
|
36
36
|
# image
|
37
37
|
|
38
|
-
def image_build(image, dockerfile:, push: true)
|
39
|
-
cmd = "
|
40
|
-
cmd += " --
|
38
|
+
def image_build(image, dockerfile:, build_args: [], push: true)
|
39
|
+
cmd = "docker build -t #{image} -f #{dockerfile}"
|
40
|
+
build_args.each { |build_arg| cmd += " --build-arg #{build_arg}" }
|
41
|
+
cmd += " #{config.app_dir}"
|
41
42
|
perform!(cmd)
|
43
|
+
|
44
|
+
image_push(image) if push
|
42
45
|
end
|
43
46
|
|
44
47
|
def image_query(app_name = config.app, org_name = config.org)
|
@@ -97,7 +100,7 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
97
100
|
gvc_data = fetch_gvc(a_gvc)
|
98
101
|
return gvc_data if gvc_data
|
99
102
|
|
100
|
-
raise "Can't find
|
103
|
+
raise "Can't find app '#{gvc}', please create it with 'cpl setup-app -a #{config.app}'."
|
101
104
|
end
|
102
105
|
|
103
106
|
def gvc_delete(a_gvc = gvc)
|
@@ -122,7 +125,13 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
122
125
|
workload_data = fetch_workload(workload)
|
123
126
|
return workload_data if workload_data
|
124
127
|
|
125
|
-
raise "Can't find workload '#{workload}', please create it with 'cpl
|
128
|
+
raise "Can't find workload '#{workload}', please create it with 'cpl apply-template #{workload} -a #{config.app}'."
|
129
|
+
end
|
130
|
+
|
131
|
+
def query_workloads(workload, partial_match: false)
|
132
|
+
op = partial_match ? "~" : "="
|
133
|
+
|
134
|
+
api.query_workloads(org: org, gvc: gvc, workload: workload, op_type: op)
|
126
135
|
end
|
127
136
|
|
128
137
|
def workload_get_replicas(workload, location:)
|
@@ -136,6 +145,10 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
136
145
|
$CHILD_STATUS.success? ? YAML.safe_load(result) : nil
|
137
146
|
end
|
138
147
|
|
148
|
+
def fetch_workload_deployments(workload)
|
149
|
+
api.workload_deployments(workload: workload, gvc: gvc, org: org)
|
150
|
+
end
|
151
|
+
|
139
152
|
def workload_set_image_ref(workload, container:, image:)
|
140
153
|
cmd = "cpln workload update #{workload} #{gvc_org}"
|
141
154
|
cmd += " --set spec.containers.#{container}.image=/org/#{config.org}/image/#{image}"
|
@@ -143,10 +156,26 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
143
156
|
perform!(cmd)
|
144
157
|
end
|
145
158
|
|
146
|
-
def
|
159
|
+
def set_workload_env_var(workload, container:, name:, value:)
|
160
|
+
data = fetch_workload!(workload)
|
161
|
+
data["spec"]["containers"].each do |container_data|
|
162
|
+
next unless container_data["name"] == container
|
163
|
+
|
164
|
+
container_data["env"].each do |env_data|
|
165
|
+
next unless env_data["name"] == name
|
166
|
+
|
167
|
+
env_data["value"] = value
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
api.update_workload(org: org, gvc: gvc, workload: workload, data: data)
|
172
|
+
end
|
173
|
+
|
174
|
+
def set_workload_suspend(workload, value)
|
147
175
|
data = fetch_workload!(workload)
|
148
176
|
data["spec"]["defaultOptions"]["suspend"] = value
|
149
|
-
|
177
|
+
|
178
|
+
api.update_workload(org: org, gvc: gvc, workload: workload, data: data)
|
150
179
|
end
|
151
180
|
|
152
181
|
def workload_force_redeployment(workload)
|
@@ -155,15 +184,8 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
155
184
|
perform!(cmd)
|
156
185
|
end
|
157
186
|
|
158
|
-
def
|
159
|
-
|
160
|
-
cmd += " 2> /dev/null"
|
161
|
-
perform(cmd)
|
162
|
-
end
|
163
|
-
|
164
|
-
def workload_delete!(workload)
|
165
|
-
cmd = "cpln workload delete #{workload} #{gvc_org}"
|
166
|
-
perform!(cmd)
|
187
|
+
def delete_workload(workload)
|
188
|
+
api.delete_workload(org: org, gvc: gvc, workload: workload)
|
167
189
|
end
|
168
190
|
|
169
191
|
def workload_connect(workload, location:, container: nil, shell: nil)
|
@@ -180,6 +202,40 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
180
202
|
perform!(cmd)
|
181
203
|
end
|
182
204
|
|
205
|
+
# domain
|
206
|
+
|
207
|
+
def find_domain_route(data)
|
208
|
+
port = data["spec"]["ports"].find { |current_port| current_port["number"] == 80 || current_port["number"] == 443 }
|
209
|
+
return nil if port.nil? || port["routes"].nil?
|
210
|
+
|
211
|
+
route = port["routes"].find { |current_route| current_route["prefix"] == "/" }
|
212
|
+
return nil if route.nil?
|
213
|
+
|
214
|
+
route
|
215
|
+
end
|
216
|
+
|
217
|
+
def find_domain_for(workloads)
|
218
|
+
domains = api.list_domains(org: org)["items"]
|
219
|
+
domains.find do |domain_data|
|
220
|
+
route = find_domain_route(domain_data)
|
221
|
+
next false if route.nil?
|
222
|
+
|
223
|
+
workloads.any? { |workload| route["workloadLink"].split("/").last == workload }
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def get_domain_workload(data)
|
228
|
+
route = find_domain_route(data)
|
229
|
+
route["workloadLink"].split("/").last
|
230
|
+
end
|
231
|
+
|
232
|
+
def set_domain_workload(data, workload)
|
233
|
+
route = find_domain_route(data)
|
234
|
+
route["workloadLink"] = "/org/#{org}/gvc/#{gvc}/workload/#{workload}"
|
235
|
+
|
236
|
+
api.update_domain(org: org, domain: data["name"], data: data)
|
237
|
+
end
|
238
|
+
|
183
239
|
# logs
|
184
240
|
|
185
241
|
def logs(workload:)
|
@@ -33,6 +33,29 @@ class ControlplaneApi
|
|
33
33
|
api_json_direct("/logs/org/#{org}/loki/api/v1/query_range?#{params}", method: :get, host: :logs)
|
34
34
|
end
|
35
35
|
|
36
|
+
def query_workloads(org:, gvc:, workload:, op_type:) # rubocop:disable Metrics/MethodLength
|
37
|
+
body = {
|
38
|
+
kind: "string",
|
39
|
+
spec: {
|
40
|
+
match: "all",
|
41
|
+
terms: [
|
42
|
+
{
|
43
|
+
rel: "gvc",
|
44
|
+
op: "=",
|
45
|
+
value: gvc
|
46
|
+
},
|
47
|
+
{
|
48
|
+
property: "name",
|
49
|
+
op: op_type,
|
50
|
+
value: workload
|
51
|
+
}
|
52
|
+
]
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
api_json("/org/#{org}/workload/-query", method: :post, body: body)
|
57
|
+
end
|
58
|
+
|
36
59
|
def workload_list(org:, gvc:)
|
37
60
|
api_json("/org/#{org}/gvc/#{gvc}/workload", method: :get)
|
38
61
|
end
|
@@ -45,10 +68,26 @@ class ControlplaneApi
|
|
45
68
|
api_json("/org/#{org}/gvc/#{gvc}/workload/#{workload}", method: :get)
|
46
69
|
end
|
47
70
|
|
71
|
+
def update_workload(org:, gvc:, workload:, data:)
|
72
|
+
api_json("/org/#{org}/gvc/#{gvc}/workload/#{workload}", method: :patch, body: data)
|
73
|
+
end
|
74
|
+
|
48
75
|
def workload_deployments(org:, gvc:, workload:)
|
49
76
|
api_json("/org/#{org}/gvc/#{gvc}/workload/#{workload}/deployment", method: :get)
|
50
77
|
end
|
51
78
|
|
79
|
+
def delete_workload(org:, gvc:, workload:)
|
80
|
+
api_json("/org/#{org}/gvc/#{gvc}/workload/#{workload}", method: :delete)
|
81
|
+
end
|
82
|
+
|
83
|
+
def list_domains(org:)
|
84
|
+
api_json("/org/#{org}/domain", method: :get)
|
85
|
+
end
|
86
|
+
|
87
|
+
def update_domain(org:, domain:, data:)
|
88
|
+
api_json("/org/#{org}/domain/#{domain}", method: :patch, body: data)
|
89
|
+
end
|
90
|
+
|
52
91
|
private
|
53
92
|
|
54
93
|
# switch between cpln rest and api
|
@@ -1,7 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class ControlplaneApiDirect
|
4
|
-
API_METHODS = {
|
4
|
+
API_METHODS = {
|
5
|
+
get: Net::HTTP::Get,
|
6
|
+
patch: Net::HTTP::Patch,
|
7
|
+
post: Net::HTTP::Post,
|
8
|
+
put: Net::HTTP::Put,
|
9
|
+
delete: Net::HTTP::Delete
|
10
|
+
}.freeze
|
5
11
|
API_HOSTS = { api: "https://api.cpln.io", logs: "https://logs.cpln.io" }.freeze
|
6
12
|
|
7
13
|
# API_TOKEN_REGEX = Regexp.union(
|
@@ -11,11 +17,12 @@ class ControlplaneApiDirect
|
|
11
17
|
|
12
18
|
API_TOKEN_REGEX = /^[\w\-._]+$/.freeze
|
13
19
|
|
14
|
-
def call(url, method:, host: :api) # rubocop:disable Metrics/MethodLength
|
20
|
+
def call(url, method:, host: :api, body: nil) # rubocop:disable Metrics/MethodLength
|
15
21
|
uri = URI("#{API_HOSTS[host]}#{url}")
|
16
22
|
request = API_METHODS[method].new(uri)
|
17
23
|
request["Content-Type"] = "application/json"
|
18
24
|
request["Authorization"] = api_token
|
25
|
+
request.body = body.to_json if body
|
19
26
|
|
20
27
|
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }
|
21
28
|
|
@@ -31,13 +38,16 @@ class ControlplaneApiDirect
|
|
31
38
|
end
|
32
39
|
end
|
33
40
|
|
41
|
+
# rubocop:disable Style/ClassVars
|
34
42
|
def api_token
|
35
43
|
return @@api_token if defined?(@@api_token)
|
36
44
|
|
37
|
-
@@api_token = ENV.fetch("CPLN_TOKEN",
|
45
|
+
@@api_token = ENV.fetch("CPLN_TOKEN", nil)
|
46
|
+
@@api_token = `cpln profile token`.chomp if @@api_token.nil?
|
38
47
|
return @@api_token if @@api_token.match?(API_TOKEN_REGEX)
|
39
48
|
|
40
49
|
raise "Unknown API token format. " \
|
41
50
|
"Please re-run 'cpln profile login' or set the correct CPLN_TOKEN env variable."
|
42
51
|
end
|
52
|
+
# rubocop:enable Style/ClassVars
|
43
53
|
end
|
data/lib/cpl/version.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
kind: workload
|
2
|
+
name: daily-task
|
3
|
+
spec:
|
4
|
+
# https://docs.controlplane.com/reference/workload#cron-configuration
|
5
|
+
type: cron
|
6
|
+
job:
|
7
|
+
# Run daily job at 2am, see cron docs
|
8
|
+
schedule: 0 2 * * *
|
9
|
+
# Never or OnFailure
|
10
|
+
restartPolicy: Never
|
11
|
+
containers:
|
12
|
+
- name: daily-task
|
13
|
+
args:
|
14
|
+
- bundle
|
15
|
+
- exec
|
16
|
+
- rails
|
17
|
+
- db:prepare
|
18
|
+
cpu: 50m
|
19
|
+
inheritEnv: true
|
20
|
+
image: '/org/APP_ORG/image/APP_IMAGE'
|
21
|
+
memory: 256Mi
|
22
|
+
defaultOptions:
|
23
|
+
autoscaling:
|
24
|
+
maxScale: 1
|
25
|
+
capacityAI: false
|
26
|
+
firewallConfig:
|
27
|
+
external:
|
28
|
+
outboundAllowCIDR:
|
29
|
+
- 0.0.0.0/0
|
30
|
+
identityLink: /org/APP_ORG/gvc/APP_GVC/identity/APP_GVC-identity
|
@@ -0,0 +1,24 @@
|
|
1
|
+
kind: workload
|
2
|
+
name: maintenance
|
3
|
+
spec:
|
4
|
+
type: standard
|
5
|
+
containers:
|
6
|
+
- name: maintenance
|
7
|
+
env:
|
8
|
+
- name: PORT
|
9
|
+
value: '3000'
|
10
|
+
- name: PAGE_URL
|
11
|
+
value: ''
|
12
|
+
image: 'shakacode/maintenance-mode'
|
13
|
+
ports:
|
14
|
+
- number: 3000
|
15
|
+
protocol: http
|
16
|
+
defaultOptions:
|
17
|
+
autoscaling:
|
18
|
+
maxScale: 1
|
19
|
+
capacityAI: false
|
20
|
+
timeoutSeconds: 60
|
21
|
+
firewallConfig:
|
22
|
+
external:
|
23
|
+
inboundAllowCIDR:
|
24
|
+
- 0.0.0.0/0
|
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: 0.
|
4
|
+
version: 0.7.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: 2023-
|
12
|
+
date: 2023-05-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: debug
|
@@ -151,6 +151,48 @@ dependencies:
|
|
151
151
|
- - "~>"
|
152
152
|
- !ruby/object:Gem::Version
|
153
153
|
version: 0.22.0
|
154
|
+
- !ruby/object:Gem::Dependency
|
155
|
+
name: timecop
|
156
|
+
requirement: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - "~>"
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: 0.9.6
|
161
|
+
type: :development
|
162
|
+
prerelease: false
|
163
|
+
version_requirements: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - "~>"
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
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
|
+
- !ruby/object:Gem::Dependency
|
183
|
+
name: webmock
|
184
|
+
requirement: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - "~>"
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: 3.18.1
|
189
|
+
type: :development
|
190
|
+
prerelease: false
|
191
|
+
version_requirements: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - "~>"
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: 3.18.1
|
154
196
|
description: CLI for providing Heroku-like platform-as-a-service on Control Plane
|
155
197
|
email:
|
156
198
|
- justin@shakacode.com
|
@@ -197,6 +239,10 @@ files:
|
|
197
239
|
- lib/command/info.rb
|
198
240
|
- lib/command/latest_image.rb
|
199
241
|
- lib/command/logs.rb
|
242
|
+
- lib/command/maintenance.rb
|
243
|
+
- lib/command/maintenance_off.rb
|
244
|
+
- lib/command/maintenance_on.rb
|
245
|
+
- lib/command/maintenance_set_page.rb
|
200
246
|
- lib/command/no_command.rb
|
201
247
|
- lib/command/open.rb
|
202
248
|
- lib/command/promote_app_from_upstream.rb
|
@@ -205,6 +251,7 @@ files:
|
|
205
251
|
- lib/command/ps_start.rb
|
206
252
|
- lib/command/ps_stop.rb
|
207
253
|
- lib/command/run.rb
|
254
|
+
- lib/command/run_cleanup.rb
|
208
255
|
- lib/command/run_detached.rb
|
209
256
|
- lib/command/setup_app.rb
|
210
257
|
- lib/command/test.rb
|
@@ -224,8 +271,10 @@ files:
|
|
224
271
|
- script/add_command
|
225
272
|
- script/generate_commands_docs
|
226
273
|
- script/rename_command
|
274
|
+
- templates/daily-task.yml
|
227
275
|
- templates/gvc.yml
|
228
276
|
- templates/identity.yml
|
277
|
+
- templates/maintenance.yml
|
229
278
|
- templates/memcached.yml
|
230
279
|
- templates/postgres.yml
|
231
280
|
- templates/rails.yml
|