require 'net/http'
require 'uri'
require 'puppet/util/json'
require 'puppet/util/connection'
require 'semantic_puppet'
require 'puppet/network/http'
require 'puppet/network/http_pool'
# Access objects via REST
class Puppet::Indirector::REST < Puppet::Indirector::Terminus
include Puppet::Network::HTTP::Compression.module
IndirectedRoutes = Puppet::Network::HTTP::API::IndirectedRoutes
EXCLUDED_FORMATS = [:yaml, :b64_zlib_yaml, :dot]
# puppet major version where JSON is enabled by default
MAJOR_VERSION_JSON_DEFAULT = 5
class << self
attr_reader :server_setting, :port_setting
end
# Specify the setting that we should use to get the server name.
def self.use_server_setting(setting)
@server_setting = setting
end
# Specify the setting that we should use to get the port.
def self.use_port_setting(setting)
@port_setting = setting
end
# Specify the service to use when doing SRV record lookup
def self.use_srv_service(service)
@srv_service = service
end
def self.srv_service
@srv_service || :puppet
end
# Select the server to use based on the settings configuration
# for this indirection, taking into account the HA server list.
def self.server
Puppet::Util::Connection.determine_server(server_setting)
end
# Select the port to use based on the settings configuration
# for this indirection, taking into account the HA server list.
def self.port
Puppet::Util::Connection.determine_port(port_setting, server_setting)
end
# Should we use puppet's http client to make requests. Will return
# false when running in puppetserver
def use_http_client?
Puppet::Network::HttpPool.http_client_class == Puppet::Network::HTTP::Connection ||
Puppet::Network::HttpPool.http_client_class == Puppet::Network::HTTP::ConnectionAdapter
end
# Provide appropriate headers.
def headers
# yaml is not allowed on the network
network_formats = model.supported_formats - EXCLUDED_FORMATS
mime_types = network_formats.map { |f| model.get_format(f).mime }
common_headers = {
"Accept" => mime_types.join(', '),
Puppet::Network::HTTP::HEADER_PUPPET_VERSION => Puppet.version
}
add_accept_encoding(common_headers)
end
def add_profiling_header(headers)
if (Puppet[:profile])
headers[Puppet::Network::HTTP::HEADER_ENABLE_PROFILING] = "true"
end
headers
end
def network(request)
ssl_context = Puppet.lookup(:ssl_context)
Puppet::Network::HttpPool.connection(request.server || self.class.server,
request.port || self.class.port,
ssl_context: ssl_context)
end
def http_get(request, path, headers = nil, *args)
http_request(:get, request, path, add_profiling_header(headers), *args)
end
def http_post(request, path, data, headers = nil, *args)
http_request(:post, request, path, data, add_profiling_header(headers), *args)
end
def http_head(request, path, headers = nil, *args)
http_request(:head, request, path, add_profiling_header(headers), *args)
end
def http_delete(request, path, headers = nil, *args)
http_request(:delete, request, path, add_profiling_header(headers), *args)
end
def http_put(request, path, data, headers = nil, *args)
http_request(:put, request, path, data, add_profiling_header(headers), *args)
end
def http_request(method, request, *args)
conn = network(request)
conn.send(method, *args)
end
def find(request)
Puppet.deprecation_warning(_("Puppet::Indirector::Rest#find is deprecated. Use Puppet::HTTP::Client instead."))
uri, body = IndirectedRoutes.request_to_uri_and_body(request)
uri_with_query_string = "#{uri}?#{body}"
response = do_request(request) do |req|
# WEBrick in Ruby 1.9.1 only supports up to 1024 character lines in an HTTP request
# http://redmine.ruby-lang.org/issues/show/3991
if "GET #{uri_with_query_string} HTTP/1.1\r\n".length > 1024
uri_with_env = "#{uri}?environment=#{request.environment.name}"
http_post(req, uri_with_env, body, headers)
else
http_get(req, uri_with_query_string, headers)
end
end
if is_http_200?(response)
content_type, body = parse_response(response)
result = deserialize_find(content_type, body)
result.name = request.key if result.respond_to?(:name=)
result
elsif is_http_404?(response)
return nil unless request.options[:fail_on_404]
# 404 can get special treatment as the indirector API can not produce a meaningful
# reason to why something is not found - it may not be the thing the user is
# expecting to find that is missing, but something else (like the environment).
# While this way of handling the issue is not perfect, there is at least an error
# that makes a user aware of the reason for the failure.
#
_, body = parse_response(response)
msg = _("Find %{uri} resulted in 404 with the message: %{body}") % { uri: elide(uri_with_query_string, 100), body: body }
raise Puppet::Error, msg
else
nil
end
end
def head(request)
Puppet.deprecation_warning(_("Puppet::Indirector::Rest#head is deprecated. Use Puppet::HTTP::Client instead."))
response = do_request(request) do |req|
http_head(req, IndirectedRoutes.request_to_uri(req), headers)
end
if is_http_200?(response)
true
else
false
end
end
def search(request)
Puppet.deprecation_warning(_("Puppet::Indirector::Rest#search is deprecated. Use Puppet::HTTP::Client instead."))
response = do_request(request) do |req|
http_get(req, IndirectedRoutes.request_to_uri(req), headers)
end
if is_http_200?(response)
content_type, body = parse_response(response)
deserialize_search(content_type, body) || []
else
[]
end
end
def destroy(request)
Puppet.deprecation_warning(_("Puppet::Indirector::Rest#destroy is deprecated. Use Puppet::HTTP::Client instead."))
raise ArgumentError, _("DELETE does not accept options") unless request.options.empty?
response = do_request(request) do |req|
http_delete(req, IndirectedRoutes.request_to_uri(req), headers)
end
if is_http_200?(response)
content_type, body = parse_response(response)
deserialize_destroy(content_type, body)
else
nil
end
end
def save(request)
Puppet.deprecation_warning(_("Puppet::Indirector::Rest#save is deprecated. Use Puppet::HTTP::Client instead."))
raise ArgumentError, _("PUT does not accept options") unless request.options.empty?
response = do_request(request) do |req|
http_put(req, IndirectedRoutes.request_to_uri(req), req.instance.render, headers.merge({ "Content-Type" => req.instance.mime }))
end
if is_http_200?(response)
content_type, body = parse_response(response)
deserialize_save(content_type, body)
else
nil
end
end
# Encapsulate call to request.do_request with the arguments from this class
# Then yield to the code block that was called in
# We certainly could have retained the full request.do_request(...) { |r| ... }
# but this makes the code much cleaner and we only then actually make the call
# to request.do_request from here, thus if we change what we pass or how we
# get it, we only need to change it here.
def do_request(request)
response = request.do_request(self.class.srv_service, self.class.server, self.class.port) { |req| yield(req) }
handle_response(request, response) if response
response
end
def handle_response(request, response)
server_version = response[Puppet::Network::HTTP::HEADER_PUPPET_VERSION]
if server_version
Puppet.lookup(:server_agent_version) do
Puppet.push_context(:server_agent_version => server_version)
end
if SemanticPuppet::Version.parse(server_version).major < MAJOR_VERSION_JSON_DEFAULT &&
Puppet[:preferred_serialization_format] != 'pson'
#TRANSLATORS "PSON" should not be translated
Puppet.warning(_("Downgrading to PSON for future requests"))
Puppet[:preferred_serialization_format] = 'pson'
end
end
end
def validate_key(request)
# Validation happens on the remote end
end
private
def is_http_200?(response)
case response.code
when "404"
false
when /^2/
true
else
# Raise the http error if we didn't get a 'success' of some kind.
raise convert_to_http_error(response)
end
end
def is_http_404?(response)
response.code == "404"
end
def convert_to_http_error(response)
if response.body.to_s.empty? && response.respond_to?(:message)
returned_message = response.message
elsif response['content-type'].is_a?(String)
content_type, body = parse_response(response)
if content_type =~ /[pj]son/
returned_message = Puppet::Util::Json.load(body)["message"]
else
returned_message = uncompress_body(response)
end
else
returned_message = uncompress_body(response)
end
message = _("Error %{code} on SERVER: %{returned_message}") % { code: response.code, returned_message: returned_message }
Net::HTTPError.new(message, response)
end
# Returns the content_type, stripping any appended charset, and the
# body, decompressed if necessary (content-encoding is checked inside
# uncompress_body)
def parse_response(response)
if response['content-type']
[ response['content-type'].gsub(/\s*;.*$/,''), uncompress_body(response) ]
else
raise _("No content type in http response; cannot parse")
end
end
def deserialize_find(content_type, body)
model.convert_from(content_type, body)
end
def deserialize_search(content_type, body)
model.convert_from_multiple(content_type, body)
end
def deserialize_destroy(content_type, body)
model.convert_from(content_type, body)
end
def deserialize_save(content_type, body)
nil
end
def elide(string, length)
if Puppet::Util::Log.level == :debug || string.length <= length
string
else
string[0, length - 3] + "..."
end
end
end
Anons79 File Manager Version 1.0, Coded By Anons79
Email: [email protected]