spice 0.8.0 → 1.0.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +6 -6
- data/README.md +5 -63
- data/lib/spice.rb +56 -151
- data/lib/spice/client.rb +23 -36
- data/lib/spice/config.rb +52 -0
- data/lib/spice/connection.rb +89 -74
- data/lib/spice/connection/authentication.rb +47 -0
- data/lib/spice/connection/clients.rb +15 -0
- data/lib/spice/connection/cookbooks.rb +42 -0
- data/lib/spice/connection/data_bags.rb +35 -0
- data/lib/spice/connection/environments.rb +16 -0
- data/lib/spice/connection/nodes.rb +15 -0
- data/lib/spice/connection/roles.rb +15 -0
- data/lib/spice/connection/search.rb +49 -0
- data/lib/spice/cookbook.rb +13 -30
- data/lib/spice/cookbook_version.rb +43 -0
- data/lib/spice/data_bag.rb +17 -121
- data/lib/spice/data_bag_item.rb +35 -0
- data/lib/spice/environment.rb +15 -79
- data/lib/spice/error.rb +30 -0
- data/lib/spice/node.rb +19 -36
- data/lib/spice/persistence.rb +42 -0
- data/lib/spice/request.rb +27 -0
- data/lib/spice/request/auth.rb +14 -0
- data/lib/spice/response/client_error.rb +30 -0
- data/lib/spice/response/parse_json.rb +24 -0
- data/lib/spice/role.rb +18 -25
- data/lib/spice/version.rb +1 -1
- data/spec/spec_helper.rb +0 -1
- data/spice.gemspec +4 -3
- metadata +65 -42
- data/lib/spice/core_ext/hash.rb +0 -21
- data/lib/spice/search.rb +0 -23
    
        data/lib/spice/connection.rb
    CHANGED
    
    | @@ -1,81 +1,94 @@ | |
| 1 1 | 
             
            require 'yajl'
         | 
| 2 | 
            +
            require 'uri'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'spice/request'
         | 
| 5 | 
            +
            require 'spice/connection/authentication'
         | 
| 6 | 
            +
            require 'spice/connection/search'
         | 
| 7 | 
            +
            require 'spice/connection/clients'
         | 
| 8 | 
            +
            require 'spice/connection/cookbooks'
         | 
| 9 | 
            +
            require 'spice/connection/data_bags'
         | 
| 10 | 
            +
            require 'spice/connection/environments'
         | 
| 11 | 
            +
            require 'spice/connection/nodes'
         | 
| 12 | 
            +
            require 'spice/connection/roles'
         | 
| 13 | 
            +
            require 'spice/connection/search'
         | 
| 2 14 |  | 
| 3 15 | 
             
            module Spice
         | 
| 4 16 | 
             
              class Connection
         | 
| 5 | 
            -
                 | 
| 17 | 
            +
                include Toy::Store
         | 
| 18 | 
            +
                store :memory, {}
         | 
| 6 19 |  | 
| 7 | 
            -
                 | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
                 | 
| 20 | 
            +
                include Spice::Connection::Clients
         | 
| 21 | 
            +
                include Spice::Connection::Cookbooks
         | 
| 22 | 
            +
                include Spice::Connection::DataBags
         | 
| 23 | 
            +
                include Spice::Connection::Environments
         | 
| 24 | 
            +
                include Spice::Connection::Nodes
         | 
| 25 | 
            +
                include Spice::Connection::Roles
         | 
| 26 | 
            +
                include Spice::Connection::Search
         | 
| 27 | 
            +
                include Spice::Connection::Authentication
         | 
| 28 | 
            +
                include Spice::Connection::Search
         | 
| 29 | 
            +
                include Spice::Request
         | 
| 30 | 
            +
                    
         | 
| 31 | 
            +
                attribute :client_name, String
         | 
| 32 | 
            +
                attribute :key_file, String
         | 
| 33 | 
            +
                attribute :key, String
         | 
| 34 | 
            +
                attribute :server_url, String
         | 
| 35 | 
            +
                attribute :sign_on_redirect, Boolean, :default => true
         | 
| 36 | 
            +
                attribute :sign_request, Boolean, :default => true
         | 
| 37 | 
            +
                attribute :endpoint, String
         | 
| 17 38 |  | 
| 18 | 
            -
                 | 
| 19 | 
            -
                  begin
         | 
| 20 | 
            -
                    response = RestClient.get(
         | 
| 21 | 
            -
                      "#{@server_url}#{path}", 
         | 
| 22 | 
            -
                      build_headers(:GET, "#{@url_path}#{path}", headers)
         | 
| 23 | 
            -
                    )
         | 
| 24 | 
            -
                    return Yajl.load(response.body)
         | 
| 25 | 
            -
                   
         | 
| 26 | 
            -
                  rescue => e
         | 
| 27 | 
            -
                    e.response
         | 
| 28 | 
            -
                  end
         | 
| 29 | 
            -
                end
         | 
| 39 | 
            +
                validates_presence_of :client_name, :key_file, :server_url
         | 
| 30 40 |  | 
| 31 | 
            -
                def  | 
| 32 | 
            -
                   | 
| 33 | 
            -
                    response = RestClient.post(
         | 
| 34 | 
            -
                      "#{@server_url}#{path}",
         | 
| 35 | 
            -
                      Yajl.dump(payload),
         | 
| 36 | 
            -
                      build_headers(:POST, "#{@url_path}#{path}", headers, Yajl.dump(payload))
         | 
| 37 | 
            -
                    )      
         | 
| 38 | 
            -
                    return Yajl.load(response.body)
         | 
| 39 | 
            -
                      
         | 
| 40 | 
            -
                  rescue => e
         | 
| 41 | 
            -
                    e.response
         | 
| 42 | 
            -
                  end
         | 
| 41 | 
            +
                def connection
         | 
| 42 | 
            +
                  self
         | 
| 43 43 | 
             
                end
         | 
| 44 44 |  | 
| 45 | 
            -
                def  | 
| 46 | 
            -
                   | 
| 47 | 
            -
                    response = RestClient.put(
         | 
| 48 | 
            -
                    "#{@server_url}#{path}",
         | 
| 49 | 
            -
                      Yajl.dump(payload),
         | 
| 50 | 
            -
                      build_headers(:PUT, "#{@url_path}#{path}", headers, Yajl.dump(payload))
         | 
| 51 | 
            -
                    )
         | 
| 52 | 
            -
                    return Yajl.load(response.body)
         | 
| 53 | 
            -
                    
         | 
| 54 | 
            -
                  rescue => e
         | 
| 55 | 
            -
                    e.response
         | 
| 56 | 
            -
                  end
         | 
| 45 | 
            +
                def parsed_url
         | 
| 46 | 
            +
                  URI.parse(server_url)
         | 
| 57 47 | 
             
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                def get(path)
         | 
| 50 | 
            +
                  response = request(:headers => build_headers(
         | 
| 51 | 
            +
                    :GET, 
         | 
| 52 | 
            +
                    "#{parsed_url.path}#{path}")
         | 
| 53 | 
            +
                  ).get(
         | 
| 54 | 
            +
                    "#{server_url}#{path}"
         | 
| 55 | 
            +
                  )
         | 
| 56 | 
            +
                  return response
         | 
| 57 | 
            +
                end # def get
         | 
| 58 58 |  | 
| 59 | 
            -
                def  | 
| 60 | 
            -
                   | 
| 61 | 
            -
                     | 
| 62 | 
            -
                    "#{ | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
                     | 
| 66 | 
            -
                    
         | 
| 67 | 
            -
                   | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
                end  
         | 
| 59 | 
            +
                def post(path, payload)
         | 
| 60 | 
            +
                  response = request(:headers => build_headers(
         | 
| 61 | 
            +
                    :POST, 
         | 
| 62 | 
            +
                    "#{parsed_url.path}#{path}", 
         | 
| 63 | 
            +
                    Yajl.dump(payload))
         | 
| 64 | 
            +
                  ).post(
         | 
| 65 | 
            +
                    "#{server_url}#{path}", 
         | 
| 66 | 
            +
                    Yajl.dump(payload)
         | 
| 67 | 
            +
                  )
         | 
| 68 | 
            +
                  return response
         | 
| 69 | 
            +
                end # def post
         | 
| 71 70 |  | 
| 72 | 
            -
                def  | 
| 73 | 
            -
                   | 
| 74 | 
            -
             | 
| 71 | 
            +
                def put(path, payload, headers={})
         | 
| 72 | 
            +
                  response = request(:headers => build_headers(
         | 
| 73 | 
            +
                    :PUT, 
         | 
| 74 | 
            +
                    "#{parsed_url.path}#{path}", 
         | 
| 75 | 
            +
                    Yajl.dump(payload))
         | 
| 76 | 
            +
                  ).put(
         | 
| 77 | 
            +
                    "#{server_url}#{path}", 
         | 
| 78 | 
            +
                    Yajl.dump(payload)
         | 
| 79 | 
            +
                  )
         | 
| 80 | 
            +
                  return response
         | 
| 81 | 
            +
                end # def put
         | 
| 75 82 |  | 
| 76 | 
            -
                def  | 
| 77 | 
            -
                   | 
| 78 | 
            -
             | 
| 83 | 
            +
                def delete(path, headers={})
         | 
| 84 | 
            +
                  response = request(:headers => build_headers(
         | 
| 85 | 
            +
                    :DELETE, 
         | 
| 86 | 
            +
                    "#{parsed_url.path}#{path}")
         | 
| 87 | 
            +
                  ).delete(
         | 
| 88 | 
            +
                    "#{server_url}#{path}"
         | 
| 89 | 
            +
                  )
         | 
| 90 | 
            +
                  return response
         | 
| 91 | 
            +
                end # def delete
         | 
| 79 92 |  | 
| 80 93 | 
             
                private
         | 
| 81 94 |  | 
| @@ -84,18 +97,20 @@ module Spice | |
| 84 97 | 
             
                    :http_method => method, 
         | 
| 85 98 | 
             
                    :path => path.gsub(/\?.*$/, ''),
         | 
| 86 99 | 
             
                    :body => json_body, 
         | 
| 87 | 
            -
                    :host => "#{ | 
| 100 | 
            +
                    :host => "#{parsed_url.host}:#{parsed_url.port}"
         | 
| 88 101 | 
             
                  }
         | 
| 89 102 | 
             
                  request_params[:body] ||= ""
         | 
| 90 | 
            -
                   | 
| 91 | 
            -
                end
         | 
| 103 | 
            +
                  signature_headers(request_params)
         | 
| 104 | 
            +
                end # def authentication_headers
         | 
| 92 105 |  | 
| 93 | 
            -
                def build_headers(method, path,  | 
| 94 | 
            -
                  headers | 
| 95 | 
            -
                  headers[ | 
| 106 | 
            +
                def build_headers(method, path, json_body=nil)
         | 
| 107 | 
            +
                  headers={}
         | 
| 108 | 
            +
                  # headers['Accept']       = "application/json"
         | 
| 109 | 
            +
                  # headers["Content-Type"] = 'application/json' if json_body
         | 
| 96 110 | 
             
                  headers['Content-Length'] = json_body.bytesize.to_s if json_body
         | 
| 97 111 | 
             
                  headers.merge!(authentication_headers(method, path, json_body)) if sign_requests?
         | 
| 98 112 | 
             
                  headers
         | 
| 99 | 
            -
                end
         | 
| 100 | 
            -
             | 
| 101 | 
            -
            end
         | 
| 113 | 
            +
                end # def build_headers
         | 
| 114 | 
            +
             | 
| 115 | 
            +
              end # class Connection
         | 
| 116 | 
            +
            end # module Spice
         | 
| @@ -0,0 +1,47 @@ | |
| 1 | 
            +
            module Spice
         | 
| 2 | 
            +
              class Connection
         | 
| 3 | 
            +
                module Authentication
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                  def sign_requests?
         | 
| 6 | 
            +
                    !!key_file
         | 
| 7 | 
            +
                  end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def signature_headers(request_params={})
         | 
| 10 | 
            +
                    load_signing_key if sign_requests?
         | 
| 11 | 
            +
                    request_params             = request_params.dup
         | 
| 12 | 
            +
                    request_params[:timestamp] = Time.now.utc.iso8601
         | 
| 13 | 
            +
                    request_params[:user_id]   = client_name
         | 
| 14 | 
            +
                    host = request_params.delete(:host) || "localhost"
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object(request_params)
         | 
| 17 | 
            +
                    signed = sign_obj.sign(@key).merge({:host => host})
         | 
| 18 | 
            +
                    signed.inject({}){|memo, kv| memo["#{kv[0].to_s.upcase}"] = kv[1];memo}
         | 
| 19 | 
            +
                    # Platform requires X-Chef-Version header
         | 
| 20 | 
            +
                    version = { "X-Chef-Version" => Spice.chef_version }
         | 
| 21 | 
            +
                    signed.merge!(version)      
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  private
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def load_signing_key
         | 
| 27 | 
            +
                    begin
         | 
| 28 | 
            +
                      raw_key = File.read(key_file).strip
         | 
| 29 | 
            +
                    rescue SystemCallError, IOError => e
         | 
| 30 | 
            +
                      raise IOError, "Unable to read #{key_file}"
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                    assert_valid_key_format!(raw_key)
         | 
| 33 | 
            +
                    @key = OpenSSL::PKey::RSA.new(raw_key)
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def assert_valid_key_format!(raw_key)
         | 
| 37 | 
            +
                    unless (raw_key =~ /\A-----BEGIN RSA PRIVATE KEY-----$/) &&
         | 
| 38 | 
            +
                       (raw_key =~ /^-----END RSA PRIVATE KEY-----\Z/)
         | 
| 39 | 
            +
                      msg = "The file #{key_file} does not contain a correctly formatted private key.\n"
         | 
| 40 | 
            +
                      msg << "The key file should begin with '-----BEGIN RSA PRIVATE KEY-----' and end with '-----END RSA PRIVATE KEY-----'"
         | 
| 41 | 
            +
                      raise ArgumentError, msg
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                  
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
            end
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            module Spice
         | 
| 2 | 
            +
              class Connection
         | 
| 3 | 
            +
                module Clients
         | 
| 4 | 
            +
                  def clients(options={})
         | 
| 5 | 
            +
                    connection.search('client', options)
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def client(name)
         | 
| 9 | 
            +
                    attributes = connection.get("/clients/#{name}").body
         | 
| 10 | 
            +
                    Spice::Client.new(attributes)
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                end # module Clients
         | 
| 14 | 
            +
              end # class Connection
         | 
| 15 | 
            +
            end # module Spice
         | 
| @@ -0,0 +1,42 @@ | |
| 1 | 
            +
            module Spice
         | 
| 2 | 
            +
              class Connection
         | 
| 3 | 
            +
                module Cookbooks
         | 
| 4 | 
            +
                  def cookbooks(options={})
         | 
| 5 | 
            +
                    if Gem::Version.new(Spice.chef_version) >= Gem::Version.new("0.10.0")
         | 
| 6 | 
            +
                      cookbooks = []
         | 
| 7 | 
            +
                      connection.get("/cookbooks").body.each_pair do |key, value|
         | 
| 8 | 
            +
                        versions = value['versions'].map{ |v| v['version'] }
         | 
| 9 | 
            +
                        cookbooks << Spice::Cookbook.new(:name => key, :versions => versions)
         | 
| 10 | 
            +
                      end
         | 
| 11 | 
            +
                      cookbooks
         | 
| 12 | 
            +
                    else
         | 
| 13 | 
            +
                      connection.get("/cookbooks").body.keys.map do |cookbook|
         | 
| 14 | 
            +
                        attributes = connection.get("/cookbooks/#{cookbook}").body.to_a[0]
         | 
| 15 | 
            +
                        Spice::Cookbook.new(:name => attributes[0], :versions => attributes[1])
         | 
| 16 | 
            +
                      end
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def cookbook(name)
         | 
| 21 | 
            +
                    if Gem::Version.new(Spice.chef_version) >= Gem::Version.new("0.10.0")
         | 
| 22 | 
            +
                      cookbook = connection.get("/cookbooks/#{name}").body
         | 
| 23 | 
            +
                      puts cookbook[name]
         | 
| 24 | 
            +
                      versions = cookbook[name]['versions'].map{ |v| v['version'] }
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                      Spice::Cookbook.new(:name => name, :versions => versions)
         | 
| 27 | 
            +
                    else
         | 
| 28 | 
            +
                      connection.get("/cookbooks").body.keys.map do |cookbook|
         | 
| 29 | 
            +
                        attributes = connection.get("/cookbooks/#{cookbook}").body.to_a[0]
         | 
| 30 | 
            +
                        Spice::Cookbook.new(:name => attributes[0], :versions => attributes[1])
         | 
| 31 | 
            +
                      end
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                  
         | 
| 35 | 
            +
                  def cookbook_version(name, version)
         | 
| 36 | 
            +
                    attributes = connection.get("/cookbooks/#{name}/#{version}").body
         | 
| 37 | 
            +
                    Spice::CookbookVersion.new(attributes)
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                  
         | 
| 40 | 
            +
                end # module Cookbooks
         | 
| 41 | 
            +
              end # class Connection
         | 
| 42 | 
            +
            end # module Spice
         | 
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            module Spice
         | 
| 2 | 
            +
              class Connection
         | 
| 3 | 
            +
                module DataBags
         | 
| 4 | 
            +
                  def data_bags
         | 
| 5 | 
            +
                    connection.get("/data").body.keys.map do |data_bag|
         | 
| 6 | 
            +
                      items = connection.search(data_bag)
         | 
| 7 | 
            +
                      Spice::DataBag.new(:name => data_bag, :items => items)
         | 
| 8 | 
            +
                    end
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  alias :data :data_bags
         | 
| 12 | 
            +
                  
         | 
| 13 | 
            +
                  def data_bag(name)
         | 
| 14 | 
            +
                    items = connection.search(name)
         | 
| 15 | 
            +
                    Spice::DataBag.new(:name => name, :items => items)
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def data_bag_item(name, id)
         | 
| 19 | 
            +
                    data = connection.get("/data/#{name}/#{id}")
         | 
| 20 | 
            +
                    data.delete('id')
         | 
| 21 | 
            +
                    Spice::DataBagItem.new(:_id => id, :data => data, :name => name)
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  private
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def get_data_bag_items(name)
         | 
| 27 | 
            +
                    connection.get("/data/#{name}").body.keys.map do |id|
         | 
| 28 | 
            +
                      data = connection.get("/data/#{name}/#{id}").body
         | 
| 29 | 
            +
                      Spice::DataBagItem.new(:_id => id, :data => data, :name => name)
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                end # module DataBags
         | 
| 34 | 
            +
              end # class Connection
         | 
| 35 | 
            +
            end # module Spice
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            module Spice
         | 
| 2 | 
            +
              class Connection
         | 
| 3 | 
            +
                module Environments
         | 
| 4 | 
            +
                  def environments(options={})
         | 
| 5 | 
            +
                    connection.search('environment', options)
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def environment(name)
         | 
| 9 | 
            +
                    attributes = connection.get("/environments/#{name}").body
         | 
| 10 | 
            +
                    attributes['attrs'] = attributes.delete('attributes')
         | 
| 11 | 
            +
                    Spice::Environment.new(attributes)
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                end # module Environments
         | 
| 15 | 
            +
              end # class Connection
         | 
| 16 | 
            +
            end # module Spice
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            module Spice
         | 
| 2 | 
            +
              class Connection
         | 
| 3 | 
            +
                module Nodes
         | 
| 4 | 
            +
                  def nodes(options={})
         | 
| 5 | 
            +
                    connection.search('node', options)
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def node(name)
         | 
| 9 | 
            +
                    node_attributes = connection.get("/nodes/#{name}").body
         | 
| 10 | 
            +
                    Spice::Node.new(node_attributes)
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                end # module Nodes
         | 
| 14 | 
            +
              end # class Connection
         | 
| 15 | 
            +
            end # module Spice
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            module Spice
         | 
| 2 | 
            +
              class Connection
         | 
| 3 | 
            +
                module Roles
         | 
| 4 | 
            +
                  def roles(options={})
         | 
| 5 | 
            +
                    connection.search('role', options)
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def role(name)
         | 
| 9 | 
            +
                    attributes = connection.get("/roles/#{name}").body
         | 
| 10 | 
            +
                    Spice::Role.new(attributes)
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                end # module Roles
         | 
| 14 | 
            +
              end # class Connection
         | 
| 15 | 
            +
            end # module Spice
         | 
| @@ -0,0 +1,49 @@ | |
| 1 | 
            +
            require 'cgi'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Spice
         | 
| 4 | 
            +
              class Connection
         | 
| 5 | 
            +
                module Search
         | 
| 6 | 
            +
                  def search(index, options={})
         | 
| 7 | 
            +
                    options = {:q => options} if options.is_a? String
         | 
| 8 | 
            +
                    options.symbolize_keys!
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    options[:q] ||= '*:*'
         | 
| 11 | 
            +
                    options[:sort] ||= "X_CHEF_id_CHEF_X asc"
         | 
| 12 | 
            +
                    options[:start] ||= 0
         | 
| 13 | 
            +
                    options[:rows] ||= 1000
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    # clean up options hash
         | 
| 16 | 
            +
                    options.delete_if{|k,v| !%w(q sort start rows).include?(k.to_s)}
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    params = options.collect{ |k, v| "#{k}=#{CGI::escape(v.to_s)}"}.join("&")
         | 
| 19 | 
            +
                    case index
         | 
| 20 | 
            +
                    when 'node'
         | 
| 21 | 
            +
                      connection.get("/search/#{CGI::escape(index.to_s)}?#{params}").body['rows'].map do |node|
         | 
| 22 | 
            +
                        Spice::Node.new(node)
         | 
| 23 | 
            +
                      end
         | 
| 24 | 
            +
                    when 'role'
         | 
| 25 | 
            +
                      connection.get("/search/#{CGI::escape(index.to_s)}?#{params}").body['rows'].map do |role|
         | 
| 26 | 
            +
                        Spice::Role.new(role)
         | 
| 27 | 
            +
                      end
         | 
| 28 | 
            +
                    when 'client'
         | 
| 29 | 
            +
                      connection.get("/search/#{CGI::escape(index.to_s)}?#{params}").body['rows'].map do |client|
         | 
| 30 | 
            +
                        Spice::Client.new(client)
         | 
| 31 | 
            +
                      end
         | 
| 32 | 
            +
                    when 'environment'
         | 
| 33 | 
            +
                      connection.get("/search/#{CGI::escape(index.to_s)}?#{params}").body['rows'].map do |env|
         | 
| 34 | 
            +
                        env['attrs'] = env.delete('attributes')
         | 
| 35 | 
            +
                        Spice::Environment.new(env)
         | 
| 36 | 
            +
                      end
         | 
| 37 | 
            +
                    else
         | 
| 38 | 
            +
                      # assume it's a data bag
         | 
| 39 | 
            +
                      connection.get("/search/#{CGI::escape(index.to_s)}?#{params}").body['rows'].map do |db|
         | 
| 40 | 
            +
                        data = db['raw_data']
         | 
| 41 | 
            +
                        id = data.delete('id')
         | 
| 42 | 
            +
                        Spice::DataBagItem.new(:_id => id, :data => data, :name => index)
         | 
| 43 | 
            +
                      end
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
                  end # def search
         | 
| 46 | 
            +
                  
         | 
| 47 | 
            +
                end # module Search
         | 
| 48 | 
            +
              end # class Connection
         | 
| 49 | 
            +
            end # module Spice
         | 
    
        data/lib/spice/cookbook.rb
    CHANGED
    
    | @@ -1,33 +1,16 @@ | |
| 1 | 
            +
            require 'spice/persistence'
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module Spice
         | 
| 2 | 
            -
              class Cookbook | 
| 3 | 
            -
                 | 
| 4 | 
            -
             | 
| 5 | 
            -
                 | 
| 6 | 
            -
                
         | 
| 7 | 
            -
                 | 
| 8 | 
            -
             | 
| 9 | 
            -
                 | 
| 10 | 
            -
                
         | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
                  name = options.delete(:name)
         | 
| 14 | 
            -
                  connection.get("/cookbooks/#{name}")
         | 
| 15 | 
            -
                end
         | 
| 16 | 
            -
                
         | 
| 17 | 
            -
                def self.create(options={})
         | 
| 18 | 
            -
                  connection.post("/cookbooks", options)
         | 
| 19 | 
            -
                end
         | 
| 20 | 
            -
                
         | 
| 21 | 
            -
                def self.update(options={})
         | 
| 22 | 
            -
                  raise ArgumentError, "Option :name must be present" unless options[:name]
         | 
| 23 | 
            -
                  name = options.delete(:name)
         | 
| 24 | 
            -
                  connection.put("/cookbooks/#{name}", options)
         | 
| 25 | 
            -
                end
         | 
| 26 | 
            -
                
         | 
| 27 | 
            -
                def self.delete(options={})
         | 
| 28 | 
            -
                  raise ArgumentError, "Option :name must be present" unless options[:name]
         | 
| 29 | 
            -
                  name = options.delete(:name)
         | 
| 30 | 
            -
                  connection.delete("/cookbooks/#{name}", options)
         | 
| 31 | 
            -
                end
         | 
| 4 | 
            +
              class Cookbook
         | 
| 5 | 
            +
                include Toy::Store
         | 
| 6 | 
            +
                include Spice::Persistence
         | 
| 7 | 
            +
                extend Spice::Persistence
         | 
| 8 | 
            +
                store :memory, {}
         | 
| 9 | 
            +
                endpoint "cookbooks"
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                attribute :name, String
         | 
| 12 | 
            +
                attribute :versions, Array, :default => []
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                validates_presence_of :name
         | 
| 32 15 | 
             
              end
         | 
| 33 16 | 
             
            end
         |