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