cpflow 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/check_cpln_links.yml +19 -0
  3. data/.github/workflows/command_docs.yml +24 -0
  4. data/.github/workflows/rspec-shared.yml +56 -0
  5. data/.github/workflows/rspec.yml +28 -0
  6. data/.github/workflows/rubocop.yml +24 -0
  7. data/.gitignore +18 -0
  8. data/.overcommit.yml +16 -0
  9. data/.rubocop.yml +22 -0
  10. data/.simplecov_spawn.rb +10 -0
  11. data/CHANGELOG.md +259 -0
  12. data/CONTRIBUTING.md +73 -0
  13. data/Gemfile +7 -0
  14. data/Gemfile.lock +126 -0
  15. data/LICENSE +21 -0
  16. data/README.md +546 -0
  17. data/Rakefile +21 -0
  18. data/bin/cpflow +6 -0
  19. data/cpflow +6 -0
  20. data/cpflow.gemspec +41 -0
  21. data/docs/assets/grafana-alert.png +0 -0
  22. data/docs/assets/memcached.png +0 -0
  23. data/docs/assets/sidekiq-pre-stop-hook.png +0 -0
  24. data/docs/commands.md +454 -0
  25. data/docs/dns.md +15 -0
  26. data/docs/migrating.md +262 -0
  27. data/docs/postgres.md +436 -0
  28. data/docs/redis.md +128 -0
  29. data/docs/secrets-and-env-values.md +42 -0
  30. data/docs/tips.md +150 -0
  31. data/docs/troubleshooting.md +6 -0
  32. data/examples/circleci.yml +104 -0
  33. data/examples/controlplane.yml +159 -0
  34. data/lib/command/apply_template.rb +209 -0
  35. data/lib/command/base.rb +540 -0
  36. data/lib/command/build_image.rb +49 -0
  37. data/lib/command/cleanup_images.rb +136 -0
  38. data/lib/command/cleanup_stale_apps.rb +79 -0
  39. data/lib/command/config.rb +48 -0
  40. data/lib/command/copy_image_from_upstream.rb +108 -0
  41. data/lib/command/delete.rb +149 -0
  42. data/lib/command/deploy_image.rb +56 -0
  43. data/lib/command/doctor.rb +47 -0
  44. data/lib/command/env.rb +22 -0
  45. data/lib/command/exists.rb +23 -0
  46. data/lib/command/generate.rb +45 -0
  47. data/lib/command/info.rb +222 -0
  48. data/lib/command/latest_image.rb +19 -0
  49. data/lib/command/logs.rb +49 -0
  50. data/lib/command/maintenance.rb +42 -0
  51. data/lib/command/maintenance_off.rb +62 -0
  52. data/lib/command/maintenance_on.rb +62 -0
  53. data/lib/command/maintenance_set_page.rb +34 -0
  54. data/lib/command/no_command.rb +23 -0
  55. data/lib/command/open.rb +33 -0
  56. data/lib/command/open_console.rb +26 -0
  57. data/lib/command/promote_app_from_upstream.rb +38 -0
  58. data/lib/command/ps.rb +41 -0
  59. data/lib/command/ps_restart.rb +37 -0
  60. data/lib/command/ps_start.rb +51 -0
  61. data/lib/command/ps_stop.rb +82 -0
  62. data/lib/command/ps_wait.rb +40 -0
  63. data/lib/command/run.rb +573 -0
  64. data/lib/command/setup_app.rb +113 -0
  65. data/lib/command/test.rb +23 -0
  66. data/lib/command/version.rb +18 -0
  67. data/lib/constants/exit_code.rb +7 -0
  68. data/lib/core/config.rb +316 -0
  69. data/lib/core/controlplane.rb +552 -0
  70. data/lib/core/controlplane_api.rb +170 -0
  71. data/lib/core/controlplane_api_direct.rb +112 -0
  72. data/lib/core/doctor_service.rb +104 -0
  73. data/lib/core/helpers.rb +26 -0
  74. data/lib/core/shell.rb +100 -0
  75. data/lib/core/template_parser.rb +76 -0
  76. data/lib/cpflow/version.rb +6 -0
  77. data/lib/cpflow.rb +288 -0
  78. data/lib/deprecated_commands.json +9 -0
  79. data/lib/generator_templates/Dockerfile +27 -0
  80. data/lib/generator_templates/controlplane.yml +62 -0
  81. data/lib/generator_templates/entrypoint.sh +8 -0
  82. data/lib/generator_templates/templates/app.yml +21 -0
  83. data/lib/generator_templates/templates/postgres.yml +176 -0
  84. data/lib/generator_templates/templates/rails.yml +36 -0
  85. data/rakelib/create_release.rake +81 -0
  86. data/script/add_command +37 -0
  87. data/script/check_command_docs +3 -0
  88. data/script/check_cpln_links +45 -0
  89. data/script/rename_command +43 -0
  90. data/script/update_command_docs +62 -0
  91. data/templates/app.yml +13 -0
  92. data/templates/daily-task.yml +32 -0
  93. data/templates/maintenance.yml +25 -0
  94. data/templates/memcached.yml +24 -0
  95. data/templates/postgres.yml +32 -0
  96. data/templates/rails.yml +27 -0
  97. data/templates/redis.yml +21 -0
  98. data/templates/redis2.yml +37 -0
  99. data/templates/sidekiq.yml +38 -0
  100. metadata +341 -0
data/lib/cpflow.rb ADDED
@@ -0,0 +1,288 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ require "dotenv/load"
5
+ require "cgi"
6
+ require "json"
7
+ require "jwt"
8
+ require "net/http"
9
+ require "open3"
10
+ require "pathname"
11
+ require "tempfile"
12
+ require "thor"
13
+ require "yaml"
14
+
15
+ require_relative "constants/exit_code"
16
+
17
+ # We need to require base before all commands, since the commands inherit from it
18
+ require_relative "command/base"
19
+
20
+ modules = Dir["#{__dir__}/**/*.rb"].reject do |file|
21
+ file == __FILE__ || file.end_with?("base.rb")
22
+ end
23
+ modules.sort.each { require(_1) }
24
+
25
+ # NOTE: this snippet combines all subprocesses into a group and kills all on exit to avoid hanging orphans
26
+ $child_pids = [] # rubocop:disable Style/GlobalVars
27
+ at_exit do
28
+ $child_pids.each do |pid| # rubocop:disable Style/GlobalVars
29
+ Process.kill("TERM", pid)
30
+ end
31
+ end
32
+
33
+ # Fix for https://github.com/erikhuda/thor/issues/398
34
+ # Copied from https://github.com/rails/thor/issues/398#issuecomment-622988390
35
+ class Thor
36
+ module Shell
37
+ class Basic
38
+ def print_wrapped(message, options = {})
39
+ indent = (options[:indent] || 0).to_i
40
+ if indent.zero?
41
+ stdout.puts(message)
42
+ else
43
+ message.each_line do |message_line|
44
+ stdout.print(" " * indent)
45
+ stdout.puts(message_line.chomp)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ module Cpflow
54
+ class Error < StandardError; end
55
+
56
+ class Cli < Thor # rubocop:disable Metrics/ClassLength
57
+ package_name "cpflow"
58
+ default_task :no_command
59
+
60
+ def self.start(*args)
61
+ ENV["CPLN_SKIP_UPDATE_CHECK"] = "true"
62
+
63
+ check_cpln_version
64
+ check_cpflow_version
65
+ fix_help_option
66
+
67
+ super(*args)
68
+ end
69
+
70
+ def self.check_cpln_version # rubocop:disable Metrics/MethodLength
71
+ return if @checked_cpln_version
72
+
73
+ @checked_cpln_version = true
74
+
75
+ result = ::Shell.cmd("cpln", "--version", capture_stderr: true)
76
+ if result[:success]
77
+ data = JSON.parse(result[:output])
78
+
79
+ version = data["npm"]
80
+ min_version = Cpflow::MIN_CPLN_VERSION
81
+ if Gem::Version.new(version) < Gem::Version.new(min_version)
82
+ ::Shell.abort("Current 'cpln' version: #{version}. Minimum supported version: #{min_version}. " \
83
+ "Please update it with 'npm update -g @controlplane/cli'.")
84
+ end
85
+ else
86
+ ::Shell.abort("Can't find 'cpln' executable. Please install it with 'npm install -g @controlplane/cli'.")
87
+ end
88
+ end
89
+
90
+ def self.check_cpflow_version # rubocop:disable Metrics/MethodLength
91
+ return if @checked_cpflow_version
92
+
93
+ @checked_cpflow_version = true
94
+
95
+ result = ::Shell.cmd("gem", "search", "^cpflow$", "--remote", capture_stderr: true)
96
+ return unless result[:success]
97
+
98
+ matches = result[:output].match(/cpflow \((.+)\)/)
99
+ return unless matches
100
+
101
+ version = Cpflow::VERSION
102
+ latest_version = matches[1]
103
+ return unless Gem::Version.new(version) < Gem::Version.new(latest_version)
104
+
105
+ ::Shell.warn("You are not using the latest 'cpflow' version. Please update it with 'gem update cpflow'.")
106
+ $stderr.puts
107
+ end
108
+
109
+ # This is so that we're able to run `cpflow COMMAND --help` to print the help
110
+ # (it basically changes it to `cpflow --help COMMAND`, which Thor recognizes)
111
+ # Based on https://stackoverflow.com/questions/49042591/how-to-add-help-h-flag-to-thor-command
112
+ def self.fix_help_option
113
+ help_mappings = Thor::HELP_MAPPINGS + ["help"]
114
+ matches = help_mappings & ARGV
115
+ matches.each do |match|
116
+ ARGV.delete(match)
117
+ ARGV.unshift(match)
118
+ end
119
+ end
120
+
121
+ # Needed to silence deprecation warning
122
+ def self.exit_on_failure?
123
+ true
124
+ end
125
+
126
+ # Needed to be able to use "run" as a command
127
+ def self.is_thor_reserved_word?(word, type) # rubocop:disable Naming/PredicateName
128
+ return false if word == "run"
129
+
130
+ super(word, type)
131
+ end
132
+
133
+ def self.deprecated_commands
134
+ @deprecated_commands ||= begin
135
+ deprecated_commands_file_path = "#{__dir__}/deprecated_commands.json"
136
+ deprecated_commands_data = File.binread(deprecated_commands_file_path)
137
+ deprecated_commands = JSON.parse(deprecated_commands_data)
138
+ deprecated_commands.to_h do |old_command_name, new_command_name|
139
+ file_name = new_command_name.gsub(/[^A-Za-z]/, "_")
140
+ class_name = file_name.split("_").map(&:capitalize).join
141
+
142
+ [old_command_name, Object.const_get("::Command::#{class_name}")]
143
+ end
144
+ end
145
+ end
146
+
147
+ def self.all_base_commands
148
+ ::Command::Base.all_commands.merge(deprecated_commands)
149
+ end
150
+
151
+ def self.process_option_params(params)
152
+ # Ensures that if no value is provided for a non-boolean option (e.g., `cpflow command --option`),
153
+ # it defaults to an empty string instead of the option name (which is the default Thor behavior)
154
+ params[:lazy_default] ||= "" if params[:type] != :boolean
155
+
156
+ params
157
+ end
158
+
159
+ @commands_with_required_options = []
160
+ @commands_with_extra_options = []
161
+
162
+ ::Command::Base.common_options.each do |option|
163
+ params = process_option_params(option[:params])
164
+ class_option(option[:name], **params)
165
+ end
166
+
167
+ all_base_commands.each do |command_key, command_class| # rubocop:disable Metrics/BlockLength
168
+ deprecated = deprecated_commands[command_key]
169
+
170
+ name = command_class::NAME
171
+ name_for_method = deprecated ? command_key : name.tr("-", "_")
172
+ usage = command_class::USAGE.empty? ? name : command_class::USAGE
173
+ requires_args = command_class::REQUIRES_ARGS
174
+ default_args = command_class::DEFAULT_ARGS
175
+ command_options = command_class::OPTIONS
176
+ accepts_extra_options = command_class::ACCEPTS_EXTRA_OPTIONS
177
+ description = command_class::DESCRIPTION
178
+ long_description = command_class::LONG_DESCRIPTION
179
+ examples = command_class::EXAMPLES
180
+ hide = command_class::HIDE || deprecated
181
+ with_info_header = command_class::WITH_INFO_HEADER
182
+ validations = command_class::VALIDATIONS
183
+
184
+ long_description += "\n#{examples}" if examples.length.positive?
185
+
186
+ # `handle_argument_error` does not exist in the context below,
187
+ # so we store it here to be able to use it
188
+ raise_args_error = ->(*args) { handle_argument_error(commands[name_for_method], ArgumentError, *args) }
189
+
190
+ desc(usage, description, hide: hide)
191
+ long_desc(long_description)
192
+
193
+ command_options.each do |option|
194
+ params = process_option_params(option[:params])
195
+ method_option(option[:name], **params)
196
+ end
197
+
198
+ # We'll handle required options manually in `Config`
199
+ required_options = command_options.select { |option| option[:params][:required] }.map { |option| option[:name] }
200
+ @commands_with_required_options.push(name_for_method.to_sym) if required_options.any?
201
+
202
+ @commands_with_extra_options.push(name_for_method.to_sym) if accepts_extra_options
203
+
204
+ define_method(name_for_method) do |*provided_args| # rubocop:disable Metrics/BlockLength, Metrics/MethodLength
205
+ if deprecated
206
+ normalized_old_name = ::Helpers.normalize_command_name(command_key)
207
+ ::Shell.warn_deprecated("Command '#{normalized_old_name}' is deprecated, " \
208
+ "please use '#{name}' instead.")
209
+ $stderr.puts
210
+ end
211
+
212
+ args = if provided_args.length.positive?
213
+ provided_args
214
+ else
215
+ default_args
216
+ end
217
+
218
+ if (args.empty? && requires_args) || (!args.empty? && !requires_args && !accepts_extra_options)
219
+ raise_args_error.call(args, nil)
220
+ end
221
+
222
+ begin
223
+ Cpflow::Cli.validate_options!(options)
224
+
225
+ config = Config.new(args, options, required_options)
226
+
227
+ Cpflow::Cli.show_info_header(config) if with_info_header
228
+
229
+ if validations.any? && ENV.fetch("DISABLE_VALIDATIONS", nil) != "true"
230
+ doctor = DoctorService.new(config)
231
+ doctor.run_validations(validations, silent_if_passing: true)
232
+ end
233
+
234
+ command_class.new(config).call
235
+ rescue RuntimeError => e
236
+ ::Shell.abort(e.message)
237
+ end
238
+ end
239
+ rescue StandardError => e
240
+ ::Shell.abort("Unable to load command: #{e.message}")
241
+ end
242
+
243
+ disable_required_check!(*@commands_with_required_options)
244
+ check_unknown_options!(except: @commands_with_extra_options)
245
+ stop_on_unknown_option!
246
+
247
+ def self.validate_options!(options) # rubocop:disable Metrics/MethodLength
248
+ options.each do |name, value|
249
+ normalized_name = ::Helpers.normalize_option_name(name)
250
+ raise "No value provided for option #{normalized_name}." if value.to_s.strip.empty?
251
+
252
+ option = ::Command::Base.all_options.find { |current_option| current_option[:name].to_s == name }
253
+ if option[:new_name]
254
+ normalized_new_name = ::Helpers.normalize_option_name(option[:new_name])
255
+ ::Shell.warn_deprecated("Option #{normalized_name} is deprecated, " \
256
+ "please use #{normalized_new_name} instead.")
257
+ $stderr.puts
258
+ end
259
+
260
+ params = option[:params]
261
+ next unless params[:valid_regex]
262
+
263
+ raise "Invalid value provided for option #{normalized_name}." unless value.match?(params[:valid_regex])
264
+ end
265
+ end
266
+
267
+ def self.show_info_header(config) # rubocop:disable Metrics/MethodLength
268
+ return if @showed_info_header
269
+
270
+ rows = {}
271
+ rows["ORG"] = config.org || "NOT PROVIDED!"
272
+ rows["ORG"] += " (comes from CPLN_ORG env var)" if config.org_comes_from_env
273
+ rows["APP"] = config.app || "NOT PROVIDED!"
274
+ rows["APP"] += " (comes from CPLN_APP env var)" if config.app_comes_from_env
275
+
276
+ rows.each do |key, value|
277
+ puts "#{key}: #{value}"
278
+ end
279
+
280
+ @showed_info_header = true
281
+
282
+ # Add a newline after the info header
283
+ puts
284
+ end
285
+ end
286
+ end
287
+
288
+ Shell.trap_interrupt unless ENV.fetch("DISABLE_INTERRUPT_TRAP", nil) == "true"
@@ -0,0 +1,9 @@
1
+ {
2
+ "build": "build-image",
3
+ "cleanup_old_images": "cleanup-images",
4
+ "promote": "deploy-image",
5
+ "promote_image": "deploy-image",
6
+ "run:detached": "run",
7
+ "runner": "run",
8
+ "setup": "apply-template"
9
+ }
@@ -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,62 @@
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
+ # These are updated with the new image when running the `deploy-image` command,
23
+ # and are also used by the `info` and `ps:` commands in order to get all of the defined workloads.
24
+ # On the other hand, if you have a workload for Redis, that would NOT use the application Docker image
25
+ # and not be listed here.
26
+ app_workloads:
27
+ - rails
28
+
29
+ # Additional "service type" workloads, using non-application Docker images.
30
+ # These are only used by the `info` and `ps:` commands in order to get all of the defined workloads.
31
+ additional_workloads:
32
+ - postgres
33
+
34
+ # Configure the workload name used when maintenance mode is on (defaults to "maintenance")
35
+ maintenance_workload: maintenance
36
+
37
+ apps:
38
+ my-app-staging:
39
+ # Use the values from the common section above.
40
+ <<: *common
41
+ my-app-review:
42
+ <<: *common
43
+ # If `match_if_app_name_starts_with` is `true`, then use this config for app names starting with this name,
44
+ # e.g., "my-app-review-pr123", "my-app-review-anything-goes", etc.
45
+ match_if_app_name_starts_with: true
46
+ my-app-production:
47
+ <<: *common
48
+
49
+ # You can also opt out of allowing the use of CPLN_ORG and CPLN_APP env vars per app.
50
+ # It's recommended to leave this off for production, to avoid any accidents.
51
+ allow_org_override_by_env: false
52
+ allow_app_override_by_env: false
53
+
54
+ # Use a different organization for production.
55
+ cpln_org: my-org-production
56
+ # Allows running the command `cpflow promote-app-from-upstream -a my-app-production`
57
+ # to promote the staging app to production.
58
+ upstream: my-app-staging
59
+ my-app-other:
60
+ <<: *common
61
+ # You can specify a different `Dockerfile` relative to the `.controlplane/` directory (defaults to "Dockerfile").
62
+ dockerfile: ../some_other/Dockerfile
@@ -0,0 +1,8 @@
1
+ #!/bin/sh
2
+ # Runs before the main command
3
+
4
+ echo " -- Preparing database"
5
+ rails db:prepare
6
+
7
+ echo " -- Finishing entrypoint.sh, executing '$@'"
8
+ exec "$@"
@@ -0,0 +1,21 @@
1
+ # Template setup of the GVC, roughly corresponding to a Heroku app
2
+ kind: gvc
3
+ name: {{APP_NAME}}
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_NAME}}.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_NAME}}.cpln.local:5432/{{APP_NAME}}'
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
+ - {{APP_LOCATION_LINK}}
@@ -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_NAME}}/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: {{APP_IMAGE_LINK}}
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