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.
- checksums.yaml +7 -0
 - data/.github/workflows/check_cpln_links.yml +19 -0
 - data/.github/workflows/command_docs.yml +24 -0
 - data/.github/workflows/rspec-shared.yml +56 -0
 - data/.github/workflows/rspec.yml +28 -0
 - data/.github/workflows/rubocop.yml +24 -0
 - data/.gitignore +18 -0
 - data/.overcommit.yml +16 -0
 - data/.rubocop.yml +22 -0
 - data/.simplecov_spawn.rb +10 -0
 - data/CHANGELOG.md +259 -0
 - data/CONTRIBUTING.md +73 -0
 - data/Gemfile +7 -0
 - data/Gemfile.lock +126 -0
 - data/LICENSE +21 -0
 - data/README.md +546 -0
 - data/Rakefile +21 -0
 - data/bin/cpflow +6 -0
 - data/cpflow +6 -0
 - data/cpflow.gemspec +41 -0
 - data/docs/assets/grafana-alert.png +0 -0
 - data/docs/assets/memcached.png +0 -0
 - data/docs/assets/sidekiq-pre-stop-hook.png +0 -0
 - data/docs/commands.md +454 -0
 - data/docs/dns.md +15 -0
 - data/docs/migrating.md +262 -0
 - data/docs/postgres.md +436 -0
 - data/docs/redis.md +128 -0
 - data/docs/secrets-and-env-values.md +42 -0
 - data/docs/tips.md +150 -0
 - data/docs/troubleshooting.md +6 -0
 - data/examples/circleci.yml +104 -0
 - data/examples/controlplane.yml +159 -0
 - data/lib/command/apply_template.rb +209 -0
 - data/lib/command/base.rb +540 -0
 - data/lib/command/build_image.rb +49 -0
 - data/lib/command/cleanup_images.rb +136 -0
 - data/lib/command/cleanup_stale_apps.rb +79 -0
 - data/lib/command/config.rb +48 -0
 - data/lib/command/copy_image_from_upstream.rb +108 -0
 - data/lib/command/delete.rb +149 -0
 - data/lib/command/deploy_image.rb +56 -0
 - data/lib/command/doctor.rb +47 -0
 - data/lib/command/env.rb +22 -0
 - data/lib/command/exists.rb +23 -0
 - data/lib/command/generate.rb +45 -0
 - data/lib/command/info.rb +222 -0
 - data/lib/command/latest_image.rb +19 -0
 - data/lib/command/logs.rb +49 -0
 - data/lib/command/maintenance.rb +42 -0
 - data/lib/command/maintenance_off.rb +62 -0
 - data/lib/command/maintenance_on.rb +62 -0
 - data/lib/command/maintenance_set_page.rb +34 -0
 - data/lib/command/no_command.rb +23 -0
 - data/lib/command/open.rb +33 -0
 - data/lib/command/open_console.rb +26 -0
 - data/lib/command/promote_app_from_upstream.rb +38 -0
 - data/lib/command/ps.rb +41 -0
 - data/lib/command/ps_restart.rb +37 -0
 - data/lib/command/ps_start.rb +51 -0
 - data/lib/command/ps_stop.rb +82 -0
 - data/lib/command/ps_wait.rb +40 -0
 - data/lib/command/run.rb +573 -0
 - data/lib/command/setup_app.rb +113 -0
 - data/lib/command/test.rb +23 -0
 - data/lib/command/version.rb +18 -0
 - data/lib/constants/exit_code.rb +7 -0
 - data/lib/core/config.rb +316 -0
 - data/lib/core/controlplane.rb +552 -0
 - data/lib/core/controlplane_api.rb +170 -0
 - data/lib/core/controlplane_api_direct.rb +112 -0
 - data/lib/core/doctor_service.rb +104 -0
 - data/lib/core/helpers.rb +26 -0
 - data/lib/core/shell.rb +100 -0
 - data/lib/core/template_parser.rb +76 -0
 - data/lib/cpflow/version.rb +6 -0
 - data/lib/cpflow.rb +288 -0
 - data/lib/deprecated_commands.json +9 -0
 - data/lib/generator_templates/Dockerfile +27 -0
 - data/lib/generator_templates/controlplane.yml +62 -0
 - data/lib/generator_templates/entrypoint.sh +8 -0
 - data/lib/generator_templates/templates/app.yml +21 -0
 - data/lib/generator_templates/templates/postgres.yml +176 -0
 - data/lib/generator_templates/templates/rails.yml +36 -0
 - data/rakelib/create_release.rake +81 -0
 - data/script/add_command +37 -0
 - data/script/check_command_docs +3 -0
 - data/script/check_cpln_links +45 -0
 - data/script/rename_command +43 -0
 - data/script/update_command_docs +62 -0
 - data/templates/app.yml +13 -0
 - data/templates/daily-task.yml +32 -0
 - data/templates/maintenance.yml +25 -0
 - data/templates/memcached.yml +24 -0
 - data/templates/postgres.yml +32 -0
 - data/templates/rails.yml +27 -0
 - data/templates/redis.yml +21 -0
 - data/templates/redis2.yml +37 -0
 - data/templates/sidekiq.yml +38 -0
 - 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,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,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
         
     |