class Puppet::Network::HTTP::API::IndirectedRoutes
Constants
- IndirectionType
- METHOD_MAP
How we map http methods and the indirection name in the URI to an indirection method.
Public Class Methods
routes()
click to toggle source
# File lib/puppet/network/http/api/indirected_routes.rb 28 def self.routes 29 Puppet::Network::HTTP::Route.path(/.*/).any(new) 30 end
Private Class Methods
pluralize(indirection)
click to toggle source
# File lib/puppet/network/http/api/indirected_routes.rb 251 def self.pluralize(indirection) 252 return(indirection == "status" ? "statuses" : indirection + "s") 253 end
Public Instance Methods
call(request, response)
click to toggle source
Handle an HTTP request. The request has already been authenticated prior to calling this method.
# File lib/puppet/network/http/api/indirected_routes.rb 34 def call(request, response) 35 indirection, method, key, params = uri2indirection(request.method, request.path, request.params) 36 certificate = request.client_cert 37 38 if !indirection.allow_remote_requests? 39 # TODO: should we tell the user we found an indirection but it doesn't 40 # allow remote requests, or just pretend there's no handler at all? what 41 # are the security implications for the former? 42 raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new(_("No handler for %{indirection}") % { indirection: indirection.name }, :NO_INDIRECTION_REMOTE_REQUESTS) 43 end 44 45 overrides = { 46 trusted_information: Puppet::Context::TrustedInformation.remote(params[:authenticated], params[:node], certificate), 47 } 48 if params[:environment] 49 overrides[:current_environment] = params[:environment] 50 end 51 52 Puppet.override(overrides) do 53 send("do_#{method}", indirection, key, params, request, response) 54 end 55 end
uri2indirection(http_method, uri, params)
click to toggle source
# File lib/puppet/network/http/api/indirected_routes.rb 57 def uri2indirection(http_method, uri, params) 58 # the first field is always nil because of the leading slash, 59 indirection_type, version, indirection_name, key = uri.split("/", 5)[1..-1] 60 url_prefix = "/#{indirection_type}/#{version}" 61 environment = params.delete(:environment) 62 63 if indirection_name !~ /^\w+$/ 64 raise Puppet::Network::HTTP::Error::HTTPBadRequestError.new( 65 _("The indirection name must be purely alphanumeric, not '%{indirection_name}'") % { indirection_name: indirection_name }) 66 end 67 68 # this also depluralizes the indirection_name if it is a search 69 method = indirection_method(http_method, indirection_name) 70 71 # check whether this indirection matches the prefix and version in the 72 # request 73 if url_prefix != IndirectionType.url_prefix_for(indirection_name) 74 raise Puppet::Network::HTTP::Error::HTTPBadRequestError.new( 75 _("Indirection '%{indirection_name}' does not match url prefix '%{url_prefix}'") % { indirection_name: indirection_name, url_prefix: url_prefix }) 76 end 77 78 indirection = Puppet::Indirector::Indirection.instance(indirection_name.to_sym) 79 if !indirection 80 raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new( 81 _("Could not find indirection '%{indirection_name}'") % { indirection_name: indirection_name }, 82 Puppet::Network::HTTP::Issues::HANDLER_NOT_FOUND) 83 end 84 85 if !environment 86 raise Puppet::Network::HTTP::Error::HTTPBadRequestError.new( 87 _("An environment parameter must be specified")) 88 end 89 90 if ! Puppet::Node::Environment.valid_name?(environment) 91 raise Puppet::Network::HTTP::Error::HTTPBadRequestError.new( 92 _("The environment must be purely alphanumeric, not '%{environment}'") % { environment: environment }) 93 end 94 95 configured_environment = Puppet.lookup(:environments).get(environment) 96 unless configured_environment.nil? 97 configured_environment = configured_environment.override_from_commandline(Puppet.settings) 98 params[:environment] = configured_environment 99 end 100 101 if configured_environment.nil? && indirection.terminus.require_environment? 102 raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new( 103 _("Could not find environment '%{environment}'") % { environment: environment }) 104 end 105 106 params.delete(:bucket_path) 107 108 if key == "" or key.nil? 109 raise Puppet::Network::HTTP::Error::HTTPBadRequestError.new( 110 _("No request key specified in %{uri}") % { uri: uri }) 111 end 112 113 [indirection, method, key, params] 114 end
Private Instance Methods
accepted_response_formatter_or_json_for(model_class, request)
click to toggle source
Return the first response formatter that the client accepts and the server supports, or default to 'application/json'.
# File lib/puppet/network/http/api/indirected_routes.rb 215 def accepted_response_formatter_or_json_for(model_class, request) 216 request.response_formatters_for(model_class.supported_formats, "application/json").first 217 end
accepted_response_formatters_for(model_class, request)
click to toggle source
Return an array of response formatters that the client accepts and the server supports.
# File lib/puppet/network/http/api/indirected_routes.rb 209 def accepted_response_formatters_for(model_class, request) 210 request.response_formatters_for(model_class.supported_formats) 211 end
do_destroy(indirection, key, params, request, response)
click to toggle source
Execute our destroy.
# File lib/puppet/network/http/api/indirected_routes.rb 168 def do_destroy(indirection, key, params, request, response) 169 formatter = accepted_response_formatter_or_json_for(indirection.model, request) 170 171 result = indirection.destroy(key, params) 172 173 response.respond_with(200, formatter, formatter.render(result)) 174 end
do_find(indirection, key, params, request, response)
click to toggle source
Execute our find.
# File lib/puppet/network/http/api/indirected_routes.rb 119 def do_find(indirection, key, params, request, response) 120 result = indirection.find(key, params) 121 unless result 122 raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new(_("Could not find %{value0} %{key}") % { value0: indirection.name, key: key }, Puppet::Network::HTTP::Issues::RESOURCE_NOT_FOUND) 123 end 124 125 rendered_result = result 126 127 rendered_format = first_response_formatter_for(indirection.model, request) do |format| 128 if result.respond_to?(:render) 129 Puppet::Util::Profiler.profile(_("Rendered result in %{format}") % { format: format }, [:http, :v3_render, format]) do 130 rendered_result = result.render(format) 131 end 132 end 133 end 134 135 Puppet::Util::Profiler.profile(_("Sent response"), [:http, :v3_response]) do 136 response.respond_with(200, rendered_format, rendered_result) 137 end 138 end
do_head(indirection, key, params, request, response)
click to toggle source
Execute our head.
# File lib/puppet/network/http/api/indirected_routes.rb 141 def do_head(indirection, key, params, request, response) 142 unless indirection.head(key, params) 143 raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new(_("Could not find %{indirection} %{key}") % { indirection: indirection.name, key: key }, Puppet::Network::HTTP::Issues::RESOURCE_NOT_FOUND) 144 end 145 146 # No need to set a response because no response is expected from a 147 # HEAD request. All we need to do is not die. 148 end
do_save(indirection, key, params, request, response)
click to toggle source
Execute our save.
# File lib/puppet/network/http/api/indirected_routes.rb 177 def do_save(indirection, key, params, request, response) 178 formatter = accepted_response_formatter_or_json_for(indirection.model, request) 179 sent_object = read_body_into_model(indirection.model, request) 180 181 result = indirection.save(sent_object, key) 182 183 response.respond_with(200, formatter, formatter.render(result)) 184 end
do_search(indirection, key, params, request, response)
click to toggle source
Execute our search.
# File lib/puppet/network/http/api/indirected_routes.rb 151 def do_search(indirection, key, params, request, response) 152 result = indirection.search(key, params) 153 154 if result.nil? 155 raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new(_("Could not find instances in %{indirection} with '%{key}'") % { indirection: indirection.name, key: key }, Puppet::Network::HTTP::Issues::RESOURCE_NOT_FOUND) 156 end 157 158 rendered_result = nil 159 160 rendered_format = first_response_formatter_for(indirection.model, request) do |format| 161 rendered_result = indirection.model.render_multiple(format, result) 162 end 163 164 response.respond_with(200, rendered_format, rendered_result) 165 end
first_response_formatter_for(model, request) { |format| ... }
click to toggle source
Return the first response formatter that didn't cause the yielded block to raise a FormatError.
# File lib/puppet/network/http/api/indirected_routes.rb 188 def first_response_formatter_for(model, request, &block) 189 formats = accepted_response_formatters_for(model, request) 190 formatter = formats.find do |format| 191 begin 192 yield format 193 true 194 rescue Puppet::Network::FormatHandler::FormatError => err 195 Puppet.log_exception(err, err.message, level: :debug) 196 false 197 end 198 end 199 200 return formatter if formatter 201 202 raise Puppet::Network::HTTP::Error::HTTPNotAcceptableError.new( 203 _("No supported formats are acceptable (Accept: %{accepted_formats})") % { accepted_formats: formats.map(&:mime).join(', ') }, 204 Puppet::Network::HTTP::Issues::UNSUPPORTED_FORMAT) 205 end
indirection_method(http_method, indirection)
click to toggle source
# File lib/puppet/network/http/api/indirected_routes.rb 238 def indirection_method(http_method, indirection) 239 raise Puppet::Network::HTTP::Error::HTTPMethodNotAllowedError.new( 240 _("No support for http method %{http_method}") % { http_method: http_method }) unless METHOD_MAP[http_method] 241 242 method = METHOD_MAP[http_method][plurality(indirection)] 243 unless method 244 raise Puppet::Network::HTTP::Error::HTTPBadRequestError.new( 245 _("No support for plurality %{indirection} for %{http_method} operations") % { indirection: plurality(indirection), http_method: http_method }) 246 end 247 248 method 249 end
plurality(indirection)
click to toggle source
# File lib/puppet/network/http/api/indirected_routes.rb 256 def plurality(indirection) 257 # NOTE These specific hooks for paths are ridiculous, but it's a *many*-line 258 # fix to not need this, and our goal is to move away from the complication 259 # that leads to the fix being too long. 260 return :singular if indirection == "facts" 261 return :singular if indirection == "status" 262 return :singular if indirection == "certificate_status" 263 264 result = (indirection =~ /s$|_search$/) ? :plural : :singular 265 266 indirection.sub!(/s$|_search$/, '') 267 indirection.sub!(/statuse$/, 'status') 268 269 result 270 end
read_body_into_model(model_class, request)
click to toggle source
# File lib/puppet/network/http/api/indirected_routes.rb 219 def read_body_into_model(model_class, request) 220 data = request.body.to_s 221 formatter = request.formatter 222 223 if formatter.supported?(model_class) 224 begin 225 return model_class.convert_from(formatter.name.to_s, data) 226 rescue => e 227 raise Puppet::Network::HTTP::Error::HTTPBadRequestError.new( 228 _("The request body is invalid: %{message}") % { message: e.message }) 229 end 230 end 231 232 #TRANSLATORS "mime-type" is a keyword and should not be translated 233 raise Puppet::Network::HTTP::Error::HTTPUnsupportedMediaTypeError.new( 234 _("Client sent a mime-type (%{header}) that doesn't correspond to a format we support") % { header: request.headers['content-type'] }, 235 Puppet::Network::HTTP::Issues::UNSUPPORTED_MEDIA_TYPE) 236 end