cpl 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.overcommit.yml +10 -0
- data/Gemfile.lock +10 -3
- data/README.md +6 -0
- data/cpl.gemspec +1 -0
- data/docs/commands.md +51 -3
- data/googlee2da545df05d92f9.html +1 -0
- data/lib/command/base.rb +65 -7
- data/lib/command/build_image.rb +6 -5
- data/lib/command/cleanup_old_images.rb +8 -7
- data/lib/command/cleanup_stale_apps.rb +11 -9
- data/lib/command/config.rb +30 -15
- data/lib/command/copy_image_from_upstream.rb +110 -0
- data/lib/command/delete.rb +10 -12
- data/lib/command/deploy_image.rb +6 -5
- data/lib/command/env.rb +2 -2
- data/lib/command/exists.rb +4 -4
- data/lib/command/info.rb +233 -0
- data/lib/command/latest_image.rb +2 -2
- data/lib/command/logs.rb +4 -4
- data/lib/command/no_command.rb +3 -3
- data/lib/command/open.rb +4 -4
- data/lib/command/promote_app_from_upstream.rb +58 -0
- data/lib/command/ps.rb +10 -13
- data/lib/command/ps_restart.rb +9 -6
- data/lib/command/ps_start.rb +7 -6
- data/lib/command/ps_stop.rb +7 -6
- data/lib/command/run.rb +5 -5
- data/lib/command/run_detached.rb +7 -5
- data/lib/command/setup.rb +71 -13
- data/lib/command/test.rb +2 -2
- data/lib/command/version.rb +2 -2
- data/lib/core/config.rb +26 -19
- data/lib/core/controlplane.rb +77 -11
- data/lib/core/controlplane_api.rb +12 -0
- data/lib/core/controlplane_api_cli.rb +1 -1
- data/lib/core/controlplane_api_direct.rb +2 -2
- data/lib/core/shell.rb +25 -3
- data/lib/cpl/version.rb +1 -1
- data/lib/cpl.rb +19 -10
- data/lib/deprecated_commands.json +6 -0
- data/script/add_command +37 -0
- data/script/generate_commands_docs +5 -5
- data/script/rename_command +43 -0
- metadata +24 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 0be2530a33ac1f8a6b71d6d90eca0026b1048faf76b3e2f88cf13572f7e50a92
         | 
| 4 | 
            +
              data.tar.gz: c15e191f5d8e984aece73bf5954ba977ee1c3013851447592ef4f2e96c1e2732
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 9fe01da2ec88fdb25ba56b9ca78267f8cca42b73f219576decc4af5c0aa695168d08b32692bcdcabc7274ab1f5e9f587d3d7b09d18817c262f011fc328ee7409
         | 
| 7 | 
            +
              data.tar.gz: a88847f98eaa1426eeabe23919cd1dbbf31275491e61ba825eebb44ae991b8eba087ffa1c61986e9f576026dbac59f4f118f00abcbfdfb273997ba95c1b27cdc
         | 
    
        data/.overcommit.yml
    ADDED
    
    
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            PATH
         | 
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                cpl (0. | 
| 4 | 
            +
                cpl (0.5.0)
         | 
| 5 5 | 
             
                  debug (~> 1.7.1)
         | 
| 6 6 | 
             
                  dotenv (~> 2.8.1)
         | 
| 7 7 | 
             
                  psych (~> 5.1.0)
         | 
| @@ -11,16 +11,22 @@ GEM | |
| 11 11 | 
             
              remote: https://rubygems.org/
         | 
| 12 12 | 
             
              specs:
         | 
| 13 13 | 
             
                ast (2.4.2)
         | 
| 14 | 
            -
                 | 
| 14 | 
            +
                childprocess (4.1.0)
         | 
| 15 | 
            +
                debug (1.7.2)
         | 
| 15 16 | 
             
                  irb (>= 1.5.0)
         | 
| 16 17 | 
             
                  reline (>= 0.3.1)
         | 
| 17 18 | 
             
                diff-lcs (1.5.0)
         | 
| 18 19 | 
             
                docile (1.4.0)
         | 
| 19 20 | 
             
                dotenv (2.8.1)
         | 
| 21 | 
            +
                iniparse (1.5.0)
         | 
| 20 22 | 
             
                io-console (0.6.0)
         | 
| 21 23 | 
             
                irb (1.6.3)
         | 
| 22 24 | 
             
                  reline (>= 0.3.0)
         | 
| 23 25 | 
             
                json (2.6.3)
         | 
| 26 | 
            +
                overcommit (0.60.0)
         | 
| 27 | 
            +
                  childprocess (>= 0.6.3, < 5)
         | 
| 28 | 
            +
                  iniparse (~> 1.4)
         | 
| 29 | 
            +
                  rexml (~> 3.2)
         | 
| 24 30 | 
             
                parallel (1.22.1)
         | 
| 25 31 | 
             
                parser (3.2.0.0)
         | 
| 26 32 | 
             
                  ast (~> 2.4.1)
         | 
| @@ -29,7 +35,7 @@ GEM | |
| 29 35 | 
             
                rainbow (3.1.1)
         | 
| 30 36 | 
             
                rake (13.0.6)
         | 
| 31 37 | 
             
                regexp_parser (2.6.2)
         | 
| 32 | 
            -
                reline (0.3. | 
| 38 | 
            +
                reline (0.3.3)
         | 
| 33 39 | 
             
                  io-console (~> 0.5)
         | 
| 34 40 | 
             
                rexml (3.2.5)
         | 
| 35 41 | 
             
                rspec (3.12.0)
         | 
| @@ -81,6 +87,7 @@ PLATFORMS | |
| 81 87 |  | 
| 82 88 | 
             
            DEPENDENCIES
         | 
| 83 89 | 
             
              cpl!
         | 
| 90 | 
            +
              overcommit (~> 0.60.0)
         | 
| 84 91 | 
             
              rake (~> 13.0)
         | 
| 85 92 | 
             
              rspec (~> 3.12.0)
         | 
| 86 93 | 
             
              rubocop (~> 1.45.0)
         | 
    
        data/README.md
    CHANGED
    
    | @@ -1,5 +1,11 @@ | |
| 1 1 | 
             
            # Heroku to Control Plane `cpl` CLI
         | 
| 2 2 |  | 
| 3 | 
            +
            <meta name="author" content="Justin Gordon and Sergey Tarasov">
         | 
| 4 | 
            +
            <meta name="description" content="Instructions on how to migrate from Heroku to Control Plane and a CLI called cpl to make it easier.">
         | 
| 5 | 
            +
            <meta name="copyright" content="ShakaCode, 2023">
         | 
| 6 | 
            +
            <meta name="keywords" content="Control Plane, Heroku, Kubernetes, K8, Infrastructure">
         | 
| 7 | 
            +
            <meta name="google-site-verification" content="dIV4nMplcYl6YOKOaZMqgvdKXhLJ4cdYY6pS6e_YrPU" />
         | 
| 8 | 
            +
             | 
| 3 9 | 
             
            _A playbook for migrating from [Heroku](https://heroku.com) to [Control Plane](https://controlplane.com)_
         | 
| 4 10 |  | 
| 5 11 | 
             
            [](https://github.com/shakacode/heroku-to-control-plane/actions/workflows/rspec.yml)
         | 
    
        data/cpl.gemspec
    CHANGED
    
    | @@ -20,6 +20,7 @@ Gem::Specification.new do |spec| | |
| 20 20 | 
             
              spec.add_dependency "psych",    "~> 5.1.0"
         | 
| 21 21 | 
             
              spec.add_dependency "thor",     "~> 1.2.1"
         | 
| 22 22 |  | 
| 23 | 
            +
              spec.add_development_dependency "overcommit",    "~> 0.60.0"
         | 
| 23 24 | 
             
              spec.add_development_dependency "rspec",         "~> 3.12.0"
         | 
| 24 25 | 
             
              spec.add_development_dependency "rubocop",       "~> 1.45.0"
         | 
| 25 26 | 
             
              spec.add_development_dependency "rubocop-rake",  "~> 0.6.0"
         | 
    
        data/docs/commands.md
    CHANGED
    
    | @@ -46,16 +46,31 @@ cpl cleanup-stale-apps -a $APP_NAME | |
| 46 46 |  | 
| 47 47 | 
             
            ### `config`
         | 
| 48 48 |  | 
| 49 | 
            -
            - Displays  | 
| 49 | 
            +
            - Displays config for each app or a specific app
         | 
| 50 50 |  | 
| 51 51 | 
             
            ```sh
         | 
| 52 | 
            -
            # Shows the  | 
| 52 | 
            +
            # Shows the config for each app.
         | 
| 53 53 | 
             
            cpl config
         | 
| 54 54 |  | 
| 55 | 
            -
            # Shows  | 
| 55 | 
            +
            # Shows the config for a specific app.
         | 
| 56 56 | 
             
            cpl config -a $APP_NAME
         | 
| 57 57 | 
             
            ```
         | 
| 58 58 |  | 
| 59 | 
            +
            ### `copy-image-from-upstream`
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            - Copies an image (by default the latest) from a source org to the current org
         | 
| 62 | 
            +
            - The source org must be specified through `upstream` in the `.controlplane/controlplane.yml` file
         | 
| 63 | 
            +
            - Additionally, the token for the source org must be provided through `--upstream-token` or `-t`
         | 
| 64 | 
            +
            - A `cpln` profile will be temporarily created to pull the image from the source org
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            ```sh
         | 
| 67 | 
            +
            # Copies the latest image from the source org to the current org.
         | 
| 68 | 
            +
            cpl copy-image-from-upstream -a $APP_NAME --upstream-token $UPSTREAM_TOKEN
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            # Copies a specific image from the source org to the current org.
         | 
| 71 | 
            +
            cpl copy-image-from-upstream -a $APP_NAME --upstream-token $UPSTREAM_TOKEN --image appimage:123
         | 
| 72 | 
            +
            ```
         | 
| 73 | 
            +
             | 
| 59 74 | 
             
            ### `delete`
         | 
| 60 75 |  | 
| 61 76 | 
             
            - Deletes the whole app (GVC with all workloads and all images)
         | 
| @@ -89,6 +104,25 @@ cpl env -a $APP_NAME | |
| 89 104 | 
             
            if [ cpl exists -a $APP_NAME ]; ...
         | 
| 90 105 | 
             
            ```
         | 
| 91 106 |  | 
| 107 | 
            +
            ### `info`
         | 
| 108 | 
            +
             | 
| 109 | 
            +
            - Displays the diff between defined/available apps/workloads (apps equal GVCs)
         | 
| 110 | 
            +
            - Apps that are defined but not available are displayed in red
         | 
| 111 | 
            +
            - Apps that are available but not defined are displayed in green
         | 
| 112 | 
            +
            - Apps that are both defined and available are displayed in white
         | 
| 113 | 
            +
            - The diff is based on what's defined in the `.controlplane/controlplane.yml` file
         | 
| 114 | 
            +
             | 
| 115 | 
            +
            ```sh
         | 
| 116 | 
            +
            # Shows diff for all apps in all orgs.
         | 
| 117 | 
            +
            cpl info
         | 
| 118 | 
            +
             | 
| 119 | 
            +
            # Shows diff for all apps in a specific org.
         | 
| 120 | 
            +
            cpl info -o $ORG_NAME
         | 
| 121 | 
            +
             | 
| 122 | 
            +
            # Shows diff for a specific app.
         | 
| 123 | 
            +
            cpl info -a $APP_NAME
         | 
| 124 | 
            +
            ```
         | 
| 125 | 
            +
             | 
| 92 126 | 
             
            ### `latest-image`
         | 
| 93 127 |  | 
| 94 128 | 
             
            - Displays the latest image name
         | 
| @@ -121,6 +155,18 @@ cpl open -a $APP_NAME | |
| 121 155 | 
             
            cpl open -a $APP_NAME -w $WORKLOAD_NAME
         | 
| 122 156 | 
             
            ```
         | 
| 123 157 |  | 
| 158 | 
            +
            ### `promote-app-from-upstream`
         | 
| 159 | 
            +
             | 
| 160 | 
            +
            - Copies the latest image from upstream, runs a release script (optional), and deploys the image
         | 
| 161 | 
            +
            - It performs the following steps:
         | 
| 162 | 
            +
              - Runs `cpl copy-image-from-upstream` to copy the latest image from upstream
         | 
| 163 | 
            +
              - Runs a release script if specified through `release_script` in the `.controlplane/controlplane.yml` file
         | 
| 164 | 
            +
              - Runs `cpl deploy-image` to deploy the image
         | 
| 165 | 
            +
             | 
| 166 | 
            +
            ```sh
         | 
| 167 | 
            +
            cpl promote-app-from-upstream -a $APP_NAME -t $UPSTREAM_TOKEN
         | 
| 168 | 
            +
            ```
         | 
| 169 | 
            +
             | 
| 124 170 | 
             
            ### `ps`
         | 
| 125 171 |  | 
| 126 172 | 
             
            - Shows running replicas in app
         | 
| @@ -204,6 +250,8 @@ cpl run rails db:migrate -a $APP_NAME --image latest       # Latest sequential i | |
| 204 250 |  | 
| 205 251 | 
             
            ```sh
         | 
| 206 252 | 
             
            cpl run:detached rails db:prepare -a $APP_NAME
         | 
| 253 | 
            +
             | 
| 254 | 
            +
            # Need to quote COMMAND if setting ENV value or passing args to command to run
         | 
| 207 255 | 
             
            cpl run:detached 'LOG_LEVEL=warn rails db:migrate' -a $APP_NAME
         | 
| 208 256 |  | 
| 209 257 | 
             
            # Uses some other image.
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            google-site-verification: googlee2da545df05d92f9.html
         | 
    
        data/lib/command/base.rb
    CHANGED
    
    | @@ -38,6 +38,19 @@ module Command | |
| 38 38 | 
             
                  end
         | 
| 39 39 | 
             
                end
         | 
| 40 40 |  | 
| 41 | 
            +
                def self.org_option(required: false)
         | 
| 42 | 
            +
                  {
         | 
| 43 | 
            +
                    name: :org,
         | 
| 44 | 
            +
                    params: {
         | 
| 45 | 
            +
                      aliases: ["-o"],
         | 
| 46 | 
            +
                      banner: "ORG_NAME",
         | 
| 47 | 
            +
                      desc: "Organization name",
         | 
| 48 | 
            +
                      type: :string,
         | 
| 49 | 
            +
                      required: required
         | 
| 50 | 
            +
                    }
         | 
| 51 | 
            +
                  }
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 41 54 | 
             
                def self.app_option(required: false)
         | 
| 42 55 | 
             
                  {
         | 
| 43 56 | 
             
                    name: :app,
         | 
| @@ -90,6 +103,19 @@ module Command | |
| 90 103 | 
             
                  }
         | 
| 91 104 | 
             
                end
         | 
| 92 105 |  | 
| 106 | 
            +
                def self.upstream_token_option(required: false)
         | 
| 107 | 
            +
                  {
         | 
| 108 | 
            +
                    name: :upstream_token,
         | 
| 109 | 
            +
                    params: {
         | 
| 110 | 
            +
                      aliases: ["-t"],
         | 
| 111 | 
            +
                      banner: "UPSTREAM_TOKEN",
         | 
| 112 | 
            +
                      desc: "Upstream token",
         | 
| 113 | 
            +
                      type: :string,
         | 
| 114 | 
            +
                      required: required
         | 
| 115 | 
            +
                    }
         | 
| 116 | 
            +
                  }
         | 
| 117 | 
            +
                end
         | 
| 118 | 
            +
             | 
| 93 119 | 
             
                def self.skip_confirm_option(required: false)
         | 
| 94 120 | 
             
                  {
         | 
| 95 121 | 
             
                    name: :yes,
         | 
| @@ -163,17 +189,19 @@ module Command | |
| 163 189 | 
             
                  end
         | 
| 164 190 | 
             
                end
         | 
| 165 191 |  | 
| 166 | 
            -
                def latest_image
         | 
| 167 | 
            -
                  @latest_image ||=
         | 
| 192 | 
            +
                def latest_image(app = config.app, org = config.org)
         | 
| 193 | 
            +
                  @latest_image ||= {}
         | 
| 194 | 
            +
                  @latest_image[app] ||=
         | 
| 168 195 | 
             
                    begin
         | 
| 169 | 
            -
                      items = cp.image_query["items"]
         | 
| 170 | 
            -
                      latest_image_from(items)
         | 
| 196 | 
            +
                      items = cp.image_query(app, org)["items"]
         | 
| 197 | 
            +
                      latest_image_from(items, app_name: app)
         | 
| 171 198 | 
             
                    end
         | 
| 172 199 | 
             
                end
         | 
| 173 200 |  | 
| 174 | 
            -
                def latest_image_next
         | 
| 175 | 
            -
                  @latest_image_next ||=  | 
| 176 | 
            -
             | 
| 201 | 
            +
                def latest_image_next(app = config.app, org = config.org)
         | 
| 202 | 
            +
                  @latest_image_next ||= {}
         | 
| 203 | 
            +
                  @latest_image_next[app] ||= begin
         | 
| 204 | 
            +
                    image = latest_image(app, org).split(":").first
         | 
| 177 205 | 
             
                    image += ":#{extract_image_number(latest_image) + 1}"
         | 
| 178 206 | 
             
                    image += "_#{config.options[:commit]}" if config.options[:commit]
         | 
| 179 207 | 
             
                    image
         | 
| @@ -190,10 +218,40 @@ module Command | |
| 190 218 | 
             
                  $stderr
         | 
| 191 219 | 
             
                end
         | 
| 192 220 |  | 
| 221 | 
            +
                def step(message, abort_on_error: true) # rubocop:disable Metrics/MethodLength
         | 
| 222 | 
            +
                  progress.print("#{message}...")
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                  Shell.use_tmp_stderr do
         | 
| 225 | 
            +
                    success = false
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                    begin
         | 
| 228 | 
            +
                      success = yield
         | 
| 229 | 
            +
                    rescue RuntimeError => e
         | 
| 230 | 
            +
                      message = e.message
         | 
| 231 | 
            +
                      if abort_on_error
         | 
| 232 | 
            +
                        progress.puts(" #{Shell.color('failed!', :red)}\n\n")
         | 
| 233 | 
            +
                        Shell.abort(message)
         | 
| 234 | 
            +
                      else
         | 
| 235 | 
            +
                        Shell.write_to_tmp_stderr(message)
         | 
| 236 | 
            +
                      end
         | 
| 237 | 
            +
                    end
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                    if success
         | 
| 240 | 
            +
                      progress.puts(" #{Shell.color('done!', :green)}")
         | 
| 241 | 
            +
                    else
         | 
| 242 | 
            +
                      progress.puts(" #{Shell.color('failed!', :red)}\n\n#{Shell.read_from_tmp_stderr}\n\n")
         | 
| 243 | 
            +
                    end
         | 
| 244 | 
            +
                  end
         | 
| 245 | 
            +
                end
         | 
| 246 | 
            +
             | 
| 193 247 | 
             
                def cp
         | 
| 194 248 | 
             
                  @cp ||= Controlplane.new(config)
         | 
| 195 249 | 
             
                end
         | 
| 196 250 |  | 
| 251 | 
            +
                def perform(cmd)
         | 
| 252 | 
            +
                  system(cmd) || exit(false)
         | 
| 253 | 
            +
                end
         | 
| 254 | 
            +
             | 
| 197 255 | 
             
                private
         | 
| 198 256 |  | 
| 199 257 | 
             
                # returns 0 if no prior image
         | 
    
        data/lib/command/build_image.rb
    CHANGED
    
    | @@ -8,18 +8,19 @@ module Command | |
| 8 8 | 
             
                  commit_option
         | 
| 9 9 | 
             
                ].freeze
         | 
| 10 10 | 
             
                DESCRIPTION = "Builds and pushes the image to Control Plane"
         | 
| 11 | 
            -
                LONG_DESCRIPTION = <<~ | 
| 11 | 
            +
                LONG_DESCRIPTION = <<~DESC
         | 
| 12 12 | 
             
                  - Builds and pushes the image to Control Plane
         | 
| 13 13 | 
             
                  - Automatically assigns image numbers, e.g., `app:1`, `app:2`, etc.
         | 
| 14 14 | 
             
                  - Uses `.controlplane/Dockerfile`
         | 
| 15 | 
            -
                 | 
| 15 | 
            +
                DESC
         | 
| 16 16 |  | 
| 17 17 | 
             
                def call
         | 
| 18 18 | 
             
                  ensure_docker_running!
         | 
| 19 19 |  | 
| 20 20 | 
             
                  dockerfile = config.current[:dockerfile] || "Dockerfile"
         | 
| 21 21 | 
             
                  dockerfile = "#{config.app_cpln_dir}/#{dockerfile}"
         | 
| 22 | 
            -
             | 
| 22 | 
            +
             | 
| 23 | 
            +
                  progress.puts("Building image from Dockerfile '#{dockerfile}'...\n\n")
         | 
| 23 24 |  | 
| 24 25 | 
             
                  cp.image_build(latest_image_next, dockerfile: dockerfile)
         | 
| 25 26 | 
             
                end
         | 
| @@ -28,9 +29,9 @@ module Command | |
| 28 29 |  | 
| 29 30 | 
             
                def ensure_docker_running!
         | 
| 30 31 | 
             
                  `docker version > /dev/null 2>&1`
         | 
| 31 | 
            -
                  return if  | 
| 32 | 
            +
                  return if $CHILD_STATUS.success?
         | 
| 32 33 |  | 
| 33 | 
            -
                   | 
| 34 | 
            +
                  raise "Can't run Docker. Please make sure that it's installed and started, then try again."
         | 
| 34 35 | 
             
                end
         | 
| 35 36 | 
             
              end
         | 
| 36 37 | 
             
            end
         | 
| @@ -8,19 +8,19 @@ module Command | |
| 8 8 | 
             
                  skip_confirm_option
         | 
| 9 9 | 
             
                ].freeze
         | 
| 10 10 | 
             
                DESCRIPTION = "Deletes all images for an app that are older than the specified amount of days"
         | 
| 11 | 
            -
                LONG_DESCRIPTION = <<~ | 
| 11 | 
            +
                LONG_DESCRIPTION = <<~DESC
         | 
| 12 12 | 
             
                  - Deletes all images for an app that are older than the specified amount of days
         | 
| 13 13 | 
             
                  - Specify the amount of days through `old_image_retention_days` in the `.controlplane/controlplane.yml` file
         | 
| 14 14 | 
             
                  - Will ask for explicit user confirmation
         | 
| 15 15 | 
             
                  - Does not affect the latest image, regardless of how old it is
         | 
| 16 | 
            -
                 | 
| 16 | 
            +
                DESC
         | 
| 17 17 |  | 
| 18 18 | 
             
                def call
         | 
| 19 | 
            -
                  return progress.puts | 
| 19 | 
            +
                  return progress.puts("No old images found.") if old_images.empty?
         | 
| 20 20 |  | 
| 21 | 
            -
                  progress.puts | 
| 21 | 
            +
                  progress.puts("Old images:")
         | 
| 22 22 | 
             
                  old_images.each do |image|
         | 
| 23 | 
            -
                    progress.puts | 
| 23 | 
            +
                    progress.puts("  - #{image[:name]} (#{Shell.color((image[:date]).to_s, :red)})")
         | 
| 24 24 | 
             
                  end
         | 
| 25 25 |  | 
| 26 26 | 
             
                  return unless confirm_delete
         | 
| @@ -68,8 +68,9 @@ module Command | |
| 68 68 |  | 
| 69 69 | 
             
                def delete_images
         | 
| 70 70 | 
             
                  old_images.each do |image|
         | 
| 71 | 
            -
                     | 
| 72 | 
            -
             | 
| 71 | 
            +
                    step("Deleting image '#{image[:name]}'") do
         | 
| 72 | 
            +
                      cp.image_delete(image[:name])
         | 
| 73 | 
            +
                    end
         | 
| 73 74 | 
             
                  end
         | 
| 74 75 | 
             
                end
         | 
| 75 76 | 
             
              end
         | 
| @@ -10,20 +10,20 @@ module Command | |
| 10 10 | 
             
                  skip_confirm_option
         | 
| 11 11 | 
             
                ].freeze
         | 
| 12 12 | 
             
                DESCRIPTION = "Deletes the whole app (GVC with all workloads and all images) for all stale apps"
         | 
| 13 | 
            -
                LONG_DESCRIPTION = <<~ | 
| 13 | 
            +
                LONG_DESCRIPTION = <<~DESC
         | 
| 14 14 | 
             
                  - Deletes the whole app (GVC with all workloads and all images) for all stale apps
         | 
| 15 15 | 
             
                  - Stale apps are identified based on the creation date of the latest image
         | 
| 16 16 | 
             
                  - Specify the amount of days after an app should be considered stale through `stale_app_image_deployed_days` in the `.controlplane/controlplane.yml` file
         | 
| 17 17 | 
             
                  - If `match_if_app_name_starts_with` is `true` in the `.controlplane/controlplane.yml` file, it will delete all stale apps that start with the name
         | 
| 18 18 | 
             
                  - Will ask for explicit user confirmation
         | 
| 19 | 
            -
                 | 
| 19 | 
            +
                DESC
         | 
| 20 20 |  | 
| 21 21 | 
             
                def call # rubocop:disable Metrics/MethodLength
         | 
| 22 | 
            -
                  return progress.puts | 
| 22 | 
            +
                  return progress.puts("No stale apps found.") if stale_apps.empty?
         | 
| 23 23 |  | 
| 24 | 
            -
                  progress.puts | 
| 24 | 
            +
                  progress.puts("Stale apps:")
         | 
| 25 25 | 
             
                  stale_apps.each do |app|
         | 
| 26 | 
            -
                    progress.puts | 
| 26 | 
            +
                    progress.puts("  #{app[:name]} (#{Shell.color((app[:date]).to_s, :red)})")
         | 
| 27 27 | 
             
                  end
         | 
| 28 28 |  | 
| 29 29 | 
             
                  return unless confirm_delete
         | 
| @@ -74,14 +74,16 @@ module Command | |
| 74 74 | 
             
                end
         | 
| 75 75 |  | 
| 76 76 | 
             
                def delete_gvc(app)
         | 
| 77 | 
            -
                   | 
| 78 | 
            -
             | 
| 77 | 
            +
                  step("Deleting app '#{app[:name]}'") do
         | 
| 78 | 
            +
                    cp.gvc_delete(app[:name])
         | 
| 79 | 
            +
                  end
         | 
| 79 80 | 
             
                end
         | 
| 80 81 |  | 
| 81 82 | 
             
                def delete_images(app)
         | 
| 82 83 | 
             
                  app[:images].each do |image|
         | 
| 83 | 
            -
                     | 
| 84 | 
            -
             | 
| 84 | 
            +
                    step("Deleting image '#{image}'") do
         | 
| 85 | 
            +
                      cp.image_delete(image)
         | 
| 86 | 
            +
                    end
         | 
| 85 87 | 
             
                  end
         | 
| 86 88 | 
             
                end
         | 
| 87 89 | 
             
              end
         | 
    
        data/lib/command/config.rb
    CHANGED
    
    | @@ -6,28 +6,43 @@ module Command | |
| 6 6 | 
             
                OPTIONS = [
         | 
| 7 7 | 
             
                  app_option
         | 
| 8 8 | 
             
                ].freeze
         | 
| 9 | 
            -
                DESCRIPTION = "Displays  | 
| 10 | 
            -
                LONG_DESCRIPTION = <<~ | 
| 11 | 
            -
                  - Displays  | 
| 12 | 
            -
                 | 
| 13 | 
            -
                EXAMPLES = <<~ | 
| 9 | 
            +
                DESCRIPTION = "Displays config for each app or a specific app"
         | 
| 10 | 
            +
                LONG_DESCRIPTION = <<~DESC
         | 
| 11 | 
            +
                  - Displays config for each app or a specific app
         | 
| 12 | 
            +
                DESC
         | 
| 13 | 
            +
                EXAMPLES = <<~EX
         | 
| 14 14 | 
             
                  ```sh
         | 
| 15 | 
            -
                  # Shows the  | 
| 15 | 
            +
                  # Shows the config for each app.
         | 
| 16 16 | 
             
                  cpl config
         | 
| 17 17 |  | 
| 18 | 
            -
                  # Shows  | 
| 18 | 
            +
                  # Shows the config for a specific app.
         | 
| 19 19 | 
             
                  cpl config -a $APP_NAME
         | 
| 20 20 | 
             
                  ```
         | 
| 21 | 
            -
                 | 
| 21 | 
            +
                EX
         | 
| 22 22 |  | 
| 23 | 
            -
                def call
         | 
| 24 | 
            -
                   | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 23 | 
            +
                def call # rubocop:disable Metrics/MethodLength
         | 
| 24 | 
            +
                  if config.app
         | 
| 25 | 
            +
                    puts "#{Shell.color("Current config (app '#{config.app}')", :blue)}:"
         | 
| 26 | 
            +
                    puts pretty_print(config.current)
         | 
| 27 | 
            +
                    puts
         | 
| 28 | 
            +
                  else
         | 
| 29 | 
            +
                    config.apps.each do |app_name, app_options|
         | 
| 30 | 
            +
                      puts "#{Shell.color("Config for app '#{app_name}'", :blue)}:"
         | 
| 31 | 
            +
                      puts pretty_print(app_options)
         | 
| 32 | 
            +
                      puts
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                private
         | 
| 27 38 |  | 
| 28 | 
            -
             | 
| 29 | 
            -
                   | 
| 30 | 
            -
             | 
| 39 | 
            +
                def pretty_print(hash)
         | 
| 40 | 
            +
                  hash.transform_keys(&:to_s)
         | 
| 41 | 
            +
                      .to_yaml(indentation: 2)[4..]
         | 
| 42 | 
            +
                      # Adds an indentation of 2 to the beginning of each line
         | 
| 43 | 
            +
                      .gsub(/^(\s*)/, "  \\1")
         | 
| 44 | 
            +
                      # Adds an indentation of 2 before the '-' in array items
         | 
| 45 | 
            +
                      .gsub(/^(\s*)-\s/, "\\1  - ")
         | 
| 31 46 | 
             
                end
         | 
| 32 47 | 
             
              end
         | 
| 33 48 | 
             
            end
         | 
| @@ -0,0 +1,110 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Command
         | 
| 4 | 
            +
              class CopyImageFromUpstream < Base
         | 
| 5 | 
            +
                NAME = "copy-image-from-upstream"
         | 
| 6 | 
            +
                OPTIONS = [
         | 
| 7 | 
            +
                  app_option(required: true),
         | 
| 8 | 
            +
                  upstream_token_option(required: true),
         | 
| 9 | 
            +
                  image_option
         | 
| 10 | 
            +
                ].freeze
         | 
| 11 | 
            +
                DESCRIPTION = "Copies an image (by default the latest) from a source org to the current org"
         | 
| 12 | 
            +
                LONG_DESCRIPTION = <<~DESC
         | 
| 13 | 
            +
                  - Copies an image (by default the latest) from a source org to the current org
         | 
| 14 | 
            +
                  - The source org must be specified through `upstream` in the `.controlplane/controlplane.yml` file
         | 
| 15 | 
            +
                  - Additionally, the token for the source org must be provided through `--upstream-token` or `-t`
         | 
| 16 | 
            +
                  - A `cpln` profile will be temporarily created to pull the image from the source org
         | 
| 17 | 
            +
                DESC
         | 
| 18 | 
            +
                EXAMPLES = <<~EX
         | 
| 19 | 
            +
                  ```sh
         | 
| 20 | 
            +
                  # Copies the latest image from the source org to the current org.
         | 
| 21 | 
            +
                  cpl copy-image-from-upstream -a $APP_NAME --upstream-token $UPSTREAM_TOKEN
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  # Copies a specific image from the source org to the current org.
         | 
| 24 | 
            +
                  cpl copy-image-from-upstream -a $APP_NAME --upstream-token $UPSTREAM_TOKEN --image appimage:123
         | 
| 25 | 
            +
                  ```
         | 
| 26 | 
            +
                EX
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def call # rubocop:disable Metrics/MethodLength
         | 
| 29 | 
            +
                  ensure_docker_running!
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  @upstream = config[:upstream]
         | 
| 32 | 
            +
                  @upstream_org = config.apps[@upstream.to_sym][:cpln_org] || config.apps[@upstream.to_sym][:org]
         | 
| 33 | 
            +
                  ensure_upstream_org!
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  create_upstream_profile
         | 
| 36 | 
            +
                  fetch_upstream_image_url
         | 
| 37 | 
            +
                  fetch_app_image_url
         | 
| 38 | 
            +
                  pull_image_from_upstream
         | 
| 39 | 
            +
                  push_image_to_app
         | 
| 40 | 
            +
                ensure
         | 
| 41 | 
            +
                  delete_upstream_profile
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                private
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def ensure_docker_running!
         | 
| 47 | 
            +
                  `docker version > /dev/null 2>&1`
         | 
| 48 | 
            +
                  return if $CHILD_STATUS.success?
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  raise "Can't run Docker. Please make sure that it's installed and started, then try again."
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def ensure_upstream_org!
         | 
| 54 | 
            +
                  raise "Can't find option 'cpln_org' for app '#{@upstream}' in 'controlplane.yml'." unless @upstream_org
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                def create_upstream_profile
         | 
| 58 | 
            +
                  step("Creating upstream profile") do
         | 
| 59 | 
            +
                    loop do
         | 
| 60 | 
            +
                      @upstream_profile = "upstream-#{rand(1000..9999)}"
         | 
| 61 | 
            +
                      break unless cp.profile_exists?(@upstream_profile)
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    cp.profile_create(@upstream_profile, config.options[:upstream_token])
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                def fetch_upstream_image_url
         | 
| 69 | 
            +
                  step("Fetching upstream image URL") do
         | 
| 70 | 
            +
                    cp.profile_switch(@upstream_profile)
         | 
| 71 | 
            +
                    upstream_image = config.options[:image]
         | 
| 72 | 
            +
                    upstream_image = latest_image(@upstream, @upstream_org) if !upstream_image || upstream_image == "latest"
         | 
| 73 | 
            +
                    @upstream_image_url = "#{@upstream_org}.registry.cpln.io/#{upstream_image}"
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                def fetch_app_image_url
         | 
| 78 | 
            +
                  step("Fetching app image URL") do
         | 
| 79 | 
            +
                    cp.profile_switch("default")
         | 
| 80 | 
            +
                    app_image = latest_image_next(config.app, config.org)
         | 
| 81 | 
            +
                    @app_image_url = "#{config.org}.registry.cpln.io/#{app_image}"
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                def pull_image_from_upstream
         | 
| 86 | 
            +
                  step("Pulling image from '#{@upstream_image_url}'") do
         | 
| 87 | 
            +
                    cp.profile_switch(@upstream_profile)
         | 
| 88 | 
            +
                    cp.image_login(@upstream_org)
         | 
| 89 | 
            +
                    cp.image_pull(@upstream_image_url)
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                def push_image_to_app
         | 
| 94 | 
            +
                  step("Pushing image to '#{@app_image_url}'") do
         | 
| 95 | 
            +
                    cp.profile_switch("default")
         | 
| 96 | 
            +
                    cp.image_login(config.org)
         | 
| 97 | 
            +
                    cp.image_tag(@upstream_image_url, @app_image_url)
         | 
| 98 | 
            +
                    cp.image_push(@app_image_url)
         | 
| 99 | 
            +
                  end
         | 
| 100 | 
            +
                end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                def delete_upstream_profile
         | 
| 103 | 
            +
                  return unless @upstream_profile
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                  step("Deleting upstream profile") do
         | 
| 106 | 
            +
                    cp.profile_delete(@upstream_profile)
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
              end
         | 
| 110 | 
            +
            end
         | 
    
        data/lib/command/delete.rb
    CHANGED
    
    | @@ -8,10 +8,10 @@ module Command | |
| 8 8 | 
             
                  skip_confirm_option
         | 
| 9 9 | 
             
                ].freeze
         | 
| 10 10 | 
             
                DESCRIPTION = "Deletes the whole app (GVC with all workloads and all images)"
         | 
| 11 | 
            -
                LONG_DESCRIPTION = <<~ | 
| 11 | 
            +
                LONG_DESCRIPTION = <<~DESC
         | 
| 12 12 | 
             
                  - Deletes the whole app (GVC with all workloads and all images)
         | 
| 13 13 | 
             
                  - Will ask for explicit user confirmation
         | 
| 14 | 
            -
                 | 
| 14 | 
            +
                DESC
         | 
| 15 15 |  | 
| 16 16 | 
             
                def call
         | 
| 17 17 | 
             
                  return unless confirm_delete
         | 
| @@ -33,25 +33,23 @@ module Command | |
| 33 33 | 
             
                end
         | 
| 34 34 |  | 
| 35 35 | 
             
                def delete_gvc
         | 
| 36 | 
            -
                  progress.puts " | 
| 36 | 
            +
                  return progress.puts("App '#{config.app}' does not exist.") if cp.fetch_gvc.nil?
         | 
| 37 37 |  | 
| 38 | 
            -
                   | 
| 39 | 
            -
             | 
| 40 | 
            -
                   | 
| 41 | 
            -
                  progress.puts config.app
         | 
| 38 | 
            +
                  step("Deleting app '#{config.app}'") do
         | 
| 39 | 
            +
                    cp.gvc_delete
         | 
| 40 | 
            +
                  end
         | 
| 42 41 | 
             
                end
         | 
| 43 42 |  | 
| 44 43 | 
             
                def delete_images
         | 
| 45 | 
            -
                  progress.puts "- Deleting image(s):"
         | 
| 46 | 
            -
             | 
| 47 44 | 
             
                  images = cp.image_query["items"]
         | 
| 48 45 | 
             
                             .filter_map { |item| item["name"] if item["name"].start_with?("#{config.app}:") }
         | 
| 49 46 |  | 
| 50 | 
            -
                  return progress.puts " | 
| 47 | 
            +
                  return progress.puts("No images to delete.") unless images.any?
         | 
| 51 48 |  | 
| 52 49 | 
             
                  images.each do |image|
         | 
| 53 | 
            -
                     | 
| 54 | 
            -
             | 
| 50 | 
            +
                    step("Deleting image '#{image}'") do
         | 
| 51 | 
            +
                      cp.image_delete(image)
         | 
| 52 | 
            +
                    end
         | 
| 55 53 | 
             
                  end
         | 
| 56 54 | 
             
                end
         | 
| 57 55 | 
             
              end
         | 
    
        data/lib/command/deploy_image.rb
    CHANGED
    
    | @@ -7,19 +7,20 @@ module Command | |
| 7 7 | 
             
                  app_option(required: true)
         | 
| 8 8 | 
             
                ].freeze
         | 
| 9 9 | 
             
                DESCRIPTION = "Deploys the latest image to app workloads"
         | 
| 10 | 
            -
                LONG_DESCRIPTION = <<~ | 
| 10 | 
            +
                LONG_DESCRIPTION = <<~DESC
         | 
| 11 11 | 
             
                  - Deploys the latest image to app workloads
         | 
| 12 | 
            -
                 | 
| 12 | 
            +
                DESC
         | 
| 13 13 |  | 
| 14 14 | 
             
                def call
         | 
| 15 15 | 
             
                  image = latest_image
         | 
| 16 16 |  | 
| 17 17 | 
             
                  config[:app_workloads].each do |workload|
         | 
| 18 18 | 
             
                    cp.fetch_workload!(workload).dig("spec", "containers").each do |container|
         | 
| 19 | 
            -
                      next unless container["image"].match?(%r{^/org/#{config | 
| 19 | 
            +
                      next unless container["image"].match?(%r{^/org/#{config.org}/image/#{config.app}:})
         | 
| 20 20 |  | 
| 21 | 
            -
                       | 
| 22 | 
            -
             | 
| 21 | 
            +
                      step("Deploying image '#{image}' for workload '#{container['name']}'") do
         | 
| 22 | 
            +
                        cp.workload_set_image_ref(workload, container: container["name"], image: image)
         | 
| 23 | 
            +
                      end
         | 
| 23 24 | 
             
                    end
         | 
| 24 25 | 
             
                  end
         | 
| 25 26 | 
             
                end
         | 
    
        data/lib/command/env.rb
    CHANGED
    
    | @@ -7,9 +7,9 @@ module Command | |
| 7 7 | 
             
                  app_option(required: true)
         | 
| 8 8 | 
             
                ].freeze
         | 
| 9 9 | 
             
                DESCRIPTION = "Displays app-specific environment variables"
         | 
| 10 | 
            -
                LONG_DESCRIPTION = <<~ | 
| 10 | 
            +
                LONG_DESCRIPTION = <<~DESC
         | 
| 11 11 | 
             
                  - Displays app-specific environment variables
         | 
| 12 | 
            -
                 | 
| 12 | 
            +
                DESC
         | 
| 13 13 |  | 
| 14 14 | 
             
                def call
         | 
| 15 15 | 
             
                  cp.fetch_gvc!.dig("spec", "env").map do |prop|
         |