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 192 193 194 195 196 197
|
# frozen_string_literal: true
require 'cgi'
require 'uri'
require_relative '../../puppet/indirector'
require_relative '../../puppet/util/psych_support'
require_relative '../../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.each_with_object({}) { |ary, hash| hash[ary[0].to_sym] = ary[1]; }
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 =~ %r{^\w+:/} and !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
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
uri || "/#{indirection_name}/#{key}"
end
def remote?
node or 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(%r{^/}, ''))
end
end
|