2019-03-02 22:35:43 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module API
|
2021-01-03 14:25:43 +05:30
|
|
|
class ProjectContainerRepositories < ::API::Base
|
2019-03-02 22:35:43 +05:30
|
|
|
include PaginationParams
|
2021-11-18 22:05:49 +05:30
|
|
|
include ::API::Helpers::ContainerRegistryHelpers
|
|
|
|
|
2021-01-03 14:25:43 +05:30
|
|
|
helpers ::API::Helpers::PackagesHelpers
|
2019-03-02 22:35:43 +05:30
|
|
|
|
2019-10-12 21:52:04 +05:30
|
|
|
REPOSITORY_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(
|
2019-03-02 22:35:43 +05:30
|
|
|
tag_name: API::NO_SLASH_URL_PART_REGEX)
|
|
|
|
|
|
|
|
before { authorize_read_container_images! }
|
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
feature_category :container_registry
|
2022-07-16 23:28:13 +05:30
|
|
|
urgency :low
|
2021-01-29 00:20:46 +05:30
|
|
|
|
2019-03-02 22:35:43 +05:30
|
|
|
params do
|
2023-01-13 00:05:48 +05:30
|
|
|
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
2021-06-08 01:23:25 +05:30
|
|
|
route_setting :authentication, job_token_allowed: true, job_token_scope: :project
|
2019-03-02 22:35:43 +05:30
|
|
|
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
2023-03-04 22:38:38 +05:30
|
|
|
desc 'List container repositories within a project' do
|
2019-03-02 22:35:43 +05:30
|
|
|
detail 'This feature was introduced in GitLab 11.8.'
|
|
|
|
success Entities::ContainerRegistry::Repository
|
2023-03-04 22:38:38 +05:30
|
|
|
failure [
|
|
|
|
{ code: 401, message: 'Unauthorized' },
|
|
|
|
{ code: 404, message: 'Not Found' }
|
|
|
|
]
|
|
|
|
is_array true
|
|
|
|
tags %w[container_registry]
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
params do
|
|
|
|
use :pagination
|
2019-10-12 21:52:04 +05:30
|
|
|
optional :tags, type: Boolean, default: false, desc: 'Determines if tags should be included'
|
2020-06-23 00:09:42 +05:30
|
|
|
optional :tags_count, type: Boolean, default: false, desc: 'Determines if the tags count should be included'
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
get ':id/registry/repositories' do
|
2019-10-12 21:52:04 +05:30
|
|
|
repositories = ContainerRepositoriesFinder.new(
|
2019-12-26 22:10:19 +05:30
|
|
|
user: current_user, subject: user_project
|
2019-10-12 21:52:04 +05:30
|
|
|
).execute
|
2019-03-02 22:35:43 +05:30
|
|
|
|
2021-09-04 01:27:46 +05:30
|
|
|
track_package_event('list_repositories', :container, user: current_user, project: user_project, namespace: user_project.namespace)
|
2019-12-26 22:10:19 +05:30
|
|
|
|
2020-06-23 00:09:42 +05:30
|
|
|
present paginate(repositories), with: Entities::ContainerRegistry::Repository, tags: params[:tags], tags_count: params[:tags_count]
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
desc 'Delete repository' do
|
|
|
|
detail 'This feature was introduced in GitLab 11.8.'
|
2023-03-04 22:38:38 +05:30
|
|
|
success status: :accepted, message: 'Success'
|
|
|
|
failure [
|
|
|
|
{ code: 401, message: 'Unauthorized' },
|
|
|
|
{ code: 404, message: 'Not Found' }
|
|
|
|
]
|
|
|
|
is_array true
|
|
|
|
tags %w[container_registry]
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
params do
|
|
|
|
requires :repository_id, type: Integer, desc: 'The ID of the repository'
|
|
|
|
end
|
2019-10-12 21:52:04 +05:30
|
|
|
delete ':id/registry/repositories/:repository_id', requirements: REPOSITORY_ENDPOINT_REQUIREMENTS do
|
2019-03-02 22:35:43 +05:30
|
|
|
authorize_admin_container_image!
|
2023-01-13 00:05:48 +05:30
|
|
|
repository.delete_scheduled!
|
|
|
|
|
2021-09-04 01:27:46 +05:30
|
|
|
track_package_event('delete_repository', :container, user: current_user, project: user_project, namespace: user_project.namespace)
|
2019-03-02 22:35:43 +05:30
|
|
|
|
|
|
|
status :accepted
|
|
|
|
end
|
|
|
|
|
2023-03-04 22:38:38 +05:30
|
|
|
desc 'List tags of a repository' do
|
2019-03-02 22:35:43 +05:30
|
|
|
detail 'This feature was introduced in GitLab 11.8.'
|
|
|
|
success Entities::ContainerRegistry::Tag
|
2023-03-04 22:38:38 +05:30
|
|
|
failure [
|
|
|
|
{ code: 401, message: 'Unauthorized' },
|
|
|
|
{ code: 404, message: 'Not Found' }
|
|
|
|
]
|
|
|
|
is_array true
|
|
|
|
tags %w[container_registry]
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
params do
|
|
|
|
requires :repository_id, type: Integer, desc: 'The ID of the repository'
|
|
|
|
use :pagination
|
|
|
|
end
|
2019-10-12 21:52:04 +05:30
|
|
|
get ':id/registry/repositories/:repository_id/tags', requirements: REPOSITORY_ENDPOINT_REQUIREMENTS do
|
2019-03-02 22:35:43 +05:30
|
|
|
authorize_read_container_image!
|
|
|
|
|
|
|
|
tags = Kaminari.paginate_array(repository.tags)
|
2021-09-04 01:27:46 +05:30
|
|
|
track_package_event('list_tags', :container, user: current_user, project: user_project, namespace: user_project.namespace)
|
2019-12-26 22:10:19 +05:30
|
|
|
|
2019-03-02 22:35:43 +05:30
|
|
|
present paginate(tags), with: Entities::ContainerRegistry::Tag
|
|
|
|
end
|
|
|
|
|
|
|
|
desc 'Delete repository tags (in bulk)' do
|
|
|
|
detail 'This feature was introduced in GitLab 11.8.'
|
2023-03-04 22:38:38 +05:30
|
|
|
success status: :accepted, message: 'Success'
|
|
|
|
failure [
|
|
|
|
{ code: 400, message: 'Bad Request' },
|
|
|
|
{ code: 401, message: 'Unauthorized' },
|
|
|
|
{ code: 404, message: 'Not Found' }
|
|
|
|
]
|
|
|
|
tags %w[container_registry]
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
params do
|
|
|
|
requires :repository_id, type: Integer, desc: 'The ID of the repository'
|
2020-06-23 00:09:42 +05:30
|
|
|
optional :name_regex_delete, type: String, untrusted_regexp: true, desc: 'The tag name regexp to delete, specify .* to delete all'
|
|
|
|
optional :name_regex, type: String, untrusted_regexp: true, desc: 'The tag name regexp to delete, specify .* to delete all'
|
2020-04-08 14:13:33 +05:30
|
|
|
# require either name_regex (deprecated) or name_regex_delete, it is ok to have both
|
|
|
|
at_least_one_of :name_regex, :name_regex_delete
|
2020-06-23 00:09:42 +05:30
|
|
|
optional :name_regex_keep, type: String, untrusted_regexp: true, desc: 'The tag name regexp to retain'
|
2019-03-02 22:35:43 +05:30
|
|
|
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
|
2019-10-12 21:52:04 +05:30
|
|
|
delete ':id/registry/repositories/:repository_id/tags', requirements: REPOSITORY_ENDPOINT_REQUIREMENTS do
|
2019-03-02 22:35:43 +05:30
|
|
|
authorize_admin_container_image!
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
message = 'This request has already been made. You can run this at most once an hour for a given container repository'
|
|
|
|
render_api_error!(message, 400) unless obtain_new_cleanup_container_lease
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
# rubocop:disable CodeReuse/Worker
|
2019-03-02 22:35:43 +05:30
|
|
|
CleanupContainerRepositoryWorker.perform_async(current_user.id, repository.id,
|
2022-07-16 23:28:13 +05:30
|
|
|
declared_params.except(:repository_id))
|
2020-03-13 15:44:24 +05:30
|
|
|
# rubocop:enable CodeReuse/Worker
|
2019-03-02 22:35:43 +05:30
|
|
|
|
2021-09-04 01:27:46 +05:30
|
|
|
track_package_event('delete_tag_bulk', :container, user: current_user, project: user_project, namespace: user_project.namespace)
|
2019-12-26 22:10:19 +05:30
|
|
|
|
2019-03-02 22:35:43 +05:30
|
|
|
status :accepted
|
|
|
|
end
|
|
|
|
|
2023-03-04 22:38:38 +05:30
|
|
|
desc 'Get details about a repository tag' do
|
2019-03-02 22:35:43 +05:30
|
|
|
detail 'This feature was introduced in GitLab 11.8.'
|
|
|
|
success Entities::ContainerRegistry::TagDetails
|
2023-03-04 22:38:38 +05:30
|
|
|
failure [
|
|
|
|
{ code: 400, message: 'Bad Request' },
|
|
|
|
{ code: 401, message: 'Unauthorized' },
|
|
|
|
{ code: 404, message: 'Not Found' }
|
|
|
|
]
|
|
|
|
tags %w[container_registry]
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
params do
|
|
|
|
requires :repository_id, type: Integer, desc: 'The ID of the repository'
|
|
|
|
requires :tag_name, type: String, desc: 'The name of the tag'
|
|
|
|
end
|
2019-10-12 21:52:04 +05:30
|
|
|
get ':id/registry/repositories/:repository_id/tags/:tag_name', requirements: REPOSITORY_ENDPOINT_REQUIREMENTS do
|
2019-03-02 22:35:43 +05:30
|
|
|
authorize_read_container_image!
|
|
|
|
validate_tag!
|
|
|
|
|
|
|
|
present tag, with: Entities::ContainerRegistry::TagDetails
|
|
|
|
end
|
|
|
|
|
|
|
|
desc 'Delete repository tag' do
|
|
|
|
detail 'This feature was introduced in GitLab 11.8.'
|
2023-03-04 22:38:38 +05:30
|
|
|
success status: :ok, message: 'Success'
|
|
|
|
failure [
|
|
|
|
{ code: 400, message: 'Bad Request' },
|
|
|
|
{ code: 401, message: 'Unauthorized' },
|
|
|
|
{ code: 404, message: 'Not Found' }
|
|
|
|
]
|
|
|
|
tags %w[container_registry]
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
params do
|
|
|
|
requires :repository_id, type: Integer, desc: 'The ID of the repository'
|
|
|
|
requires :tag_name, type: String, desc: 'The name of the tag'
|
|
|
|
end
|
2019-10-12 21:52:04 +05:30
|
|
|
delete ':id/registry/repositories/:repository_id/tags/:tag_name', requirements: REPOSITORY_ENDPOINT_REQUIREMENTS do
|
2019-03-02 22:35:43 +05:30
|
|
|
authorize_destroy_container_image!
|
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
result = ::Projects::ContainerRepository::DeleteTagsService
|
|
|
|
.new(repository.project, current_user, tags: [declared_params[:tag_name]])
|
|
|
|
.execute(repository)
|
|
|
|
|
|
|
|
if result[:status] == :success
|
2021-09-04 01:27:46 +05:30
|
|
|
track_package_event('delete_tag', :container, user: current_user, project: user_project, namespace: user_project.namespace)
|
2019-12-26 22:10:19 +05:30
|
|
|
|
2019-12-21 20:55:43 +05:30
|
|
|
status :ok
|
|
|
|
else
|
|
|
|
status :bad_request
|
|
|
|
end
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
helpers do
|
|
|
|
def authorize_read_container_images!
|
|
|
|
authorize! :read_container_image, user_project
|
|
|
|
end
|
|
|
|
|
|
|
|
def authorize_read_container_image!
|
|
|
|
authorize! :read_container_image, repository
|
|
|
|
end
|
|
|
|
|
|
|
|
def authorize_destroy_container_image!
|
2019-09-04 21:01:54 +05:30
|
|
|
authorize! :destroy_container_image, repository
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
def authorize_admin_container_image!
|
|
|
|
authorize! :admin_container_image, repository
|
|
|
|
end
|
|
|
|
|
2019-09-30 21:07:59 +05:30
|
|
|
def obtain_new_cleanup_container_lease
|
|
|
|
Gitlab::ExclusiveLease
|
|
|
|
.new("container_repository:cleanup_tags:#{repository.id}",
|
|
|
|
timeout: 1.hour)
|
|
|
|
.try_obtain
|
|
|
|
end
|
|
|
|
|
2019-03-02 22:35:43 +05:30
|
|
|
def repository
|
|
|
|
@repository ||= user_project.container_repositories.find(params[:repository_id])
|
|
|
|
end
|
|
|
|
|
|
|
|
def tag
|
|
|
|
@tag ||= repository.tag(params[:tag_name])
|
|
|
|
end
|
|
|
|
|
|
|
|
def validate_tag!
|
|
|
|
not_found!('Tag') unless tag.valid?
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|