diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/container_registry/blob.rb | 54 | ||||
-rw-r--r-- | lib/container_registry/client.rb | 69 | ||||
-rw-r--r-- | lib/container_registry/config.rb | 15 | ||||
-rw-r--r-- | lib/container_registry/registry.rb | 15 | ||||
-rw-r--r-- | lib/container_registry/repository.rb | 61 | ||||
-rw-r--r-- | lib/container_registry/tag.rb | 79 | ||||
-rw-r--r-- | lib/gitlab/regex.rb | 4 |
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 |