Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorKamil Trzciński <ayufan@ayufan.eu>2019-01-10 17:22:58 +0300
committerKamil Trzciński <ayufan@ayufan.eu>2019-01-25 15:13:48 +0300
commit045d07bab37df2020f650f7354157f5267f57c8a (patch)
treee99aad48ed248daa02ff1d7fe9250b327ffa77bb /lib
parent267ce96e36ecec169b02410bfea85e6d31715910 (diff)
Add Container Registry API
This includes a set of APIs to manipulate container registry. This includes also an ability to delete tags based on requested criteria, like keep-last-n, matching-name, older-than.
Diffstat (limited to 'lib')
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/container_registry.rb143
-rw-r--r--lib/api/entities/container_registry.rb29
-rw-r--r--lib/container_registry/tag.rb38
4 files changed, 200 insertions, 11 deletions
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 59b67c67f9d..300fa9590e4 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -100,6 +100,7 @@ module API
mount ::API::CircuitBreakers
mount ::API::Commits
mount ::API::CommitStatuses
+ mount ::API::ContainerRegistry
mount ::API::DeployKeys
mount ::API::Deployments
mount ::API::Environments
diff --git a/lib/api/container_registry.rb b/lib/api/container_registry.rb
new file mode 100644
index 00000000000..e4493910196
--- /dev/null
+++ b/lib/api/container_registry.rb
@@ -0,0 +1,143 @@
+# frozen_string_literal: true
+
+module API
+ class ContainerRegistry < Grape::API
+ include PaginationParams
+
+ REGISTRY_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(
+ tag_name: API::NO_SLASH_URL_PART_REGEX)
+
+ before { error!('404 Not Found', 404) unless Feature.enabled?(:container_registry_api, user_project, default_enabled: true) }
+ before { authorize_read_container_images! }
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ desc 'Get a project container repositories' do
+ detail 'This feature was introduced in GitLab 11.8.'
+ success Entities::ContainerRegistry::Repository
+ end
+ params do
+ use :pagination
+ end
+ get ':id/registry/repositories' do
+ repositories = user_project.container_repositories.ordered
+
+ present paginate(repositories), with: Entities::ContainerRegistry::Repository
+ end
+
+ desc 'Delete repository' do
+ detail 'This feature was introduced in GitLab 11.8.'
+ end
+ params do
+ requires :repository_id, type: Integer, desc: 'The ID of the repository'
+ end
+ delete ':id/registry/repositories/:repository_id', requirements: REGISTRY_ENDPOINT_REQUIREMENTS do
+ authorize_admin_container_image!
+
+ DeleteContainerRepositoryWorker.perform_async(current_user.id, repository.id)
+
+ status :accepted
+ end
+
+ desc 'Get a list of repositories tags' do
+ detail 'This feature was introduced in GitLab 11.8.'
+ success Entities::ContainerRegistry::Tag
+ end
+ params do
+ requires :repository_id, type: Integer, desc: 'The ID of the repository'
+ use :pagination
+ end
+ get ':id/registry/repositories/:repository_id/tags', requirements: REGISTRY_ENDPOINT_REQUIREMENTS do
+ authorize_read_container_image!
+
+ tags = Kaminari.paginate_array(repository.tags)
+ present paginate(tags), with: Entities::ContainerRegistry::Tag
+ end
+
+ desc 'Delete repository tags (in bulk)' do
+ detail 'This feature was introduced in GitLab 11.8.'
+ end
+ params do
+ requires :repository_id, type: Integer, desc: 'The ID of the repository'
+ requires :name_regex, type: String, desc: 'The tag name regexp to delete, specify .* to delete all'
+ 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
+ delete ':id/registry/repositories/:repository_id/tags', requirements: REGISTRY_ENDPOINT_REQUIREMENTS do
+ authorize_admin_container_image!
+
+ CleanupContainerRepositoryWorker.perform_async(current_user.id, repository.id,
+ declared_params.except(:repository_id)) # rubocop: disable CodeReuse/ActiveRecord
+
+ status :accepted
+ end
+
+ desc 'Get a details about repository tag' do
+ detail 'This feature was introduced in GitLab 11.8.'
+ success Entities::ContainerRegistry::TagDetails
+ 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
+ get ':id/registry/repositories/:repository_id/tags/:tag_name', requirements: REGISTRY_ENDPOINT_REQUIREMENTS do
+ 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.'
+ 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
+ delete ':id/registry/repositories/:repository_id/tags/:tag_name', requirements: REGISTRY_ENDPOINT_REQUIREMENTS do
+ authorize_destroy_container_image!
+ validate_tag!
+
+ tag.delete
+
+ status :ok
+ 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_update_container_image!
+ authorize! :update_container_image, repository
+ end
+
+ def authorize_destroy_container_image!
+ authorize! :admin_container_image, repository
+ end
+
+ def authorize_admin_container_image!
+ authorize! :admin_container_image, repository
+ end
+
+ 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
diff --git a/lib/api/entities/container_registry.rb b/lib/api/entities/container_registry.rb
new file mode 100644
index 00000000000..00833ca7480
--- /dev/null
+++ b/lib/api/entities/container_registry.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module ContainerRegistry
+ class Repository < Grape::Entity
+ expose :id
+ expose :name
+ expose :path
+ expose :location
+ expose :created_at
+ end
+
+ class Tag < Grape::Entity
+ expose :name
+ expose :path
+ expose :location
+ end
+
+ class TagDetails < Tag
+ expose :revision
+ expose :short_revision
+ expose :digest
+ expose :created_at
+ expose :total_size
+ end
+ end
+ end
+end
diff --git a/lib/container_registry/tag.rb b/lib/container_registry/tag.rb
index 8633e764f90..ef41dc560c9 100644
--- a/lib/container_registry/tag.rb
+++ b/lib/container_registry/tag.rb
@@ -2,6 +2,8 @@
module ContainerRegistry
class Tag
+ include Gitlab::Utils::StrongMemoize
+
attr_reader :repository, :name
delegate :registry, :client, to: :repository
@@ -15,6 +17,10 @@ module ContainerRegistry
manifest.present?
end
+ def latest?
+ name == "latest"
+ end
+
def v1?
manifest && manifest['schemaVersion'] == 1
end
@@ -24,7 +30,9 @@ module ContainerRegistry
end
def manifest
- @manifest ||= client.repository_manifest(repository.path, name)
+ strong_memoize(:manifest) do
+ client.repository_manifest(repository.path, name)
+ end
end
def path
@@ -42,36 +50,44 @@ module ContainerRegistry
end
def digest
- @digest ||= client.repository_tag_digest(repository.path, name)
+ strong_memoize(:digest) do
+ client.repository_tag_digest(repository.path, name)
+ end
end
def config_blob
- return @config_blob if defined?(@config_blob)
return unless manifest && manifest['config']
- @config_blob = repository.blob(manifest['config'])
+ strong_memoize(:config_blob) do
+ repository.blob(manifest['config'])
+ end
end
def config
- return unless config_blob
+ return unless config_blob&.data
- @config ||= ContainerRegistry::Config.new(self, config_blob) if config_blob.data
+ strong_memoize(:config) do
+ ContainerRegistry::Config.new(self, config_blob)
+ end
end
def created_at
return unless config
- @created_at ||= DateTime.rfc3339(config['created'])
+ strong_memoize(:created_at) do
+ DateTime.rfc3339(config['created'])
+ end
end
def layers
- return @layers if defined?(@layers)
return unless manifest
- layers = manifest['layers'] || manifest['fsLayers']
+ strong_memoize(:layers) do
+ layers = manifest['layers'] || manifest['fsLayers']
- @layers = layers.map do |layer|
- repository.blob(layer)
+ layers.map do |layer|
+ repository.blob(layer)
+ end
end
end