cpl 1.1.2 → 1.3.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/check_cpln_links.yml +19 -0
- data/.github/workflows/rspec.yml +1 -1
- data/.overcommit.yml +3 -0
- data/CHANGELOG.md +47 -2
- data/CONTRIBUTING.md +2 -6
- data/Gemfile.lock +8 -8
- data/README.md +57 -15
- data/docs/commands.md +29 -23
- data/docs/dns.md +9 -0
- data/docs/migrating.md +3 -3
- data/examples/controlplane.yml +67 -4
- data/lib/command/apply_template.rb +2 -1
- data/lib/command/base.rb +62 -0
- data/lib/command/build_image.rb +5 -1
- data/lib/command/config.rb +0 -5
- data/lib/command/copy_image_from_upstream.rb +5 -4
- data/lib/command/delete.rb +40 -11
- data/lib/command/env.rb +1 -0
- data/lib/command/generate.rb +45 -0
- data/lib/command/info.rb +15 -33
- data/lib/command/latest_image.rb +1 -0
- data/lib/command/maintenance.rb +9 -4
- data/lib/command/maintenance_off.rb +8 -4
- data/lib/command/maintenance_on.rb +8 -4
- data/lib/command/no_command.rb +1 -0
- data/lib/command/ps.rb +5 -1
- data/lib/command/run.rb +20 -23
- data/lib/command/run_detached.rb +38 -30
- data/lib/command/setup_app.rb +3 -3
- data/lib/command/version.rb +1 -0
- data/lib/core/config.rb +194 -66
- data/lib/core/controlplane.rb +28 -7
- data/lib/core/controlplane_api.rb +13 -1
- data/lib/core/controlplane_api_direct.rb +18 -2
- data/lib/core/helpers.rb +16 -0
- data/lib/core/shell.rb +25 -3
- data/lib/cpl/version.rb +1 -1
- data/lib/cpl.rb +32 -3
- data/lib/generator_templates/Dockerfile +27 -0
- data/lib/generator_templates/controlplane.yml +57 -0
- data/lib/generator_templates/entrypoint.sh +8 -0
- data/lib/generator_templates/templates/gvc.yml +21 -0
- data/lib/generator_templates/templates/postgres.yml +176 -0
- data/lib/generator_templates/templates/rails.yml +36 -0
- data/script/check_cpln_links +45 -0
- metadata +14 -3
data/lib/core/controlplane.rb
CHANGED
@@ -23,9 +23,10 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def profile_create(profile, token)
|
26
|
+
sensitive_data_pattern = /(?<=--token )(\S+)/
|
26
27
|
cmd = "cpln profile create #{profile} --token #{token}"
|
27
28
|
cmd += " > /dev/null" if Shell.should_hide_output?
|
28
|
-
perform!(cmd)
|
29
|
+
perform!(cmd, sensitive_data_pattern: sensitive_data_pattern)
|
29
30
|
end
|
30
31
|
|
31
32
|
def profile_delete(profile)
|
@@ -43,11 +44,13 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
43
44
|
api.query_images(org: a_org, gvc: a_gvc, gvc_op_type: gvc_op)
|
44
45
|
end
|
45
46
|
|
46
|
-
def image_build(image, dockerfile:, build_args: [], push: true)
|
47
|
+
def image_build(image, dockerfile:, docker_args: [], build_args: [], push: true)
|
47
48
|
# https://docs.controlplane.com/guides/push-image#step-2
|
48
49
|
# Might need to use `docker buildx build` if compatiblitity issues arise
|
49
50
|
cmd = "docker build --platform=linux/amd64 -t #{image} -f #{dockerfile}"
|
51
|
+
cmd += " --progress=plain" if ControlplaneApiDirect.trace
|
50
52
|
|
53
|
+
cmd += " #{docker_args.join(' ')}" if docker_args.any?
|
51
54
|
build_args.each { |build_arg| cmd += " --build-arg #{build_arg}" }
|
52
55
|
cmd += " #{config.app_dir}"
|
53
56
|
perform!(cmd)
|
@@ -234,6 +237,16 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
234
237
|
perform!(cmd)
|
235
238
|
end
|
236
239
|
|
240
|
+
# volumeset
|
241
|
+
|
242
|
+
def fetch_volumesets(a_gvc = gvc)
|
243
|
+
api.list_volumesets(org: org, gvc: a_gvc)
|
244
|
+
end
|
245
|
+
|
246
|
+
def delete_volumeset(volumeset, a_gvc = gvc)
|
247
|
+
api.delete_volumeset(org: org, gvc: a_gvc, volumeset: volumeset)
|
248
|
+
end
|
249
|
+
|
237
250
|
# domain
|
238
251
|
|
239
252
|
def find_domain_route(data)
|
@@ -252,13 +265,21 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
252
265
|
route = find_domain_route(domain_data)
|
253
266
|
next false if route.nil?
|
254
267
|
|
255
|
-
workloads.any? { |workload| route["workloadLink"].
|
268
|
+
workloads.any? { |workload| route["workloadLink"].match?(%r{/org/#{org}/gvc/#{gvc}/workload/#{workload}}) }
|
256
269
|
end
|
257
270
|
end
|
258
271
|
|
259
|
-
def
|
272
|
+
def fetch_domain(domain)
|
273
|
+
domain_data = api.fetch_domain(org: org, domain: domain)
|
274
|
+
route = find_domain_route(domain_data)
|
275
|
+
return nil if route.nil?
|
276
|
+
|
277
|
+
domain_data
|
278
|
+
end
|
279
|
+
|
280
|
+
def domain_workload_matches?(data, workload)
|
260
281
|
route = find_domain_route(data)
|
261
|
-
route["workloadLink"].
|
282
|
+
route["workloadLink"].match?(%r{/org/#{org}/gvc/#{gvc}/workload/#{workload}})
|
262
283
|
end
|
263
284
|
|
264
285
|
def set_domain_workload(data, workload)
|
@@ -346,8 +367,8 @@ class Controlplane # rubocop:disable Metrics/ClassLength
|
|
346
367
|
system(cmd)
|
347
368
|
end
|
348
369
|
|
349
|
-
def perform!(cmd)
|
350
|
-
Shell.debug("CMD", cmd)
|
370
|
+
def perform!(cmd, sensitive_data_pattern: nil)
|
371
|
+
Shell.debug("CMD", cmd, sensitive_data_pattern: sensitive_data_pattern)
|
351
372
|
|
352
373
|
system(cmd) || exit(false)
|
353
374
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class ControlplaneApi
|
3
|
+
class ControlplaneApi # rubocop:disable Metrics/ClassLength
|
4
4
|
def gvc_list(org:)
|
5
5
|
api_json("/org/#{org}/gvc", method: :get)
|
6
6
|
end
|
@@ -86,6 +86,18 @@ class ControlplaneApi
|
|
86
86
|
api_json("/org/#{org}/gvc/#{gvc}/workload/#{workload}", method: :delete)
|
87
87
|
end
|
88
88
|
|
89
|
+
def list_volumesets(org:, gvc:)
|
90
|
+
api_json("/org/#{org}/gvc/#{gvc}/volumeset", method: :get)
|
91
|
+
end
|
92
|
+
|
93
|
+
def delete_volumeset(org:, gvc:, volumeset:)
|
94
|
+
api_json("/org/#{org}/gvc/#{gvc}/volumeset/#{volumeset}", method: :delete)
|
95
|
+
end
|
96
|
+
|
97
|
+
def fetch_domain(org:, domain:)
|
98
|
+
api_json("/org/#{org}/domain/#{domain}", method: :get)
|
99
|
+
end
|
100
|
+
|
89
101
|
def list_domains(org:)
|
90
102
|
api_json("/org/#{org}/domain", method: :get)
|
91
103
|
end
|
@@ -17,7 +17,12 @@ class ControlplaneApiDirect
|
|
17
17
|
|
18
18
|
API_TOKEN_REGEX = /^[\w\-._]+$/.freeze
|
19
19
|
|
20
|
-
|
20
|
+
class << self
|
21
|
+
attr_accessor :trace
|
22
|
+
end
|
23
|
+
|
24
|
+
def call(url, method:, host: :api, body: nil) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
25
|
+
trace = ControlplaneApiDirect.trace
|
21
26
|
uri = URI("#{api_host(host)}#{url}")
|
22
27
|
request = API_METHODS[method].new(uri)
|
23
28
|
request["Content-Type"] = "application/json"
|
@@ -26,7 +31,11 @@ class ControlplaneApiDirect
|
|
26
31
|
|
27
32
|
Shell.debug(method.upcase, "#{uri} #{body&.to_json}")
|
28
33
|
|
29
|
-
|
34
|
+
http = Net::HTTP.new(uri.hostname, uri.port)
|
35
|
+
http.use_ssl = uri.scheme == "https"
|
36
|
+
http.set_debug_output($stdout) if trace
|
37
|
+
|
38
|
+
response = http.start { |ht| ht.request(request) }
|
30
39
|
|
31
40
|
case response
|
32
41
|
when Net::HTTPOK
|
@@ -35,6 +44,9 @@ class ControlplaneApiDirect
|
|
35
44
|
true
|
36
45
|
when Net::HTTPNotFound
|
37
46
|
nil
|
47
|
+
when Net::HTTPForbidden
|
48
|
+
org = self.class.parse_org(url)
|
49
|
+
raise("Double check your org #{org}. #{response} #{response.body}")
|
38
50
|
else
|
39
51
|
raise("#{response} #{response.body}")
|
40
52
|
end
|
@@ -65,4 +77,8 @@ class ControlplaneApiDirect
|
|
65
77
|
remove_class_variable(:@@api_token) if defined?(@@api_token)
|
66
78
|
end
|
67
79
|
# rubocop:enable Style/ClassVars
|
80
|
+
|
81
|
+
def self.parse_org(url)
|
82
|
+
url.match(%r{^/org/([^/]+)})[1]
|
83
|
+
end
|
68
84
|
end
|
data/lib/core/helpers.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "securerandom"
|
4
|
+
|
5
|
+
module Helpers
|
6
|
+
def strip_str_and_validate(str)
|
7
|
+
return str if str.nil?
|
8
|
+
|
9
|
+
str = str.strip
|
10
|
+
str.empty? ? nil : str
|
11
|
+
end
|
12
|
+
|
13
|
+
def random_four_digits
|
14
|
+
SecureRandom.random_number(1000..9999)
|
15
|
+
end
|
16
|
+
end
|
data/lib/core/shell.rb
CHANGED
@@ -36,7 +36,7 @@ class Shell
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def self.confirm(message)
|
39
|
-
shell.yes?("#{message} (y/
|
39
|
+
shell.yes?("#{message} (y/N)")
|
40
40
|
end
|
41
41
|
|
42
42
|
def self.warn(message)
|
@@ -55,11 +55,33 @@ class Shell
|
|
55
55
|
@verbose = verbose
|
56
56
|
end
|
57
57
|
|
58
|
-
def self.debug(prefix, message)
|
59
|
-
|
58
|
+
def self.debug(prefix, message, sensitive_data_pattern: nil)
|
59
|
+
return unless verbose
|
60
|
+
|
61
|
+
filtered_message = hide_sensitive_data(message, sensitive_data_pattern)
|
62
|
+
stderr.puts("\n[#{color(prefix, :red)}] #{filtered_message}")
|
60
63
|
end
|
61
64
|
|
62
65
|
def self.should_hide_output?
|
63
66
|
tmp_stderr && !verbose
|
64
67
|
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Hide sensitive data based on the passed pattern
|
71
|
+
#
|
72
|
+
# @param [String] message
|
73
|
+
# The message to get processed.
|
74
|
+
# @param [Regexp, nil] pattern
|
75
|
+
# The regular expression to be used. If not provided, no filter gets applied.
|
76
|
+
#
|
77
|
+
# @return [String]
|
78
|
+
# Filtered message.
|
79
|
+
#
|
80
|
+
# @example
|
81
|
+
# hide_sensitive_data("--token abcd", /(?<=--token )(\S+)/)
|
82
|
+
def self.hide_sensitive_data(message, pattern = nil)
|
83
|
+
return message unless pattern.is_a?(Regexp)
|
84
|
+
|
85
|
+
message.gsub(pattern, "XXXXXXX")
|
86
|
+
end
|
65
87
|
end
|
data/lib/cpl/version.rb
CHANGED
data/lib/cpl.rb
CHANGED
@@ -141,11 +141,13 @@ 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.common_options
|
145
|
+
accepts_extra_options = command_class::ACCEPTS_EXTRA_OPTIONS
|
145
146
|
description = command_class::DESCRIPTION
|
146
147
|
long_description = command_class::LONG_DESCRIPTION
|
147
148
|
examples = command_class::EXAMPLES
|
148
149
|
hide = command_class::HIDE || deprecated
|
150
|
+
with_info_header = command_class::WITH_INFO_HEADER
|
149
151
|
|
150
152
|
long_description += "\n#{examples}" if examples.length.positive?
|
151
153
|
|
@@ -160,6 +162,10 @@ module Cpl
|
|
160
162
|
method_option(option[:name], **option[:params])
|
161
163
|
end
|
162
164
|
|
165
|
+
# We'll handle required options manually in `Config`
|
166
|
+
required_options = command_options.select { |option| option[:params][:required] }.map { |option| option[:name] }
|
167
|
+
disable_required_check! name_for_method.to_sym if required_options.any?
|
168
|
+
|
163
169
|
define_method(name_for_method) do |*provided_args| # rubocop:disable Metrics/MethodLength
|
164
170
|
if deprecated
|
165
171
|
::Shell.warn_deprecated("Command '#{command_key}' is deprecated, " \
|
@@ -173,10 +179,14 @@ module Cpl
|
|
173
179
|
default_args
|
174
180
|
end
|
175
181
|
|
176
|
-
|
182
|
+
if (args.empty? && requires_args) || (!args.empty? && !requires_args && !accepts_extra_options)
|
183
|
+
raise_args_error.call(args, nil)
|
184
|
+
end
|
177
185
|
|
178
186
|
begin
|
179
|
-
config = Config.new(args, options)
|
187
|
+
config = Config.new(args, options, required_options)
|
188
|
+
|
189
|
+
Cpl::Cli.show_info_header(config) if with_info_header
|
180
190
|
|
181
191
|
command_class.new(config).call
|
182
192
|
rescue RuntimeError => e
|
@@ -186,6 +196,25 @@ module Cpl
|
|
186
196
|
rescue StandardError => e
|
187
197
|
::Shell.abort("Unable to load command: #{e.message}")
|
188
198
|
end
|
199
|
+
|
200
|
+
def self.show_info_header(config) # rubocop:disable Metrics/MethodLength
|
201
|
+
return if @showed_info_header
|
202
|
+
|
203
|
+
rows = {}
|
204
|
+
rows["ORG"] = config.org || "NOT PROVIDED!"
|
205
|
+
rows["ORG"] += " (comes from CPLN_ORG env var)" if config.org_comes_from_env
|
206
|
+
rows["APP"] = config.app || "NOT PROVIDED!"
|
207
|
+
rows["APP"] += " (comes from CPLN_APP env var)" if config.app_comes_from_env
|
208
|
+
|
209
|
+
rows.each do |key, value|
|
210
|
+
puts "#{key}: #{value}"
|
211
|
+
end
|
212
|
+
|
213
|
+
@showed_info_header = true
|
214
|
+
|
215
|
+
# Add a newline after the info header
|
216
|
+
puts
|
217
|
+
end
|
189
218
|
end
|
190
219
|
end
|
191
220
|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
FROM ruby:3.1.2
|
2
|
+
|
3
|
+
RUN apt-get update
|
4
|
+
|
5
|
+
WORKDIR /app
|
6
|
+
|
7
|
+
# install ruby gems
|
8
|
+
COPY Gemfile* ./
|
9
|
+
|
10
|
+
RUN bundle config set without 'development test' && \
|
11
|
+
bundle config set with 'staging production' && \
|
12
|
+
bundle install --jobs=3 --retry=3
|
13
|
+
|
14
|
+
COPY . ./
|
15
|
+
|
16
|
+
ENV RAILS_ENV=production
|
17
|
+
|
18
|
+
# compiling assets requires any value for ENV of SECRET_KEY_BASE
|
19
|
+
ENV SECRET_KEY_BASE=NOT_USED_NON_BLANK
|
20
|
+
|
21
|
+
RUN rails assets:precompile
|
22
|
+
|
23
|
+
# add entrypoint
|
24
|
+
COPY .controlplane/entrypoint.sh ./
|
25
|
+
ENTRYPOINT ["/app/entrypoint.sh"]
|
26
|
+
|
27
|
+
CMD ["rails", "s"]
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Keys beginning with "cpln_" correspond to your settings in Control Plane.
|
2
|
+
|
3
|
+
# You can opt out of allowing the use of CPLN_ORG and CPLN_APP env vars
|
4
|
+
# to avoid any accidents with the wrong org / app.
|
5
|
+
allow_org_override_by_env: true
|
6
|
+
allow_app_override_by_env: true
|
7
|
+
|
8
|
+
aliases:
|
9
|
+
common: &common
|
10
|
+
# Organization name for staging (customize to your needs).
|
11
|
+
# Production apps will use a different organization, specified below, for security.
|
12
|
+
cpln_org: my-org-staging
|
13
|
+
|
14
|
+
# Example apps use only one location. Control Plane offers the ability to use multiple locations.
|
15
|
+
# TODO: Allow specification of multiple locations.
|
16
|
+
default_location: aws-us-east-2
|
17
|
+
|
18
|
+
# Configure the workload name used as a template for one-off scripts, like a Heroku one-off dyno.
|
19
|
+
one_off_workload: rails
|
20
|
+
|
21
|
+
# Workloads that are for the application itself and are using application Docker images.
|
22
|
+
app_workloads:
|
23
|
+
- rails
|
24
|
+
|
25
|
+
# Additional "service type" workloads, using non-application Docker images.
|
26
|
+
additional_workloads:
|
27
|
+
- postgres
|
28
|
+
|
29
|
+
# Configure the workload name used when maintenance mode is on (defaults to "maintenance")
|
30
|
+
maintenance_workload: maintenance
|
31
|
+
|
32
|
+
apps:
|
33
|
+
my-app-staging:
|
34
|
+
# Use the values from the common section above.
|
35
|
+
<<: *common
|
36
|
+
my-app-review:
|
37
|
+
<<: *common
|
38
|
+
# If `match_if_app_name_starts_with` is `true`, then use this config for app names starting with this name,
|
39
|
+
# e.g., "my-app-review-pr123", "my-app-review-anything-goes", etc.
|
40
|
+
match_if_app_name_starts_with: true
|
41
|
+
my-app-production:
|
42
|
+
<<: *common
|
43
|
+
|
44
|
+
# You can also opt out of allowing the use of CPLN_ORG and CPLN_APP env vars per app.
|
45
|
+
# It's recommended to leave this off for production, to avoid any accidents.
|
46
|
+
allow_org_override_by_env: false
|
47
|
+
allow_app_override_by_env: false
|
48
|
+
|
49
|
+
# Use a different organization for production.
|
50
|
+
cpln_org: my-org-production
|
51
|
+
# Allows running the command `cpl promote-app-from-upstream -a my-app-production`
|
52
|
+
# to promote the staging app to production.
|
53
|
+
upstream: my-app-staging
|
54
|
+
my-app-other:
|
55
|
+
<<: *common
|
56
|
+
# You can specify a different `Dockerfile` relative to the `.controlplane/` directory (defaults to "Dockerfile").
|
57
|
+
dockerfile: ../some_other/Dockerfile
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Template setup of the GVC, roughly corresponding to a Heroku app
|
2
|
+
kind: gvc
|
3
|
+
name: APP_GVC
|
4
|
+
spec:
|
5
|
+
# For using templates for test apps, put ENV values here, stored in git repo.
|
6
|
+
# Production apps will have values configured manually after app creation.
|
7
|
+
env:
|
8
|
+
- name: DATABASE_URL
|
9
|
+
# Password does not matter because host postgres.APP_GVC.cpln.local can only be accessed
|
10
|
+
# locally within CPLN GVC, and postgres running on a CPLN workload is something only for a
|
11
|
+
# test app that lacks persistence.
|
12
|
+
value: 'postgres://the_user:the_password@postgres.APP_GVC.cpln.local:5432/APP_GVC'
|
13
|
+
- name: RAILS_ENV
|
14
|
+
value: production
|
15
|
+
- name: RAILS_SERVE_STATIC_FILES
|
16
|
+
value: 'true'
|
17
|
+
|
18
|
+
# Part of standard configuration
|
19
|
+
staticPlacement:
|
20
|
+
locationLinks:
|
21
|
+
- /org/APP_ORG/location/APP_LOCATION
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# Comes from example at
|
2
|
+
# https://github.com/controlplane-com/examples/blob/main/examples/postgres/manifest.yaml
|
3
|
+
|
4
|
+
kind: volumeset
|
5
|
+
name: postgres-poc-vs
|
6
|
+
description: postgres-poc-vs
|
7
|
+
spec:
|
8
|
+
autoscaling:
|
9
|
+
maxCapacity: 1000
|
10
|
+
minFreePercentage: 1
|
11
|
+
scalingFactor: 1.1
|
12
|
+
fileSystemType: ext4
|
13
|
+
initialCapacity: 10
|
14
|
+
performanceClass: general-purpose-ssd
|
15
|
+
snapshots:
|
16
|
+
createFinalSnapshot: true
|
17
|
+
retentionDuration: 7d
|
18
|
+
|
19
|
+
---
|
20
|
+
kind: secret
|
21
|
+
name: postgres-poc-credentials
|
22
|
+
description: ''
|
23
|
+
type: dictionary
|
24
|
+
data:
|
25
|
+
password: the_password #Replace this with a real password
|
26
|
+
username: the_user #Replace this with a real username
|
27
|
+
|
28
|
+
---
|
29
|
+
kind: secret
|
30
|
+
name: postgres-poc-entrypoint-script
|
31
|
+
type: opaque
|
32
|
+
data:
|
33
|
+
encoding: base64
|
34
|
+
payload: >-
|
35
|
+
IyEvdXNyL2Jpbi9lbnYgYmFzaAoKc291cmNlIC91c3IvbG9jYWwvYmluL2RvY2tlci1lbnRyeXBvaW50LnNoCgppbnN0YWxsX2RlcHMoKSB7CiAgYXB0LWdldCB1cGRhdGUgLXkgPiAvZGV2L251bGwKICBhcHQtZ2V0IGluc3RhbGwgY3VybCAteSA+IC9kZXYvbnVsbAogIGFwdC1nZXQgaW5zdGFsbCB1bnppcCAteSA+IC9kZXYvbnVsbAogIGN1cmwgImh0dHBzOi8vYXdzY2xpLmFtYXpvbmF3cy5jb20vYXdzY2xpLWV4ZS1saW51eC14ODZfNjQuemlwIiAtbyAiYXdzY2xpdjIuemlwIiA+IC9kZXYvbnVsbAogIHVuemlwIGF3c2NsaXYyLnppcCA+IC9kZXYvbnVsbAogIC4vYXdzL2luc3RhbGwgPiAvZGV2L251bGwKfQoKZGJfaGFzX2JlZW5fcmVzdG9yZWQoKSB7CiAgaWYgWyAhIC1mICIkUEdEQVRBL0NQTE5fUkVTVE9SRUQiIF07IHRoZW4KICAgIHJldHVybiAxCiAgZmkKCiAgaWYgISBncmVwIC1xICJcLT4gJDEkIiAiJFBHREFUQS9DUExOX1JFU1RPUkVEIjsgdGhlbgogICAgcmV0dXJuIDEKICBlbHNlCiAgICByZXR1cm4gMAogIGZpCn0KCnJlc3RvcmVfZGIoKSB7Cgl3aGlsZSBbICEgLVMgL3Zhci9ydW4vcG9zdGdyZXNxbC8ucy5QR1NRTC41NDMyIF0KCWRvCiAgICBlY2hvICJXYWl0aW5nIDVzIGZvciBkYiBzb2NrZXQgdG8gYmUgYXZhaWxhYmxlIgogICAgc2xlZXAgNXMKICBkb25lCgoKCWlmICEgZGJfaGFzX2JlZW5fcmVzdG9yZWQgIiQxIjsgdGhlbgoJICBlY2hvICJJdCBhcHBlYXJzIGRiICckMScgaGFzIG5vdCB5ZXQgYmVlbiByZXN0b3JlZCBmcm9tIFMzLiBBdHRlbXB0aW5nIHRvIHJlc3RvcmUgJDEgZnJvbSAkMiIKCSAgaW5zdGFsbF9kZXBzCgkgIGRvY2tlcl9zZXR1cF9kYiAjRW5zdXJlcyAkUE9TVEdSRVNfREIgZXhpc3RzIChkZWZpbmVkIGluIHRoZSBlbnRyeXBvaW50IHNjcmlwdCBmcm9tIHRoZSBwb3N0Z3JlcyBkb2NrZXIgaW1hZ2UpCgkgIGF3cyBzMyBjcCAiJDIiIC0gfCBwZ19yZXN0b3JlIC0tY2xlYW4gLS1uby1hY2wgLS1uby1vd25lciAtZCAiJDEiIC1VICIkUE9TVEdSRVNfVVNFUiIKCSAgZWNobyAiJChkYXRlKTogJDIgLT4gJDEiIHwgY2F0ID4+ICIkUEdEQVRBL0NQTE5fUkVTVE9SRUQiCgllbHNlCgkgIGVjaG8gIkRiICckMScgYWxyZWFkeSBleGlzdHMuIFJlYWR5ISIKICBmaQp9CgpfbWFpbiAiJEAiICYKYmFja2dyb3VuZFByb2Nlc3M9JCEKCmlmIFsgLW4gIiRQT1NUR1JFU19BUkNISVZFX1VSSSIgXTsgdGhlbgogIHJlc3RvcmVfZGIgIiRQT1NUR1JFU19EQiIgIiRQT1NUR1JFU19BUkNISVZFX1VSSSIKZWxzZQogIGVjaG8gIkRlY2xpbmluZyB0byByZXN0b3JlIHRoZSBkYiBiZWNhdXNlIG5vIGFyY2hpdmUgdXJpIHdhcyBwcm92aWRlZCIKZmkKCndhaXQgJGJhY2tncm91bmRQcm9jZXNzCgoK
|
36
|
+
|
37
|
+
#Here is the ASCII-encoded version of the script in the secret above
|
38
|
+
#!/usr/bin/env bash
|
39
|
+
#
|
40
|
+
#source /usr/local/bin/docker-entrypoint.sh
|
41
|
+
#
|
42
|
+
#install_deps() {
|
43
|
+
# apt-get update -y > /dev/null
|
44
|
+
# apt-get install curl -y > /dev/null
|
45
|
+
# apt-get install unzip -y > /dev/null
|
46
|
+
# curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" > /dev/null
|
47
|
+
# unzip awscliv2.zip > /dev/null
|
48
|
+
# ./aws/install > /dev/null
|
49
|
+
#}
|
50
|
+
#
|
51
|
+
#db_has_been_restored() {
|
52
|
+
# if [ ! -f "$PGDATA/CPLN_RESTORED" ]; then
|
53
|
+
# return 1
|
54
|
+
# fi
|
55
|
+
#
|
56
|
+
# if ! grep -q "\-> $1$" "$PGDATA/CPLN_RESTORED"; then
|
57
|
+
# return 1
|
58
|
+
# else
|
59
|
+
# return 0
|
60
|
+
# fi
|
61
|
+
#}
|
62
|
+
#
|
63
|
+
#restore_db() {
|
64
|
+
# while [ ! -S /var/run/postgresql/.s.PGSQL.5432 ]
|
65
|
+
# do
|
66
|
+
# echo "Waiting 5s for db socket to be available"
|
67
|
+
# sleep 5s
|
68
|
+
# done
|
69
|
+
#
|
70
|
+
#
|
71
|
+
# if ! db_has_been_restored "$1"; then
|
72
|
+
# echo "It appears db '$1' has not yet been restored from S3. Attempting to restore $1 from $2"
|
73
|
+
# install_deps
|
74
|
+
# docker_setup_db #Ensures $POSTGRES_DB exists (defined in the entrypoint script from the postgres docker image)
|
75
|
+
# aws s3 cp "$2" - | pg_restore --clean --no-acl --no-owner -d "$1" -U "$POSTGRES_USER"
|
76
|
+
# echo "$(date): $2 -> $1" | cat >> "$PGDATA/CPLN_RESTORED"
|
77
|
+
# else
|
78
|
+
# echo "Db '$1' already exists. Ready!"
|
79
|
+
# fi
|
80
|
+
#}
|
81
|
+
#
|
82
|
+
#_main "$@" &
|
83
|
+
#backgroundProcess=$!
|
84
|
+
#
|
85
|
+
#if [ -n "$POSTGRES_ARCHIVE_URI" ]; then
|
86
|
+
# restore_db "$POSTGRES_DB" "$POSTGRES_ARCHIVE_URI"
|
87
|
+
#else
|
88
|
+
# echo "Declining to restore the db because no archive uri was provided"
|
89
|
+
#fi
|
90
|
+
#
|
91
|
+
#wait $backgroundProcess
|
92
|
+
|
93
|
+
---
|
94
|
+
kind: identity
|
95
|
+
name: postgres-poc-identity
|
96
|
+
description: postgres-poc-identity
|
97
|
+
|
98
|
+
---
|
99
|
+
kind: policy
|
100
|
+
name: postgres-poc-access
|
101
|
+
description: postgres-poc-access
|
102
|
+
bindings:
|
103
|
+
- permissions:
|
104
|
+
- reveal
|
105
|
+
# Uncomment these two
|
106
|
+
# - use
|
107
|
+
# - view
|
108
|
+
principalLinks:
|
109
|
+
- //gvc/APP_GVC/identity/postgres-poc-identity
|
110
|
+
targetKind: secret
|
111
|
+
targetLinks:
|
112
|
+
- //secret/postgres-poc-credentials
|
113
|
+
- //secret/postgres-poc-entrypoint-script
|
114
|
+
|
115
|
+
---
|
116
|
+
kind: workload
|
117
|
+
name: postgres
|
118
|
+
description: postgres
|
119
|
+
spec:
|
120
|
+
type: stateful
|
121
|
+
containers:
|
122
|
+
- cpu: 1000m
|
123
|
+
memory: 512Mi
|
124
|
+
env:
|
125
|
+
# Uncomment next two envs will cause the db to be restored from the archive uri
|
126
|
+
# - name: POSTGRES_ARCHIVE_URI #Use this var to control the automatic restore behavior. If you leave it out, the db will start empty.
|
127
|
+
# value: s3://YOUR_BUCKET/PATH_TO_ARCHIVE_FILE
|
128
|
+
# - name: POSTGRES_DB #The name of the initial db in case of doing a restore
|
129
|
+
# value: test
|
130
|
+
- name: PGDATA #The location postgres stores the db. This can be anything other than /var/lib/postgresql/data, but it must be inside the mount point for the volume set
|
131
|
+
value: "/var/lib/postgresql/data/pg_data"
|
132
|
+
- name: POSTGRES_PASSWORD #The password for the default user
|
133
|
+
value: cpln://secret/postgres-poc-credentials.password
|
134
|
+
- name: POSTGRES_USER #The name of the default user
|
135
|
+
value: cpln://secret/postgres-poc-credentials.username
|
136
|
+
name: stateful
|
137
|
+
image: postgres:15
|
138
|
+
command: /bin/bash
|
139
|
+
args:
|
140
|
+
- "-c"
|
141
|
+
- "cat /usr/local/bin/cpln-entrypoint.sh >> ./cpln-entrypoint.sh && chmod u+x ./cpln-entrypoint.sh && ./cpln-entrypoint.sh postgres"
|
142
|
+
#command: "cpln-entrypoint.sh"
|
143
|
+
#args:
|
144
|
+
# - "postgres"
|
145
|
+
ports:
|
146
|
+
- number: 5432
|
147
|
+
protocol: tcp
|
148
|
+
volumes:
|
149
|
+
- uri: cpln://volumeset/postgres-poc-vs
|
150
|
+
path: "/var/lib/postgresql/data"
|
151
|
+
# Make the ENV value for the entry script a file
|
152
|
+
- uri: cpln://secret/postgres-poc-entrypoint-script
|
153
|
+
path: "/usr/local/bin/cpln-entrypoint.sh"
|
154
|
+
inheritEnv: false
|
155
|
+
livenessProbe:
|
156
|
+
tcpSocket:
|
157
|
+
port: 5432
|
158
|
+
failureThreshold: 1
|
159
|
+
readinessProbe:
|
160
|
+
tcpSocket:
|
161
|
+
port: 5432
|
162
|
+
failureThreshold: 1
|
163
|
+
identityLink: //identity/postgres-poc-identity
|
164
|
+
defaultOptions:
|
165
|
+
capacityAI: false
|
166
|
+
autoscaling:
|
167
|
+
metric: cpu
|
168
|
+
target: 95
|
169
|
+
maxScale: 1
|
170
|
+
firewallConfig:
|
171
|
+
external:
|
172
|
+
inboundAllowCIDR: []
|
173
|
+
outboundAllowCIDR:
|
174
|
+
- 0.0.0.0/0
|
175
|
+
internal:
|
176
|
+
inboundAllowType: same-gvc
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Template setup of Rails server workload, roughly corresponding to Heroku dyno
|
2
|
+
# type within Procfile.
|
3
|
+
kind: workload
|
4
|
+
name: rails
|
5
|
+
spec:
|
6
|
+
type: standard
|
7
|
+
containers:
|
8
|
+
- name: rails
|
9
|
+
# 300m is a good starting place for a test app. You can experiment with CPU configuration
|
10
|
+
# once your app is running.
|
11
|
+
cpu: 300m
|
12
|
+
env:
|
13
|
+
- name: LOG_LEVEL
|
14
|
+
value: debug
|
15
|
+
# Inherit other ENV values from GVC
|
16
|
+
inheritEnv: true
|
17
|
+
image: '/org/APP_ORG/image/APP_IMAGE'
|
18
|
+
# 512 corresponds to a standard 1x dyno type
|
19
|
+
memory: 512Mi
|
20
|
+
ports:
|
21
|
+
- number: 3000
|
22
|
+
protocol: http
|
23
|
+
defaultOptions:
|
24
|
+
# Start out like this for "test apps"
|
25
|
+
autoscaling:
|
26
|
+
# Max of 1 effectively disables autoscaling, so a like a Heroku dyno count of 1
|
27
|
+
maxScale: 1
|
28
|
+
capacityAI: false
|
29
|
+
firewallConfig:
|
30
|
+
external:
|
31
|
+
# Default to allow public access to Rails server
|
32
|
+
inboundAllowCIDR:
|
33
|
+
- 0.0.0.0/0
|
34
|
+
# Could configure outbound for more security
|
35
|
+
outboundAllowCIDR:
|
36
|
+
- 0.0.0.0/0
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
bad_links=("controlplane.com/shakacode")
|
4
|
+
proper_links=("shakacode.controlplane.com")
|
5
|
+
|
6
|
+
bold=$(tput bold)
|
7
|
+
normal=$(tput sgr0)
|
8
|
+
|
9
|
+
exit_status=0
|
10
|
+
accumulated_results=""
|
11
|
+
seen_bad_links_indexes=()
|
12
|
+
|
13
|
+
for ((idx = 0; idx < ${#bad_links[@]}; idx++)); do
|
14
|
+
results=$(git grep \
|
15
|
+
--recursive \
|
16
|
+
--line-number \
|
17
|
+
--fixed-strings \
|
18
|
+
--break \
|
19
|
+
--heading \
|
20
|
+
--color=always -- \
|
21
|
+
"${bad_links[idx]}" \
|
22
|
+
':!script/check_cpln_links')
|
23
|
+
|
24
|
+
# Line would become really unwieldly if everything was mushed into the
|
25
|
+
# conditional, so let's ignore this check here.
|
26
|
+
# shellcheck disable=SC2181
|
27
|
+
if [ $? -eq 0 ]; then
|
28
|
+
accumulated_results+="$results"
|
29
|
+
seen_bad_links_indexes+=("$idx")
|
30
|
+
exit_status=1
|
31
|
+
fi
|
32
|
+
done
|
33
|
+
|
34
|
+
if [ "$exit_status" -eq 1 ]; then
|
35
|
+
echo "${bold}[!] Found the following bad links:${normal}"
|
36
|
+
echo ""
|
37
|
+
echo "$accumulated_results"
|
38
|
+
echo ""
|
39
|
+
echo "${bold}[*] Please update accordingly:${normal}"
|
40
|
+
for bad_link_index in "${seen_bad_links_indexes[@]}"; do
|
41
|
+
echo " ${bad_links[bad_link_index]} -> ${proper_links[bad_link_index]}"
|
42
|
+
done
|
43
|
+
fi
|
44
|
+
|
45
|
+
exit "$exit_status"
|