require 'cgi'
require 'uri'
require 'puppet/indirector'
require 'puppet/network/resolver'
require 'puppet/util/psych_support'
require 'puppet/util/warnings'
# This class encapsulates all of the information you need to make an
# Indirection call, and as a result also handles REST calls. It's somewhat
# analogous to an HTTP Request object, except tuned for our Indirector.
class Puppet::Indirector::Request
include Puppet::Util::PsychSupport
include Puppet::Util::Warnings
attr_accessor :key, :method, :options, :instance, :node, :ip, :authenticated, :ignore_cache, :ignore_cache_save, :ignore_terminus
attr_accessor :server, :port, :uri, :protocol
attr_reader :indirection_name
# trusted_information is specifically left out because we can't serialize it
# and keep it "trusted"
OPTION_ATTRIBUTES = [:ip, :node, :authenticated, :ignore_terminus, :ignore_cache, :ignore_cache_save, :instance, :environment]
# Is this an authenticated request?
def authenticated?
# Double negative, so we just get true or false
! ! authenticated
end
def environment
# If environment has not been set directly, we should use the application's
# current environment
@environment ||= Puppet.lookup(:current_environment)
end
def environment=(env)
@environment =
if env.is_a?(Puppet::Node::Environment)
env
else
Puppet.lookup(:environments).get!(env)
end
end
# LAK:NOTE This is a messy interface to the cache, and it's only
# used by the Configurer class. I decided it was better to implement
# it now and refactor later, when we have a better design, than
# to spend another month coming up with a design now that might
# not be any better.
def ignore_cache?
ignore_cache
end
def ignore_cache_save?
ignore_cache_save
end
def ignore_terminus?
ignore_terminus
end
def initialize(indirection_name, method, key, instance, options = {})
@instance = instance
options ||= {}
self.indirection_name = indirection_name
self.method = method
options = options.inject({}) { |hash, ary| hash[ary[0].to_sym] = ary[1]; hash }
set_attributes(options)
@options = options
if key
# If the request key is a URI, then we need to treat it specially,
# because it rewrites the key. We could otherwise strip server/port/etc
# info out in the REST class, but it seemed bad design for the REST
# class to rewrite the key.
if key.to_s =~ /^\w+:\// and not Puppet::Util.absolute_path?(key.to_s) # it's a URI
set_uri_key(key)
else
@key = key
end
end
@key = @instance.name if ! @key and @instance
end
# Look up the indirection based on the name provided.
def indirection
Puppet::Indirector::Indirection.instance(indirection_name)
end
def indirection_name=(name)
@indirection_name = name.to_sym
end
def model
ind = indirection
raise ArgumentError, _("Could not find indirection '%{indirection}'") % { indirection: indirection_name } unless ind
ind.model
end
# Are we trying to interact with multiple resources, or just one?
def plural?
method == :search
end
# Create the query string, if options are present.
def query_string
return "" if options.nil? || options.empty?
encode_params(expand_into_parameters(options.to_a))
end
def expand_into_parameters(data)
data.inject([]) do |params, key_value|
key, value = key_value
expanded_value = case value
when Array
value.collect { |val| [key, val] }
else
[key_value]
end
params.concat(expand_primitive_types_into_parameters(expanded_value))
end
end
def expand_primitive_types_into_parameters(data)
data.inject([]) do |params, key_value|
key, value = key_value
case value
when nil
params
when true, false, String, Symbol, Integer, Float
params << [key, value]
else
raise ArgumentError, _("HTTP REST queries cannot handle values of type '%{klass}'") % { klass: value.class }
end
end
end
def encode_params(params)
params.collect do |key, value|
"#{key}=#{Puppet::Util.uri_query_encode(value.to_s)}"
end.join("&")
end
def initialize_from_hash(hash)
@indirection_name = hash['indirection_name'].to_sym
@method = hash['method'].to_sym
@key = hash['key']
@instance = hash['instance']
@options = hash['options']
end
def to_data_hash
{ 'indirection_name' => @indirection_name.to_s,
'method' => @method.to_s,
'key' => @key,
'instance' => @instance,
'options' => @options }
end
def to_hash
result = options.dup
OPTION_ATTRIBUTES.each do |attribute|
value = send(attribute)
if value
result[attribute] = value
end
end
result
end
def description
return(uri ? uri : "/#{indirection_name}/#{key}")
end
def do_request(srv_service=:puppet, default_server=nil, default_port=nil, &block)
# We were given a specific server to use, so just use that one.
# This happens if someone does something like specifying a file
# source using a puppet:// URI with a specific server.
return yield(self) if !self.server.nil?
if Puppet.settings[:use_srv_records]
# We may want to consider not creating a new resolver here
# every request eventually, to take advantage of the resolver's
# caching behavior.
resolver = Puppet::Network::Resolver.new
resolver.each_srv_record(Puppet.settings[:srv_domain], srv_service) do |srv_server, srv_port|
begin
self.server = srv_server
self.port = srv_port
return yield(self)
rescue SystemCallError => e
Puppet.warning _("Error connecting to %{srv_server}:%{srv_port}: %{message}") % { srv_server: srv_server, srv_port: srv_port, message: e.message }
end
end
end
if default_server
self.server = default_server
else
self.server = Puppet.lookup(:server) do
primary_server = Puppet.settings[:server_list][0]
if primary_server
#TRANSLATORS 'server_list' is the name of a setting and should not be translated
debug_once _("Selected server from first entry of the `server_list` setting: %{server}") % {server: primary_server[0]}
primary_server[0]
else
#TRANSLATORS 'server' is the name of a setting and should not be translated
debug_once _("Selected server from the `server` setting: %{server}") % {server: Puppet.settings[:server]}
Puppet.settings[:server]
end
end
end
if default_port
self.port = default_port
else
self.port = Puppet.lookup(:serverport) do
primary_server = Puppet.settings[:server_list][0]
if primary_server
#TRANSLATORS 'server_list' is the name of a setting and should not be translated
debug_once _("Selected port from the first entry of the `server_list` setting: %{port}") % {port: primary_server[1]}
primary_server[1]
else
#TRANSLATORS 'serverport' is the name of a setting and should not be translated
debug_once _("Selected port from the `serverport` setting: %{port}") % {port: Puppet.settings[:serverport]}
Puppet.settings[:serverport]
end
end
end
return yield(self)
end
def remote?
self.node or self.ip
end
private
def set_attributes(options)
OPTION_ATTRIBUTES.each do |attribute|
if options.include?(attribute.to_sym)
send(attribute.to_s + "=", options[attribute])
options.delete(attribute)
end
end
end
# Parse the key as a URI, setting attributes appropriately.
def set_uri_key(key)
@uri = key
begin
# calling uri_encode for UTF-8 characters will % escape them and keep them UTF-8
uri = URI.parse(Puppet::Util.uri_encode(key))
rescue => detail
raise ArgumentError, _("Could not understand URL %{key}: %{detail}") % { key: key, detail: detail }, detail.backtrace
end
# Just short-circuit these to full paths
if uri.scheme == "file"
@key = Puppet::Util.uri_to_path(uri)
return
end
@server = uri.host if uri.host && !uri.host.empty?
# If the URI class can look up the scheme, it will provide a port,
# otherwise it will default to '0'.
if uri.port.to_i == 0 and uri.scheme == "puppet"
@port = Puppet.settings[:serverport].to_i
else
@port = uri.port.to_i
end
# filebucket:// is only used internally to pass request details
# from Dipper objects to the indirector. The wire always uses HTTPS.
if uri.scheme == 'filebucket'
@protocol = 'https'
else
@protocol = uri.scheme
end
@key = Puppet::Util.uri_unescape(uri.path.sub(/^\//, ''))
end
end
Anons79 File Manager Version 1.0, Coded By Anons79
Email: [email protected]