2018-07-25 09:30:33 +00:00
# frozen_string_literal: true
2019-03-28 13:17:42 +00:00
class ContainerRepository < ApplicationRecord
2019-01-10 14:22:58 +00:00
include Gitlab :: Utils :: StrongMemoize
2020-04-28 00:09:33 +00:00
include Gitlab :: SQL :: Pattern
2020-10-26 18:08:27 +00:00
include EachBatch
2021-02-09 18:09:59 +00:00
include Sortable
2020-10-26 18:08:27 +00:00
WAITING_CLEANUP_STATUSES = % i [ cleanup_scheduled cleanup_unfinished ] . freeze
2021-05-12 12:10:24 +00:00
REQUIRING_CLEANUP_STATUSES = % i [ cleanup_unscheduled cleanup_scheduled ] . freeze
2019-01-10 14:22:58 +00:00
2016-11-23 16:50:30 +00:00
belongs_to :project
2017-03-24 11:31:34 +00:00
validates :name , length : { minimum : 0 , allow_nil : false }
2017-04-03 09:51:13 +00:00
validates :name , uniqueness : { scope : :project_id }
2017-03-24 11:31:34 +00:00
2020-04-07 12:09:34 +00:00
enum status : { delete_scheduled : 0 , delete_failed : 1 }
2020-10-26 18:08:27 +00:00
enum expiration_policy_cleanup_status : { cleanup_unscheduled : 0 , cleanup_scheduled : 1 , cleanup_unfinished : 2 , cleanup_ongoing : 3 }
2020-04-07 12:09:34 +00:00
2017-03-24 11:31:34 +00:00
delegate :client , to : :registry
2017-03-31 13:10:15 +00:00
2019-01-10 14:22:58 +00:00
scope :ordered , - > { order ( :name ) }
2019-10-25 00:06:14 +00:00
scope :with_api_entity_associations , - > { preload ( project : [ :route , { namespace : :route } ] ) }
2019-11-04 15:07:36 +00:00
scope :for_group_and_its_subgroups , - > ( group ) do
2020-06-11 12:08:54 +00:00
project_scope = Project
. for_group_and_its_subgroups ( group )
2021-07-09 09:09:53 +00:00
. with_feature_enabled ( :container_registry )
. select ( :id )
2020-06-11 12:08:54 +00:00
2020-11-24 15:09:13 +00:00
joins ( " INNER JOIN ( #{ project_scope . to_sql } ) projects on projects.id=container_repositories.project_id " )
2019-11-04 15:07:36 +00:00
end
2020-10-26 18:08:27 +00:00
scope :for_project_id , - > ( project_id ) { where ( project_id : project_id ) }
2020-04-28 00:09:33 +00:00
scope :search_by_name , - > ( query ) { fuzzy_search ( query , [ :name ] , use_minimum_char_limit : false ) }
2020-10-26 18:08:27 +00:00
scope :waiting_for_cleanup , - > { where ( expiration_policy_cleanup_status : WAITING_CLEANUP_STATUSES ) }
2021-05-12 12:10:24 +00:00
scope :expiration_policy_started_at_nil_or_before , - > ( timestamp ) { where ( 'expiration_policy_started_at < ? OR expiration_policy_started_at IS NULL' , timestamp ) }
2021-05-20 15:10:13 +00:00
scope :with_stale_ongoing_cleanup , - > ( threshold ) { cleanup_ongoing . where ( 'expiration_policy_started_at < ?' , threshold ) }
2019-01-10 14:22:58 +00:00
2020-03-30 09:07:58 +00:00
def self . exists_by_path? ( path )
where (
project : path . repository_project ,
name : path . repository_name
) . exists?
end
2021-05-12 12:10:24 +00:00
def self . with_enabled_policy
2021-06-02 06:09:48 +00:00
joins ( 'INNER JOIN container_expiration_policies ON container_repositories.project_id = container_expiration_policies.project_id' )
2021-05-12 12:10:24 +00:00
. where ( container_expiration_policies : { enabled : true } )
end
def self . requiring_cleanup
2021-06-02 06:09:48 +00:00
with_enabled_policy
. where ( container_repositories : { expiration_policy_cleanup_status : REQUIRING_CLEANUP_STATUSES } )
. where ( 'container_repositories.expiration_policy_started_at IS NULL OR container_repositories.expiration_policy_started_at < container_expiration_policies.next_run_at' )
. where ( 'container_expiration_policies.next_run_at < ?' , Time . zone . now )
2021-05-12 12:10:24 +00:00
end
def self . with_unfinished_cleanup
with_enabled_policy . cleanup_unfinished
end
2018-08-27 15:31:01 +00:00
# rubocop: disable CodeReuse/ServiceClass
2017-03-22 11:28:23 +00:00
def registry
2017-03-23 13:37:17 +00:00
@registry || = begin
token = Auth :: ContainerRegistryAuthenticationService . full_access_token ( path )
url = Gitlab . config . registry . api_url
host_port = Gitlab . config . registry . host_port
ContainerRegistry :: Registry . new ( url , token : token , path : host_port )
end
end
2018-08-27 15:31:01 +00:00
# rubocop: enable CodeReuse/ServiceClass
2017-03-23 13:37:17 +00:00
def path
2017-04-13 09:54:02 +00:00
@path || = [ project . full_path , name ]
. select ( & :present? ) . join ( '/' ) . downcase
2016-11-02 02:33:35 +00:00
end
2017-04-10 10:57:19 +00:00
def location
File . join ( registry . path , path )
end
2016-11-02 02:33:35 +00:00
def tag ( tag )
ContainerRegistry :: Tag . new ( self , tag )
end
def manifest
2017-04-05 12:44:35 +00:00
@manifest || = client . repository_tags ( path )
2016-11-02 02:33:35 +00:00
end
def tags
return [ ] unless manifest && manifest [ 'tags' ]
2019-01-10 14:22:58 +00:00
strong_memoize ( :tags ) do
manifest [ 'tags' ] . sort . map do | tag |
ContainerRegistry :: Tag . new ( self , tag )
end
2016-11-02 02:33:35 +00:00
end
end
2020-05-25 21:08:00 +00:00
def tags_count
return 0 unless manifest && manifest [ 'tags' ]
manifest [ 'tags' ] . size
end
2016-11-02 02:33:35 +00:00
def blob ( config )
ContainerRegistry :: Blob . new ( self , config )
end
2017-03-29 10:14:29 +00:00
def has_tags?
2017-04-05 12:44:35 +00:00
tags . any?
2017-03-29 10:14:29 +00:00
end
2017-04-03 10:49:54 +00:00
def root_repository?
name . empty?
end
2017-03-31 09:54:09 +00:00
def delete_tags!
return unless has_tags?
2019-10-16 12:06:32 +00:00
digests = tags . map { | tag | tag . digest } . compact . to_set
2016-11-02 02:33:35 +00:00
2019-10-16 15:06:17 +00:00
digests . map ( & method ( :delete_tag_by_digest ) ) . all?
2016-11-02 02:33:35 +00:00
end
2017-03-30 11:31:33 +00:00
2019-08-01 12:03:08 +00:00
def delete_tag_by_digest ( digest )
2020-02-12 15:09:37 +00:00
client . delete_repository_tag_by_digest ( self . path , digest )
end
def delete_tag_by_name ( name )
client . delete_repository_tag_by_name ( self . path , name )
2019-08-01 12:03:08 +00:00
end
2020-09-23 00:09:45 +00:00
def reset_expiration_policy_started_at!
update! ( expiration_policy_started_at : nil )
end
def start_expiration_policy!
update! ( expiration_policy_started_at : Time . zone . now )
end
2017-04-03 10:49:54 +00:00
def self . build_from_path ( path )
self . new ( project : path . repository_project ,
name : path . repository_name )
end
2017-03-31 10:27:05 +00:00
def self . create_from_path! ( path )
2021-07-09 06:08:14 +00:00
safe_find_or_create_by! ( project : path . repository_project ,
name : path . repository_name )
2017-03-30 11:31:33 +00:00
end
2017-04-04 10:57:38 +00:00
def self . build_root_repository ( project )
self . new ( project : project , name : '' )
end
2019-07-09 15:59:52 +00:00
def self . find_by_path! ( path )
self . find_by! ( project : path . repository_project ,
name : path . repository_name )
end
2016-11-02 02:33:35 +00:00
end
2019-09-13 13:26:31 +00:00
2021-05-11 21:10:21 +00:00
ContainerRepository . prepend_mod_with ( 'ContainerRepository' )