1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
# frozen_string_literal: true
require 'faraday'
require 'faraday_middleware'
require 'digest'
module ContainerRegistry
class BaseClient
DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE = 'application/vnd.docker.distribution.manifest.v2+json'
DOCKER_DISTRIBUTION_MANIFEST_LIST_V2_TYPE = 'application/vnd.docker.distribution.manifest.list.v2+json'
OCI_MANIFEST_V1_TYPE = 'application/vnd.oci.image.manifest.v1+json'
CONTAINER_IMAGE_V1_TYPE = 'application/vnd.docker.container.image.v1+json'
ACCEPTED_TYPES = [DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE, OCI_MANIFEST_V1_TYPE].freeze
ACCEPTED_TYPES_RAW = [DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE, OCI_MANIFEST_V1_TYPE, DOCKER_DISTRIBUTION_MANIFEST_LIST_V2_TYPE].freeze
RETRY_EXCEPTIONS = [Faraday::Request::Retry::DEFAULT_EXCEPTIONS, Faraday::ConnectionFailed].flatten.freeze
RETRY_OPTIONS = {
max: 1,
interval: 5,
exceptions: RETRY_EXCEPTIONS
}.freeze
ERROR_CALLBACK_OPTIONS = {
callback: -> (env, exception) do
Gitlab::ErrorTracking.log_exception(
exception,
class: name,
url: env[:url]
)
end
}.freeze
# Taken from: FaradayMiddleware::FollowRedirects
REDIRECT_CODES = Set.new [301, 302, 303, 307]
class << self
private
def with_dummy_client(return_value_if_disabled: nil)
registry_config = Gitlab.config.registry
unless registry_config.enabled && registry_config.api_url.present?
return return_value_if_disabled
end
token = Auth::ContainerRegistryAuthenticationService.access_token([], [])
yield new(registry_config.api_url, token: token)
end
end
def initialize(base_uri, options = {})
@base_uri = base_uri
@options = options
end
private
def faraday(timeout_enabled: true)
@faraday ||= faraday_base(timeout_enabled: timeout_enabled) do |conn|
initialize_connection(conn, @options, &method(:configure_connection))
end
end
def faraday_base(timeout_enabled: true, &block)
request_options = timeout_enabled ? Gitlab::HTTP::DEFAULT_TIMEOUT_OPTIONS : nil
Faraday.new(
@base_uri,
headers: { user_agent: "GitLab/#{Gitlab::VERSION}" },
request: request_options,
&block
)
end
def initialize_connection(conn, options)
conn.request :json
if options[:user] && options[:password]
conn.request(:basic_auth, options[:user].to_s, options[:password].to_s)
elsif options[:token]
conn.request(:authorization, :bearer, options[:token].to_s)
end
yield(conn) if block_given?
conn.request(:retry, RETRY_OPTIONS)
conn.request(:gitlab_error_callback, ERROR_CALLBACK_OPTIONS)
conn.adapter :net_http
end
def response_body(response, allow_redirect: false)
if allow_redirect && REDIRECT_CODES.include?(response.status)
response = redirect_response(response.headers['location'])
end
response.body if response && response.success?
end
def redirect_response(location)
return unless location
uri = URI(@base_uri).merge(location)
raise ArgumentError, "Invalid scheme for #{location}" unless %w[http https].include?(uri.scheme)
faraday_redirect.get(uri)
end
def configure_connection(conn)
conn.headers['Accept'] = ACCEPTED_TYPES
conn.response :json, content_type: 'application/json'
conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v1+prettyjws'
conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v1+json'
conn.response :json, content_type: DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE
conn.response :json, content_type: OCI_MANIFEST_V1_TYPE
end
# Create a new request to make sure the Authorization header is not inserted
# via the Faraday middleware
def faraday_redirect
@faraday_redirect ||= faraday_base do |conn|
conn.request :json
conn.request(:retry, RETRY_OPTIONS)
conn.request(:gitlab_error_callback, ERROR_CALLBACK_OPTIONS)
conn.adapter :net_http
end
end
def delete_if_exists(path)
result = faraday.delete(path)
result.success? || result.status == 404
end
end
end
|