Class: ContainerRepository
Constant Summary
collapse
- WAITING_CLEANUP_STATUSES =
%i[cleanup_scheduled cleanup_unfinished].freeze
- REQUIRING_CLEANUP_STATUSES =
%i[cleanup_unscheduled cleanup_scheduled].freeze
- MAX_TAGS_PAGES =
2000
- MAX_DELETION_FAILURES =
10
- AUTH_TOKEN_USAGE_RESERVED_TIME_IN_SECS =
The Registry client uses JWT token to authenticate to Registry. We cache the client using expiration time of JWT token. However it’s possible that the token is valid but by the time the request is made to Regsitry, it’s already expired. To prevent this case, we are subtracting a few seconds, defined by this constant from the cache expiration time.
5
Gitlab::SQL::Pattern::MIN_CHARS_FOR_PARTIAL_MATCHING, Gitlab::SQL::Pattern::REGEX_QUOTED_TERM
ApplicationRecord::MAX_PLUCK
HasCheckConstraints::NOT_NULL_CHECK_PATTERN
ResetOnColumnErrors::MAX_RESET_PERIOD
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
#run_after_commit, #run_after_commit_or_now
split_query_to_search_terms
===, cached_column_list, #create_or_load_association, declarative_enum, default_select_columns, id_in, id_not_in, iid_in, nullable_column?, pluck_primary_key, primary_key_in, #readable_by?, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, #to_ability_name, underscore, where_exists, where_not_exists, with_fast_read_statement_timeout, without_order
#reset_on_union_error, #reset_on_unknown_attribute_error
#serializable_hash
Instance Attribute Details
#path ⇒ Object
rubocop: enable CodeReuse/ServiceClass
106
107
108
109
|
# File 'app/models/container_repository.rb', line 106
def path
@path ||= [project.full_path, name]
.select(&:present?).join('/').downcase
end
|
Class Method Details
.build_from_path(path) ⇒ Object
287
288
289
|
# File 'app/models/container_repository.rb', line 287
def self.build_from_path(path)
self.new(project: path.repository_project, name: path.repository_name)
end
|
.build_root_repository(project) ⇒ Object
300
301
302
|
# File 'app/models/container_repository.rb', line 300
def self.build_root_repository(project)
self.new(project: project, name: '')
end
|
.exists_by_path?(path) ⇒ Boolean
61
62
63
64
65
66
|
# File 'app/models/container_repository.rb', line 61
def self.exists_by_path?(path)
where(
project: path.repository_project,
name: path.repository_name
).exists?
end
|
.find_by_path(path) ⇒ Object
308
309
310
|
# File 'app/models/container_repository.rb', line 308
def self.find_by_path(path)
self.find_by(project: path.repository_project, name: path.repository_name)
end
|
.find_by_path!(path) ⇒ Object
304
305
306
|
# File 'app/models/container_repository.rb', line 304
def self.find_by_path!(path)
self.find_by!(project: path.repository_project, name: path.repository_name)
end
|
.find_or_create_from_path!(path) ⇒ Object
291
292
293
294
295
296
297
298
|
# File 'app/models/container_repository.rb', line 291
def self.find_or_create_from_path!(path)
ContainerRepository.upsert({
project_id: path.repository_project.id,
name: path.repository_name
}, unique_by: %i[project_id name])
find_by_path!(path)
end
|
.pending_destruction ⇒ Object
needed by Packages::Destructible
89
90
91
|
# File 'app/models/container_repository.rb', line 89
def self.pending_destruction
delete_scheduled.where('next_delete_attempt_at IS NULL OR next_delete_attempt_at < ?', Time.zone.now)
end
|
.registry_client_expiration_time ⇒ Object
.requiring_cleanup ⇒ Object
73
74
75
76
77
78
|
# File 'app/models/container_repository.rb', line 73
def self.requiring_cleanup
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)
end
|
.with_enabled_policy ⇒ Object
68
69
70
71
|
# File 'app/models/container_repository.rb', line 68
def self.with_enabled_policy
joins('INNER JOIN container_expiration_policies ON container_repositories.project_id = container_expiration_policies.project_id')
.where(container_expiration_policies: { enabled: true })
end
|
.with_unfinished_cleanup ⇒ Object
80
81
82
|
# File 'app/models/container_repository.rb', line 80
def self.with_unfinished_cleanup
with_enabled_policy.cleanup_unfinished
end
|
Instance Method Details
#blob(config) ⇒ Object
208
209
210
|
# File 'app/models/container_repository.rb', line 208
def blob(config)
ContainerRegistry::Blob.new(self, config)
end
|
#delete_tag(name_or_digest) ⇒ Object
228
229
230
|
# File 'app/models/container_repository.rb', line 228
def delete_tag(name_or_digest)
client.delete_repository_tag_by_digest(self.path, name_or_digest)
end
|
220
221
222
223
224
225
226
|
# File 'app/models/container_repository.rb', line 220
def delete_tags!
return unless has_tags?
digests = tags.map { |tag| tag.digest }.compact.to_set
digests.map { |digest| delete_tag(digest) }.all?
end
|
#each_tags_page(page_size: 100, &block) ⇒ Object
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
# File 'app/models/container_repository.rb', line 156
def each_tags_page(page_size: 100, &block)
raise ArgumentError, _('GitLab container registry API not supported') unless gitlab_api_client.supports_gitlab_api?
raise ArgumentError, 'block not given' unless block
next_page_uri = URI('')
page_count = 0
while next_page_uri && page_count < MAX_TAGS_PAGES
last = Rack::Utils.parse_nested_query(next_page_uri.query)['last']
current_page = gitlab_api_client.tags(self.path, page_size: page_size, last: last)
if current_page&.key?(:response_body)
yield transform_tags_page(current_page[:response_body])
next_page_uri = current_page.dig(:pagination, :next, :uri)
else
next_page_uri = nil
end
page_count += 1
end
raise 'too many pages requested' if page_count >= MAX_TAGS_PAGES
end
|
212
213
214
|
# File 'app/models/container_repository.rb', line 212
def has_tags?
tags.any?
end
|
#image_manifest(reference) ⇒ Object
129
130
131
|
# File 'app/models/container_repository.rb', line 129
def image_manifest(reference)
client.repository_manifest(path, reference)
end
|
#last_published_at ⇒ Object
248
249
250
251
252
253
254
255
|
# File 'app/models/container_repository.rb', line 248
def last_published_at
return unless gitlab_api_client.supports_gitlab_api?
timestamp_string = gitlab_api_client_repository_details['last_published_at']
DateTime.iso8601(timestamp_string)
rescue ArgumentError
nil
end
|
#location ⇒ Object
111
112
113
|
# File 'app/models/container_repository.rb', line 111
def location
File.join(registry.path, path)
end
|
#manifest ⇒ Object
133
134
135
|
# File 'app/models/container_repository.rb', line 133
def manifest
@manifest ||= client.repository_tags(path)
end
|
#registry ⇒ Object
rubocop: disable CodeReuse/ServiceClass
#root_repository? ⇒ Boolean
216
217
218
|
# File 'app/models/container_repository.rb', line 216
def root_repository?
name.empty?
end
|
#set_delete_failed_status ⇒ Object
279
280
281
282
283
284
285
|
# File 'app/models/container_repository.rb', line 279
def set_delete_failed_status
update_columns(
status: :delete_failed,
delete_started_at: nil,
status_updated_at: Time.zone.now
)
end
|
#set_delete_ongoing_status ⇒ Object
258
259
260
261
262
263
264
265
266
267
|
# File 'app/models/container_repository.rb', line 258
def set_delete_ongoing_status
now = Time.zone.now
update_columns(
status: :delete_ongoing,
delete_started_at: now,
status_updated_at: now,
next_delete_attempt_at: nil
)
end
|
#set_delete_scheduled_status ⇒ Object
269
270
271
272
273
274
275
276
277
|
# File 'app/models/container_repository.rb', line 269
def set_delete_scheduled_status
update_columns(
status: :delete_scheduled,
delete_started_at: nil,
status_updated_at: Time.zone.now,
failed_deletion_count: failed_deletion_count + 1,
next_delete_attempt_at: next_delete_attempt_with_delay
)
end
|
#size ⇒ Object
240
241
242
243
244
245
246
|
# File 'app/models/container_repository.rb', line 240
def size
strong_memoize(:size) do
next unless gitlab_api_client.supports_gitlab_api?
gitlab_api_client_repository_details['size_bytes']
end
end
|
#start_expiration_policy! ⇒ Object
232
233
234
235
236
237
238
|
# File 'app/models/container_repository.rb', line 232
def start_expiration_policy!
update!(
expiration_policy_started_at: Time.zone.now,
last_cleanup_deleted_tags_count: nil,
expiration_policy_cleanup_status: :cleanup_ongoing
)
end
|
#tag(tag) ⇒ Object
If the container registry GitLab API is available, the API does a search of tags containing the name and we filter them to find the exact match. Otherwise, we instantiate a tag.
118
119
120
121
122
123
124
125
126
127
|
# File 'app/models/container_repository.rb', line 118
def tag(tag)
if gitlab_api_client.supports_gitlab_api?
page = tags_page(name: tag)
return if page[:tags].blank?
page[:tags].find { |result_tag| result_tag.name == tag }
else
ContainerRegistry::Tag.new(self, tag)
end
end
|
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
# File 'app/models/container_repository.rb', line 137
def tags
strong_memoize(:tags) do
if gitlab_api_client.supports_gitlab_api?
result = []
each_tags_page do |array_of_tags|
result << array_of_tags
end
result.flatten
else
next [] unless manifest && manifest['tags']
manifest['tags'].sort.map do |tag|
ContainerRegistry::Tag.new(self, tag)
end
end
end
end
|
202
203
204
205
206
|
# File 'app/models/container_repository.rb', line 202
def tags_count
return 0 unless manifest && manifest['tags']
manifest['tags'].size
end
|
#tags_page(before: nil, last: nil, sort: nil, name: nil, page_size: 100, referrers: nil, referrer_type: nil) ⇒ Object
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
|
# File 'app/models/container_repository.rb', line 182
def tags_page(before: nil, last: nil, sort: nil, name: nil, page_size: 100, referrers: nil, referrer_type: nil)
raise ArgumentError, _('GitLab container registry API not supported') unless gitlab_api_client.supports_gitlab_api?
page = gitlab_api_client.tags(
self.path,
page_size: page_size,
before: before,
last: last,
sort: sort,
name: name,
referrers: referrers,
referrer_type: referrer_type
)
{
tags: transform_tags_page(page[:response_body]),
pagination: page[:pagination]
}
end
|