Commit eed0b85a authored by Andre Guedes's avatar Andre Guedes

First iteration of container_image view

  - Fixes project, container_image and tag deletion
  - Removed container_images_repository

[ci skip]
parent dcd4beb8
/**
* Container Registry
*/
.container-image {
border-bottom: 1px solid #f0f0f0;
}
.container-image-head {
padding: 0px 16px;
line-height: 4;
}
.table.tags {
margin-bottom: 0px;
}
......@@ -5,30 +5,44 @@ class Projects::ContainerRegistryController < Projects::ApplicationController
layout 'project'
def index
@tags = container_registry_repository.tags
@images = project.container_images
end
def destroy
url = namespace_project_container_registry_index_path(project.namespace, project)
if tag.delete
if tag
delete_tag(url)
else
if image.destroy
redirect_to url
else
redirect_to url, alert: 'Failed to remove tag'
redirect_to url, alert: 'Failed to remove image'
end
end
end
private
def verify_registry_enabled
render_404 unless Gitlab.config.registry.enabled
end
def container_registry_repository
@container_registry_repository ||= project.container_registry_repository
def delete_tag(url)
if tag.delete
image.destroy if image.tags.empty?
redirect_to url
else
redirect_to url, alert: 'Failed to remove tag'
end
end
def image
@image ||= project.container_images.find_by(id: params[:id])
end
def tag
@tag ||= container_registry_repository.tag(params[:id])
@tag ||= image.tag(params[:tag]) if params[:tag].present?
end
end
class ContainerImage < ActiveRecord::Base
belongs_to :container_images_repository
belongs_to :project
delegate :registry, :registry_path_with_namespace, :client, to: :container_images_repository
delegate :container_registry, :container_registry_allowed_paths,
:container_registry_path_with_namespace, to: :project
delegate :client, to: :container_registry
validates :manifest, presence: true
before_destroy :delete_tags
before_validation :update_token, on: :create
def update_token
paths = container_images_repository.allowed_paths << name_with_namespace
paths = container_registry_allowed_paths << name_with_namespace
token = Auth::ContainerRegistryAuthenticationService.full_access_token(paths)
client.update_token(token)
end
def path
[registry.path, name_with_namespace].compact.join('/')
[container_registry.path, name_with_namespace].compact.join('/')
end
def name_with_namespace
[registry_path_with_namespace, name].compact.join('/')
[container_registry_path_with_namespace, name].compact.join('/')
end
def tag(tag)
......@@ -44,7 +49,10 @@ class ContainerImage < ActiveRecord::Base
def delete_tags
return unless tags
tags.all?(&:delete)
digests = tags.map {|tag| tag.digest }.to_set
digests.all? do |digest|
client.delete_repository_tag(name_with_namespace, digest)
end
end
def self.split_namespace(full_path)
......
class ContainerImagesRepository < ActiveRecord::Base
belongs_to :project
has_many :container_images, dependent: :destroy
delegate :client, to: :registry
def registry_path_with_namespace
project.path_with_namespace.downcase
end
def allowed_paths
@allowed_paths ||= [registry_path_with_namespace] +
container_images.map { |i| i.name_with_namespace }
end
def registry
@registry ||= begin
token = Auth::ContainerRegistryAuthenticationService.full_access_token(allowed_paths)
url = Gitlab.config.registry.api_url
host_port = Gitlab.config.registry.host_port
ContainerRegistry::Registry.new(url, token: token, path: host_port)
end
end
end
......@@ -157,7 +157,7 @@ class Project < ActiveRecord::Base
has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
has_one :project_feature, dependent: :destroy
has_one :statistics, class_name: 'ProjectStatistics', dependent: :delete
has_one :container_images_repository, dependent: :destroy
has_many :container_images, dependent: :destroy
has_many :commit_statuses, dependent: :destroy, foreign_key: :gl_project_id
has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline', foreign_key: :gl_project_id
......@@ -405,15 +405,19 @@ class Project < ActiveRecord::Base
path_with_namespace.downcase
end
def container_registry_repository
def container_registry_allowed_paths
@container_registry_allowed_paths ||= [container_registry_path_with_namespace] +
container_images.map { |i| i.name_with_namespace }
end
def container_registry
return unless Gitlab.config.registry.enabled
@container_registry_repository ||= begin
token = Auth::ContainerRegistryAuthenticationService.full_access_token(container_registry_path_with_namespace)
@container_registry ||= begin
token = Auth::ContainerRegistryAuthenticationService.full_access_token(container_registry_allowed_paths)
url = Gitlab.config.registry.api_url
host_port = Gitlab.config.registry.host_port
registry = ContainerRegistry::Registry.new(url, token: token, path: host_port)
registry.repository(container_registry_path_with_namespace)
ContainerRegistry::Registry.new(url, token: token, path: host_port)
end
end
......@@ -424,9 +428,9 @@ class Project < ActiveRecord::Base
end
def has_container_registry_tags?
return unless container_registry_repository
return unless container_images
container_registry_repository.tags.any?
container_images.first.tags.any?
end
def commit(ref = 'HEAD')
......
module ContainerImages
class DestroyService < BaseService
class DestroyError < StandardError; end
def execute(container_image)
@container_image = container_image
return false unless can?(current_user, :remove_project, project)
ContainerImage.transaction do
container_image.destroy!
unless remove_container_image_tags
raise_error('Failed to remove container image tags. Please try again or contact administrator')
end
end
true
end
private
def raise_error(message)
raise DestroyError.new(message)
end
def remove_container_image_tags
container_image.delete_tags
end
end
end
module ContainerImagesRepositories
module ContainerImages
class CreateService < BaseService
def execute
@container_image = container_images_repository.container_images.create(params)
@container_image if @container_image.valid?
end
private
def container_images_repository
@container_images_repository ||= project.container_images_repository
end
end
end
end
module ContainerImagesRepositories
module ContainerImages
class DestroyService < BaseService
def execute(container_image)
return false unless container_image
container_image.destroy
end
end
end
end
module ContainerImagesRepositories
module ContainerImages
class PushService < BaseService
def execute(container_image_name, event)
find_or_create_container_image(container_image_name).valid?
end
private
def find_or_create_container_image(container_image_name)
options = {name: container_image_name}
container_images.find_by(options) ||
::ContainerImagesRepositories::ContainerImages::CreateService.new(project,
current_user, options).execute
end
def container_images_repository
@container_images_repository ||= project.container_images_repository
end
def container_images
@container_images ||= container_images_repository.container_images
end
end
end
end
module ContainerImagesRepositories
class CreateService < BaseService
def execute
project.container_images_repository || ::ContainerImagesRepository.create(project: project)
end
end
end
......@@ -31,10 +31,6 @@ module Projects
project.team.truncate
project.destroy!
unless remove_registry_tags
raise_error('Failed to remove project container registry. Please try again or contact administrator')
end
unless remove_repository(repo_path)
raise_error('Failed to remove project repository. Please try again or contact administrator')
end
......@@ -68,12 +64,6 @@ module Projects
end
end
def remove_registry_tags
return true unless Gitlab.config.registry.enabled
project.container_registry_repository.delete_tags
end
def raise_error(message)
raise DestroyError.new(message)
end
......
- expanded = false
.container-image.js-toggle-container
.container-image-head
= link_to "#", class: "js-toggle-button" do
- if expanded
= icon("chevron-up")
- else
= icon("chevron-down")
= escape_once(image.name)
= clipboard_button(clipboard_text: "docker pull #{image.path}")
.controls.hidden-xs.pull-right
= link_to namespace_project_container_registry_path(@project.namespace, @project, image.id), class: 'btn btn-remove has-tooltip', title: "Remove", data: { confirm: "Are you sure?" }, method: :delete do
= icon("trash cred")
.container-image-tags.js-toggle-content{ class: ("hide" unless expanded) }
- if image.tags.blank?
%li
.nothing-here-block No tags in Container Registry for this container image.
- else
.table-holder
%table.table.tags
%thead
%tr
%th Name
%th Image ID
%th Size
%th Created
- if can?(current_user, :update_container_image, @project)
%th
- image.tags.each do |tag|
= render 'tag', tag: tag
......@@ -25,5 +25,5 @@
- if can?(current_user, :update_container_image, @project)
%td.content
.controls.hidden-xs.pull-right
= link_to namespace_project_container_registry_path(@project.namespace, @project, tag.name), class: 'btn btn-remove has-tooltip', title: "Remove", data: { confirm: "Are you sure?" }, method: :delete do
= link_to namespace_project_container_registry_path(@project.namespace, @project, { id: tag.repository.id, tag: tag.name} ), class: 'btn btn-remove has-tooltip', title: "Remove", data: { confirm: "Are you sure?" }, method: :delete do
= icon("trash cred")
......@@ -19,21 +19,9 @@
%br
docker push #{escape_once(@project.container_registry_repository_url)}
- if @tags.blank?
%li
.nothing-here-block No images in Container Registry for this project.
- if @images.blank?
.nothing-here-block No container images in Container Registry for this project.
- else
.table-holder
%table.table.tags
%thead
%tr
%th Name
%th Image ID
%th Size
%th Created
- if can?(current_user, :update_container_image, @project)
%th
- @tags.each do |tag|
= render 'tag', tag: tag
- @images.each do |image|
= render 'image', image: image
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class CreateContainerImagesRepository < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
# DOWNTIME_REASON = ''
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def change
create_table :container_images_repositories do |t|
t.integer :project_id, null: false
end
end
end
......@@ -25,7 +25,7 @@ class CreateContainerImage < ActiveRecord::Migration
def change
create_table :container_images do |t|
t.integer :container_images_repository_id
t.integer :project_id
t.string :name
end
end
......
......@@ -41,9 +41,19 @@ module API
if event['action'] == 'push' and !!event['target']['tag']
namespace, container_image_name = ContainerImage::split_namespace(repository)
::ContainerImagesRepositories::ContainerImages::PushService.new(
Project::find_with_namespace(namespace), current_user
).execute(container_image_name, event)
project = Project::find_with_namespace(namespace)
if project
container_image = project.container_images.find_or_create_by(name: container_image_name)
if container_image.valid?
puts('Valid!')
else
render_api_error!({ error: "Failed to create container image!" }, 400)
end
else
not_found!('Project')
end
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment