cpl 1.1.2 → 1.3.0
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/.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"
|