Class: Katello::Resources::CDN::CdnResource

Inherits:
Object
  • Object
show all
Defined in:
app/lib/katello/resources/cdn.rb

Direct Known Subclasses

KatelloCdn

Constant Summary collapse

CDN_DOCKER_CONTAINER_LISTING =
"CONTAINER_REGISTRY_LISTING".freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(url, options = {}) ⇒ CdnResource

Returns a new instance of CdnResource.



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
# File 'app/lib/katello/resources/cdn.rb', line 26

def initialize(url, options = {})
  @ssl_version = Setting[:cdn_ssl_version]
  if @ssl_version && !SUPPORTED_SSL_VERSIONS.include?(@ssl_version)
    fail("Invalid SSL version specified. Check the 'CDN SSL Version' setting")
  end

  options.reverse_merge!(:verify_ssl => 9)
  options.assert_valid_keys(:ssl_client_key,
                            :ssl_client_cert,
                            :ssl_ca_file,
                            :ssl_ca_cert,
                            :verify_ssl,
                            :username,
                            :password,
                            :organization_label,
                            :ssl_ca_cert,
                            :custom_cdn)

  if options[:ssl_ca_cert].present?
    @cert_store = OpenSSL::X509::Store.new
    Foreman::Util.add_ca_bundle_to_store(options[:ssl_ca_cert], @cert_store)
  elsif options[:ssl_ca_file]
    @cert_store = OpenSSL::X509::Store.new
    @cert_store.add_file(options[:ssl_ca_file])
  end

  if @cert_store && proxy&.cacert&.present?
    Foreman::Util.add_ca_bundle_to_store(proxy.cacert, @cert_store)
  end

  @url = url
  @uri = URI.parse(url)
  @options = options
end

Class Method Details

.ca_fileObject



187
188
189
# File 'app/lib/katello/resources/cdn.rb', line 187

def self.ca_file
  "#{Katello::Engine.root}/ca/redhat-uep.pem"
end

.create(product: nil, cdn_configuration:) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'app/lib/katello/resources/cdn.rb', line 61

def self.create(product: nil, cdn_configuration:)
  options = {}
  if cdn_configuration.redhat_cdn?
    options[:ssl_client_cert] = OpenSSL::X509::Certificate.new(product.certificate)
    options[:ssl_client_key] = OpenSSL::PKey::RSA.new(product.key)
    options[:ssl_ca_file] = self.ca_file
    self.new(cdn_configuration.url, options)
  elsif cdn_configuration.custom_cdn?
    options[:ssl_ca_cert] = cdn_configuration.ssl_ca
    self.new(cdn_configuration.url, options)
  else
    options[:username] = cdn_configuration.username
    options[:password] = cdn_configuration.password
    options[:organization_label] = cdn_configuration.upstream_organization_label
    options[:content_view_label] = cdn_configuration.upstream_content_view_label
    options[:lifecycle_environment_label] = cdn_configuration.upstream_lifecycle_environment_label
    options[:ssl_ca_cert] = cdn_configuration.ssl_ca
    CDN::KatelloCdn.new(cdn_configuration.url, options)
  end
end

.redhat_cdn?(url) ⇒ Boolean

Returns:

  • (Boolean)


86
87
88
# File 'app/lib/katello/resources/cdn.rb', line 86

def self.redhat_cdn?(url)
  url.include?(redhat_cdn_url)
end

.redhat_cdn_urlObject



82
83
84
# File 'app/lib/katello/resources/cdn.rb', line 82

def self.redhat_cdn_url
  SETTINGS[:katello][:redhat_repository_url]
end

Instance Method Details

#fetch_substitutions(base_path) ⇒ Object



180
181
182
183
184
185
# File 'app/lib/katello/resources/cdn.rb', line 180

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

#get(path, _headers = {}) ⇒ Object

rubocop:disable Metrics/MethodLength



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
# File 'app/lib/katello/resources/cdn.rb', line 128

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.info "CDN: Requesting path #{used_url}"
  req = Net::HTTP::Get.new(path)

  if @options[:username] && @options[:password]
    req.basic_auth(@options[:username], @options[:password])
  end

  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 SocketError
    raise _("Couldn't establish a connection to %s") % @uri
  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

#get_container_listings(content_path) ⇒ Object



205
206
207
# File 'app/lib/katello/resources/cdn.rb', line 205

def get_container_listings(content_path)
  JSON.parse(get(File.join(content_path, CdnResource::CDN_DOCKER_CONTAINER_LISTING)))
end

#get_docker_registries(content_path) ⇒ Object

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" ]
          }
    ]
}

}



228
229
230
231
232
233
234
235
236
237
# File 'app/lib/katello/resources/cdn.rb', line 228

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

#http_downloaderObject



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
# File 'app/lib/katello/resources/cdn.rb', line 94

def http_downloader
  net = net_http_class.new(@uri.host, @uri.port)
  net.use_ssl = @uri.is_a?(URI::HTTPS)

  if @uri.is_a?(URI::HTTPS)
    net.cert_store = @cert_store
    net.cert = @options[:ssl_client_cert]
    net.key = @options[:ssl_client_key]
    net.cert_store = @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 = @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

#log(level, *args) ⇒ Object



239
240
241
# File 'app/lib/katello/resources/cdn.rb', line 239

def log(level, *args)
  [Rails.logger, @logger].compact.each { |logger| logger.send(level, *args) }
end

#net_http_classObject



191
192
193
194
195
196
197
198
# File 'app/lib/katello/resources/cdn.rb', line 191

def net_http_class
  if proxy
    uri = URI(proxy.url) #Net::HTTP::Proxy ignores port as part of the url
    Net::HTTP::Proxy("#{uri.host}", uri.port, proxy.username, proxy.password)
  else
    Net::HTTP
  end
end

#parse_host(host_or_url) ⇒ Object



200
201
202
203
# File 'app/lib/katello/resources/cdn.rb', line 200

def parse_host(host_or_url)
  uri = URI.parse(host_or_url)
  return uri.host || uri.path
end

#proxyObject



90
91
92
# File 'app/lib/katello/resources/cdn.rb', line 90

def proxy
  ::HttpProxy.default_global_content_proxy
end

#substitutorObject



22
23
24
# File 'app/lib/katello/resources/cdn.rb', line 22

def substitutor
  @substitutor ||= Util::CdnVarSubstitutor.new(self)
end

#valid_path?(path, postfix) ⇒ Boolean

rubocop:enable Metrics/MethodLength

Returns:

  • (Boolean)


172
173
174
175
176
177
178
# File 'app/lib/katello/resources/cdn.rb', line 172

def valid_path?(path, postfix)
  get(File.join(path, postfix)).present?
rescue RestClient::MovedPermanently
  return true
rescue Errors::NotFound
  return false
end