First iteration of container_image view
- Fixes project, container_image and tag deletion - Removed container_images_repository [ci skip]
This commit is contained in:
parent
dcd4beb8eb
commit
eed0b85ad0
17 changed files with 149 additions and 169 deletions
16
app/assets/stylesheets/pages/container_registry.scss
Normal file
16
app/assets/stylesheets/pages/container_registry.scss
Normal file
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Container Registry
|
||||
*/
|
||||
|
||||
.container-image {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.container-image-head {
|
||||
padding: 0px 16px;
|
||||
line-height: 4;
|
||||
}
|
||||
|
||||
.table.tags {
|
||||
margin-bottom: 0px;
|
||||
}
|
|
@ -5,17 +5,22 @@ 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
|
||||
redirect_to url
|
||||
if tag
|
||||
delete_tag(url)
|
||||
else
|
||||
redirect_to url, alert: 'Failed to remove tag'
|
||||
if image.destroy
|
||||
redirect_to url
|
||||
else
|
||||
redirect_to url, alert: 'Failed to remove image'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -24,11 +29,20 @@ class Projects::ContainerRegistryController < Projects::ApplicationController
|
|||
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
|
||||
|
|
|
@ -1,23 +1,28 @@
|
|||
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)
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
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')
|
||||
|
|
32
app/services/container_images/destroy_service.rb
Normal file
32
app/services/container_images/destroy_service.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
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
|
|
@ -1,16 +0,0 @@
|
|||
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
|
|
@ -1,11 +0,0 @@
|
|||
module ContainerImagesRepositories
|
||||
module ContainerImages
|
||||
class DestroyService < BaseService
|
||||
def execute(container_image)
|
||||
return false unless container_image
|
||||
|
||||
container_image.destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,26 +0,0 @@
|
|||
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
|
|
@ -1,7 +0,0 @@
|
|||
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
|
||||
|
|
35
app/views/projects/container_registry/_image.html.haml
Normal file
35
app/views/projects/container_registry/_image.html.haml
Normal file
|
@ -0,0 +1,35 @@
|
|||
- 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
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
# 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
|
||||
|
|
Loading…
Reference in a new issue