summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/container_registry/blob.rb54
-rw-r--r--lib/container_registry/client.rb69
-rw-r--r--lib/container_registry/config.rb15
-rw-r--r--lib/container_registry/registry.rb15
-rw-r--r--lib/container_registry/repository.rb61
-rw-r--r--lib/container_registry/tag.rb79
-rw-r--r--lib/gitlab/regex.rb4
7 files changed, 297 insertions, 0 deletions
diff --git a/lib/container_registry/blob.rb b/lib/container_registry/blob.rb
new file mode 100644
index 00000000000..d59792a383e
--- /dev/null
+++ b/lib/container_registry/blob.rb
@@ -0,0 +1,54 @@
+module ContainerRegistry
+ class Blob
+ attr_reader :repository, :config
+
+ def initialize(repository, config)
+ @repository = repository
+ @config = config || {}
+ end
+
+ def valid?
+ digest.present?
+ end
+
+ def path
+ "#{repository.path}@#{digest}"
+ end
+
+ def digest
+ config['digest']
+ end
+
+ def type
+ config['mediaType']
+ end
+
+ def size
+ config['size']
+ end
+
+ def revision
+ digest.split(':')[1]
+ end
+
+ def short_revision
+ revision[0..8]
+ end
+
+ def client
+ @client ||= repository.client
+ end
+
+ def delete
+ client.delete_blob(repository.name, digest)
+ end
+
+ def data
+ @data ||= client.blob(repository.name, digest, type)
+ end
+
+ def mount_to(to_repository)
+ client.repository_mount_blob(to_repository.name, digest, repository.name)
+ end
+ end
+end
diff --git a/lib/container_registry/client.rb b/lib/container_registry/client.rb
new file mode 100644
index 00000000000..c250a4b6946
--- /dev/null
+++ b/lib/container_registry/client.rb
@@ -0,0 +1,69 @@
+require 'faraday'
+require 'faraday_middleware'
+
+module ContainerRegistry
+ class Client
+ attr_accessor :uri
+
+ MANIFEST_VERSION = 'application/vnd.docker.distribution.manifest.v2+json'
+
+ def initialize(base_uri, options = {})
+ @base_uri = base_uri
+ @faraday = Faraday.new(@base_uri) do |conn|
+ initialize_connection(conn, options)
+ end
+ end
+
+ def repository_tags(name)
+ @faraday.get("/v2/#{name}/tags/list").body
+ end
+
+ def repository_manifest(name, reference)
+ @faraday.get("/v2/#{name}/manifests/#{reference}").body
+ end
+
+ def put_repository_manifest(name, reference, manifest)
+ @faraday.put("/v2/#{name}/manifests/#{reference}", manifest, { "Content-Type" => MANIFEST_VERSION }).success?
+ end
+
+ def repository_mount_blob(name, digest, from)
+ @faraday.post("/v2/#{name}/blobs/uploads/?mount=#{digest}&from=#{from}").status == 201
+ end
+
+ def repository_tag_digest(name, reference)
+ response = @faraday.head("/v2/#{name}/manifests/#{reference}")
+ response.headers['docker-content-digest'] if response.success?
+ end
+
+ def delete_repository_tag(name, reference)
+ @faraday.delete("/v2/#{name}/manifests/#{reference}").success?
+ end
+
+ def blob(name, digest, type = nil)
+ headers = {}
+ headers['Accept'] = type if type
+ @faraday.get("/v2/#{name}/blobs/#{digest}", nil, headers).body
+ end
+
+ def delete_blob(name, digest)
+ @faraday.delete("/v2/#{name}/blobs/#{digest}").success?
+ end
+
+ private
+
+ def initialize_connection(conn, options)
+ conn.request :json
+ conn.headers['Accept'] = MANIFEST_VERSION
+
+ conn.response :json, content_type: /\bjson$/
+
+ if options[:user] && options[:password]
+ conn.request(:basic_auth, options[:user].to_s, options[:password].to_s)
+ elsif options[:token]
+ conn.request(:authorization, :bearer, options[:token].to_s)
+ end
+
+ conn.adapter :net_http
+ end
+ end
+end
diff --git a/lib/container_registry/config.rb b/lib/container_registry/config.rb
new file mode 100644
index 00000000000..626b36cbaa9
--- /dev/null
+++ b/lib/container_registry/config.rb
@@ -0,0 +1,15 @@
+module ContainerRegistry
+ class Config
+ attr_reader :tag, :blob, :data
+
+ def initialize(tag, blob)
+ @tag, @blob = tag, blob
+ @data = JSON.parse(blob.data)
+ end
+
+ def [](key)
+ return unless data
+ data[key]
+ end
+ end
+end
diff --git a/lib/container_registry/registry.rb b/lib/container_registry/registry.rb
new file mode 100644
index 00000000000..d3b117eeaca
--- /dev/null
+++ b/lib/container_registry/registry.rb
@@ -0,0 +1,15 @@
+module ContainerRegistry
+ class Registry
+ attr_reader :uri, :client, :path
+
+ def initialize(uri, options = {})
+ @path = options[:path] || uri
+ @uri = URI.parse(uri)
+ @client = ContainerRegistry::Client.new(uri, options)
+ end
+
+ def [](name)
+ ContainerRegistry::Repository.new(self, name)
+ end
+ end
+end
diff --git a/lib/container_registry/repository.rb b/lib/container_registry/repository.rb
new file mode 100644
index 00000000000..b30cb527b60
--- /dev/null
+++ b/lib/container_registry/repository.rb
@@ -0,0 +1,61 @@
+module ContainerRegistry
+ class Repository
+ attr_reader :registry, :name
+
+ def initialize(registry, name)
+ @registry, @name = registry, name
+ end
+
+ def client
+ @client ||= registry.client
+ end
+
+ def path
+ [registry.path, name].compact.join('/')
+ end
+
+ def [](tag)
+ ContainerRegistry::Tag.new(self, tag)
+ end
+
+ def manifest
+ return @manifest if defined?(@manifest)
+ @manifest = client.repository_tags(name)
+ end
+
+ def valid?
+ manifest.present?
+ end
+
+ def tags
+ return @tags if defined?(@tags)
+ return [] unless manifest && manifest['tags']
+
+ @tags = manifest['tags'].map do |tag|
+ ContainerRegistry::Tag.new(self, tag)
+ end
+ end
+
+ def delete_tags
+ return unless tags
+
+ tags.each(:delete)
+ end
+
+ def mount_blob(blob)
+ return unless blob
+
+ client.repository_mount_blob(name, blob.digest, blob.repository.name)
+ end
+
+ def mount_manifest(tag, manifest)
+ client.put_repository_manifest(name, tag, manifest)
+ end
+
+ def copy_to(other_repository)
+ tags.all? do |tag|
+ tag.copy_to(other_repository)
+ end
+ end
+ end
+end
diff --git a/lib/container_registry/tag.rb b/lib/container_registry/tag.rb
new file mode 100644
index 00000000000..14cee8be889
--- /dev/null
+++ b/lib/container_registry/tag.rb
@@ -0,0 +1,79 @@
+module ContainerRegistry
+ class Tag
+ attr_reader :repository, :name
+
+ def initialize(repository, name)
+ @repository, @name = repository, name
+ end
+
+ def valid?
+ manifest.present?
+ end
+
+ def manifest
+ return @manifest if defined?(@manifest)
+ @manifest = client.repository_manifest(repository.name, name)
+ end
+
+ def path
+ "#{repository.path}:#{name}"
+ end
+
+ def [](key)
+ return unless manifest
+ manifest[key]
+ end
+
+ def digest
+ return @digest if defined?(@digest)
+ @digest = client.repository_tag_digest(repository.name, name)
+ end
+
+ def config_blob
+ return @config_blob if defined?(@config_blob)
+ return unless manifest && manifest['config']
+ @config_blob = ContainerRegistry::Blob.new(repository, manifest['config'])
+ end
+
+ def config
+ return unless config_blob
+ @config ||= ContainerRegistry::Config.new(self, config_blob)
+ end
+
+ def created_at
+ return unless config
+ @created_at ||= DateTime.rfc3339(config['created'])
+ end
+
+ def layers
+ return @layers if defined?(@layers)
+ return unless manifest
+ @layers = manifest['layers'].map do |layer|
+ ContainerRegistry::Blob.new(repository, layer)
+ end
+ end
+
+ def total_size
+ return unless layers
+ layers.map(&:size).sum
+ end
+
+ def delete
+ return unless digest
+ client.delete_repository_tag(repository.name, digest)
+ end
+
+ def copy_to(repository)
+ return unless manifest
+ layers.each do |blob|
+ repository.mount_blob(blob)
+ end
+ repository.mount_blob(config_blob)
+ repository.mount_manifest(name, manifest.to_json)
+ end
+
+ def client
+ @client ||= repository.client
+ end
+ end
+end
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index ace906a6f59..1cbd6d945a0 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -96,5 +96,9 @@ module Gitlab
(?<![\/.]) (?# rule #6-7)
}x.freeze
end
+
+ def container_registry_reference_regex
+ git_reference_regex
+ end
end
end