Commit ce99813c authored by Steve Abrams's avatar Steve Abrams Committed by Robert Speicher
Browse files

Add name_regex_keep param to tag bulk delete api

Add new param name_regex_keep to the container
registry bulk delete tag API endpoint.

name_regex is renamed name_regex_delete. The new
param will override matches found by the delete
param.
parent 2efa1aff
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -61,10 +61,15 @@ def order_by_date(tags)
      end

      def filter_by_name(tags)
        regex = Gitlab::UntrustedRegexp.new("\\A#{params['name_regex']}\\z")
        # Technical Debt: https://gitlab.com/gitlab-org/gitlab/issues/207267
        # name_regex to be removed when container_expiration_policies is updated
        # to have both regex columns
        regex_delete = Gitlab::UntrustedRegexp.new("\\A#{params['name_regex_delete'] || params['name_regex']}\\z")
        regex_retain = Gitlab::UntrustedRegexp.new("\\A#{params['name_regex_keep']}\\z")

        tags.select do |tag|
          regex.scan(tag.name).any?
          # regex_retain will override any overlapping matches by regex_delete
          regex_delete.match?(tag.name) && !regex_retain.match?(tag.name)
        end
      end

+5 −0
Original line number Diff line number Diff line
---
title: Add name_regex_keep param to container registry bulk delete API endpoint
merge_request: 25484
author:
type: added
+13 −5
Original line number Diff line number Diff line
@@ -231,7 +231,9 @@ DELETE /projects/:id/registry/repositories/:repository_id/tags
| --------- | ---- | -------- | ----------- |
| `id`      | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
| `repository_id` | integer | yes | The ID of registry repository. |
| `name_regex` | string | yes | The [re2](https://github.com/google/re2/wiki/Syntax) regex of the name to delete. To delete all tags specify `.*`.|
| `name_regex` | string | no | The [re2](https://github.com/google/re2/wiki/Syntax) regex of the name to delete. To delete all tags specify `.*`. **Note:** `name_regex` is deprecated in favor of `name_regex_delete`.|
| `name_regex_delete` | string | yes | The [re2](https://github.com/google/re2/wiki/Syntax) regex of the name to delete. To delete all tags specify `.*`.|
| `name_regex_keep` | string | no | The [re2](https://github.com/google/re2/wiki/Syntax) regex of the name to keep. This value will override any matches from `name_regex_delete`. Note: setting to `.*` will result in a no-op. |
| `keep_n` | integer | no | The amount of latest tags of given name to keep. |
| `older_than` | string | no | Tags to delete that are older than the given time, written in human readable form `1h`, `1d`, `1month`. |

@@ -239,7 +241,7 @@ This API call performs the following operations:

1. It orders all tags by creation date. The creation date is the time of the
   manifest creation, not the time of tag push.
1. It removes only the tags matching the given `name_regex`.
1. It removes only the tags matching the given `name_regex_delete` (or deprecated `name_regex`), keeping any that match `name_regex_keep`.
1. It never removes the tag named `latest`.
1. It keeps N latest matching tags (if `keep_n` is specified).
1. It only removes tags that are older than X amount of time (if `older_than` is specified).
@@ -261,17 +263,23 @@ Examples:
   and remove ones that are older than 2 days:

   ```shell
   curl --request DELETE --data 'name_regex=[0-9a-z]{40}' --data 'keep_n=5' --data 'older_than=2d' --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/registry/repositories/2/tags"
   curl --request DELETE --data 'name_regex_delete=[0-9a-z]{40}' --data 'keep_n=5' --data 'older_than=2d' --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/registry/repositories/2/tags"
   ```

1. Remove all tags, but keep always the latest 5:

   ```shell
   curl --request DELETE --data 'name_regex=.*' --data 'keep_n=5' --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/registry/repositories/2/tags"
   curl --request DELETE --data 'name_regex_delete=.*' --data 'keep_n=5' --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/registry/repositories/2/tags"
   ```

1. Remove all tags, but keep always tags beginning with `stable`:

   ```shell
   curl --request DELETE --data 'name_regex_delete=.*' --data 'name_regex_keep=stable.*' --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/registry/repositories/2/tags"
   ```

1. Remove all tags that are older than 1 month:

   ```shell
   curl --request DELETE --data 'name_regex=.*' --data 'older_than=1month' --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/registry/repositories/2/tags"
   curl --request DELETE --data 'name_regex_delete=.*' --data 'older_than=1month' --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/registry/repositories/2/tags"
   ```
+10 −1
Original line number Diff line number Diff line
@@ -69,7 +69,16 @@ class ProjectContainerRepositories < Grape::API
      end
      params do
        requires :repository_id, type: Integer, desc: 'The ID of the repository'
        optional :name_regex_delete, type: String, desc: 'The tag name regexp to delete, specify .* to delete all'
        # require either name_regex (deprecated) or name_regex_delete, it is ok to have both
        given name_regex_delete: ->(val) { val.nil? } do
          requires :name_regex, type: String, desc: 'The tag name regexp to delete, specify .* to delete all'
        end
        optional :name_regex, type: String, desc: 'The tag name regexp to delete, specify .* to delete all'
        given name_regex: ->(val) { val.nil? } do
          requires :name_regex_delete, type: String, desc: 'The tag name regexp to delete, specify .* to delete all'
        end
        optional :name_regex_keep, type: String, desc: 'The tag name regexp to retain'
        optional :keep_n, type: Integer, desc: 'Keep n of latest tags with matching name'
        optional :older_than, type: String, desc: 'Delete older than: 1h, 1d, 1month'
      end
+52 −3
Original line number Diff line number Diff line
@@ -109,7 +109,7 @@

    context 'disallowed' do
      let(:params) do
        { name_regex: 'v10.*' }
        { name_regex_delete: 'v10.*' }
      end

      it_behaves_like 'rejected container repository access', :developer, :forbidden
@@ -130,16 +130,33 @@
        end
      end

      context 'without name_regex' do
        let(:params) do
          { keep_n: 100,
            older_than: '1 day',
            other: 'some value' }
        end

        it 'returns bad request' do
          subject

          expect(response).to have_gitlab_http_status(:bad_request)
        end
      end

      context 'passes all declared parameters' do
        let(:params) do
          { name_regex: 'v10.*',
          { name_regex_delete: 'v10.*',
            name_regex_keep: 'v10.1.*',
            keep_n: 100,
            older_than: '1 day',
            other: 'some value' }
        end

        let(:worker_params) do
          { name_regex: 'v10.*',
          { name_regex: nil,
            name_regex_delete: 'v10.*',
            name_regex_keep: 'v10.1.*',
            keep_n: 100,
            older_than: '1 day',
            container_expiration_policy: false }
@@ -174,6 +191,38 @@
          end
        end
      end

      context 'with deprecated name_regex param' do
        let(:params) do
          { name_regex: 'v10.*',
            name_regex_keep: 'v10.1.*',
            keep_n: 100,
            older_than: '1 day',
            other: 'some value' }
        end

        let(:worker_params) do
          { name_regex: 'v10.*',
            name_regex_delete: nil,
            name_regex_keep: 'v10.1.*',
            keep_n: 100,
            older_than: '1 day',
            container_expiration_policy: false }
        end

        let(:lease_key) { "container_repository:cleanup_tags:#{root_repository.id}" }

        it 'schedules cleanup of tags repository' do
          stub_last_activity_update
          stub_exclusive_lease(lease_key, timeout: 1.hour)
          expect(CleanupContainerRepositoryWorker).to receive(:perform_async)
            .with(maintainer.id, root_repository.id, worker_params)

          subject

          expect(response).to have_gitlab_http_status(:accepted)
        end
      end
    end
  end

Loading