Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tag: debian/2.6.0-3
Fetching contributors…

Cannot retrieve contributors at this time

file 199 lines (162 sloc) 5.137 kb
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 198 199
require 'cgi'
require 'uri'
require 'puppet/indirector'

# This class encapsulates all of the information you need to make an
# Indirection call, and as a a result also handles REST calls. It's somewhat
# analogous to an HTTP Request object, except tuned for our Indirector.
class Puppet::Indirector::Request
  attr_accessor :key, :method, :options, :instance, :node, :ip, :authenticated, :ignore_cache, :ignore_terminus

  attr_accessor :server, :port, :uri, :protocol

  attr_reader :indirection_name

  OPTION_ATTRIBUTES = [:ip, :node, :authenticated, :ignore_terminus, :ignore_cache, :instance, :environment]

  # Is this an authenticated request?
  def authenticated?
    # Double negative, so we just get true or false
    ! ! authenticated
  end

  def environment
    @environment ||= Puppet::Node::Environment.new
  end

  def environment=(env)
    @environment = if env.is_a?(Puppet::Node::Environment)
      env
    else
      Puppet::Node::Environment.new(env)
    end
  end

  def escaped_key
    URI.escape(key)
  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_terminus?
    ignore_terminus
  end

  def initialize(indirection_name, method, key_or_instance, options_or_instance = {})
    if options_or_instance.is_a? Hash
      options = options_or_instance
      @instance = nil
    else
      options = {}
      @instance = options_or_instance
    end

    self.indirection_name = indirection_name
    self.method = method

    set_attributes(options)

    @options = options.inject({}) { |hash, ary| hash[ary[0].to_sym] = ary[1]; hash }

    if key_or_instance.is_a?(String) || key_or_instance.is_a?(Symbol)
      key = key_or_instance
    else
      @instance ||= key_or_instance
    end

    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+:\/\// # 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
    raise ArgumentError, "Could not find indirection '#{indirection_name}'" unless i = indirection
    i.model
  end

  # Should we allow use of the cached object?
  def use_cache?
    if defined?(@use_cache)
      ! ! use_cache
    else
      true
    end
  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 "" unless options and ! options.empty?
    "?" + options.collect do |key, value|
      case value
      when nil; next
      when true, false; value = value.to_s
      when Fixnum, Bignum, Float; value = value # nothing
      when String; value = CGI.escape(value)
      when Symbol; value = CGI.escape(value.to_s)
      when Array; value = CGI.escape(YAML.dump(value))
      else
        raise ArgumentError, "HTTP REST queries cannot handle values of type '#{value.class}'"
      end

      "#{key}=#{value}"
    end.join("&")
  end

  def to_hash
    result = options.dup

    OPTION_ATTRIBUTES.each do |attribute|
      if value = send(attribute)
        result[attribute] = value
      end
    end
    result
  end

  def to_s
    return(uri ? uri : "/#{indirection_name}/#{key}")
  end

  private

  def set_attributes(options)
    OPTION_ATTRIBUTES.each do |attribute|
      if options.include?(attribute)
        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
      uri = URI.parse(URI.escape(key))
    rescue => detail
      raise ArgumentError, "Could not understand URL #{key}: #{detail}"
    end

    # Just short-circuit these to full paths
    if uri.scheme == "file"
      @key = URI.unescape(uri.path)
      return
    end

    @server = uri.host if uri.host

    # 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[:masterport].to_i
    else
      @port = uri.port.to_i
    end

    @protocol = uri.scheme

    if uri.scheme == 'puppet'
      @key = URI.unescape(uri.path.sub(/^\//, ''))
      return
    end

    env, indirector, @key = URI.unescape(uri.path.sub(/^\//, '')).split('/',3)
    @key ||= ''
    self.environment = env unless env == ''
  end
end
Something went wrong with that request. Please try again.