-
Notifications
You must be signed in to change notification settings - Fork 289
/
cdn.rb
191 lines (170 loc) · 7.13 KB
/
cdn.rb
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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
181
182
183
184
185
186
187
188
189
190
191
module Katello
module Resources
module CDN
class Utils
# takes releasever from contentUrl (e.g. 6Server, 6.0, 6.1)
# returns hash e.g. {:major => 6, :minor => "6.1"}
# used to be able to make hierarchial view for RH repos
def self.parse_version(releasever)
if releasever.to_s =~ /^\d/
{:major => releasever[/^\d+/].to_i, :minor => releasever }
else
{}
end
end
end
class CdnResource
CDN_DOCKER_CONTAINER_LISTING = "CONTAINER_REGISTRY_LISTING".freeze
attr_reader :url, :product, :options
def substitutor(logger = nil)
@logger = logger
Util::CdnVarSubstitutor.new(self)
end
def initialize(url, options = {})
options.reverse_merge!(:verify_ssl => 9)
options.assert_valid_keys(:ssl_client_key, :ssl_client_cert, :ssl_ca_file, :verify_ssl,
:product)
ca = Katello::Repository.feed_ca_file(url)
if ca && URI(url).scheme == 'https'
options.reverse_merge!(:ssl_ca_file => ca)
elsif URI(url).scheme == 'https'
store = OpenSSL::X509::Store.new
store.set_default_paths
options.reverse_merge!(:cert_store => store)
end
@product = options[:product]
@url = url
@uri = URI.parse(url)
@options = options
end
def self.redhat_cdn?(url)
url.include?(SETTINGS[:katello][:redhat_repository_url])
end
def http_downloader
net = net_http_class.new(@uri.host, @uri.port)
net.use_ssl = @uri.is_a?(URI::HTTPS)
if CdnResource.redhat_cdn?(@uri.to_s)
net.cert = @options[:ssl_client_cert]
net.key = @options[:ssl_client_key]
net.ca_file = @options[:ssl_ca_file] if @options[:ssl_ca_file]
else
net.cert_store = @options[:cert_store]
end
# NOTE: This was added because some proxies dont support SSLv23 and do not handle TLS 1.2
# Valid values in ruby 1.9.3 are 'SSLv23' or 'TLSV1'
# Run the following command in rails console to figure out other
# valid constants in other ruby versions
# "OpenSSL::SSL::SSLContext::METHODS"
net.ssl_version = SETTINGS[:katello][:cdn_ssl_version] if SETTINGS[:katello].key?(:cdn_ssl_version)
if (@options[:verify_ssl] == false) || (@options[:verify_ssl] == OpenSSL::SSL::VERIFY_NONE)
net.verify_mode = OpenSSL::SSL::VERIFY_NONE
elsif @options[:verify_ssl].is_a? Integer
net.verify_mode = @options[:verify_ssl]
net.verify_callback = lambda do |preverify_ok, ssl_context|
if !preverify_ok || ssl_context.error != 0
err_msg = "SSL Verification failed -- Preverify: #{preverify_ok}, Error: #{ssl_context.error_string} (#{ssl_context.error})"
fail RestClient::SSLCertificateNotVerified, err_msg
end
true
end
end
net
end
def get(path, _headers = {})
net = http_downloader
path = File.join(@uri.request_uri, path)
used_url = File.join("#{@uri.scheme}://#{@uri.host}:#{@uri.port}", path)
Rails.logger.debug "CDN: Requesting path #{used_url}"
req = Net::HTTP::Get.new(path)
begin
net.start do |http|
res = http.request(req, nil) { |http_response| http_response.read_body }
code = res.code.to_i
if code == 200
return res.body
else
# we don't really use RestClient here (it doesn't allow to safely
# set the proxy only for a set of requests and we don't want the
# backend engines communication to go through the same proxy like
# accessing CDN - its another use case)
# But RestClient exceptions are really nice and can be handled in
# the same way
exception_class = RestClient::Exceptions::EXCEPTIONS_MAP[code] || RestClient::RequestFailed
fail exception_class.new(nil, code)
end
end
rescue EOFError
raise RestClient::ServerBrokeConnection
rescue Timeout::Error
raise RestClient::RequestTimeout
rescue RestClient::ResourceNotFound
raise Errors::NotFound, _("CDN loading error: %s not found") % used_url
rescue RestClient::Unauthorized
raise Errors::SecurityViolation, _("CDN loading error: access denied to %s") % used_url
rescue RestClient::Forbidden
raise Errors::SecurityViolation, _("CDN loading error: access forbidden to %s") % used_url
end
end
def fetch_substitutions(base_path)
get(File.join(base_path, "listing")).split("\n")
rescue Errors::NotFound => e # some of listing file points to not existing content
log :error, e.message
[] # return no substitution for unreachable listings
end
def self.ca_file
"#{Katello::Engine.root}/ca/redhat-uep.pem"
end
def self.ca_file_contents
File.read(ca_file)
end
def net_http_class
if (proxy = ::HttpProxy.default_global_content_proxy)
uri = URI(proxy.url) #Net::HTTP::Proxy ignores port as part of the url
Net::HTTP::Proxy("#{uri.host}#{uri.path}", uri.port, proxy.username, proxy.password)
else
Net::HTTP
end
end
def parse_host(host_or_url)
uri = URI.parse(host_or_url)
return uri.host || uri.path
end
def get_container_listings(content_path)
JSON.parse(get(File.join(content_path, CdnResource::CDN_DOCKER_CONTAINER_LISTING)))
end
# eg content url listing file ->
# /content/dist/rhel/server/7/7Server/x86_64/containers/CONTAINER_REGISTRY_LISTING
# format
# {
# "header": {
# "version": "1.0"
# },
# "payload": {
# "registries": [
# { "name": "rhel",
# "url": "<docker pull url>",
# },
# { "name": "rhel7",
# "url": "test.com:5000/rhel"
# "aliases": [ "redhat/rhel7" ]
# }
# ]
# }
# }
def get_docker_registries(content_path)
docker_listing = get_container_listings(content_path)
docker_listing.try(:[], "payload").try(:[], "registries") || []
rescue ::Katello::Errors::NotFound => e # some of listing file points to not existing content
# If the container listing file was not found
# there is probably no content to be had.
Rails.logger.warn("Could not get to #{content_path}.")
Rails.logger.warn e.to_s
[]
end
def log(level, *args)
[Rails.logger, @logger].compact.each { |logger| logger.send(level, *args) }
end
end
end
end
end