cpflow 3.0.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.
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