├── .bundle └── config ├── .document ├── .documentup.json ├── .gitignore ├── .rspec ├── .travis.yml ├── CHANGELOG.md ├── Gemfile ├── Guardfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── Rakefile.doc ├── TODO.md ├── lib ├── spice.rb └── spice │ ├── authentication.rb │ ├── base.rb │ ├── client.rb │ ├── config.rb │ ├── connection.rb │ ├── connection │ ├── clients.rb │ ├── cookbooks.rb │ ├── data_bags.rb │ ├── environments.rb │ ├── nodes.rb │ ├── roles.rb │ └── search.rb │ ├── cookbook.rb │ ├── cookbook_version.rb │ ├── core_ext │ ├── array.rb │ ├── enumerable.rb │ ├── hash.rb │ └── mash.rb │ ├── data_bag.rb │ ├── data_bag_item.rb │ ├── environment.rb │ ├── error.rb │ ├── identity_map.rb │ ├── node.rb │ ├── request.rb │ ├── request │ └── auth.rb │ ├── response │ ├── client_error.rb │ └── parse_json.rb │ ├── role.rb │ └── version.rb ├── spec ├── fixtures │ ├── client.pem │ ├── clients │ │ ├── create.json │ │ ├── index.json │ │ ├── reregister.json │ │ ├── show.json │ │ └── update.json │ ├── cookbook_versions │ │ ├── show.json │ │ └── update.json │ ├── cookbooks │ │ ├── index-0.10.json │ │ ├── index-0.9.json │ │ ├── show-0.10.json │ │ ├── show-apache2-0.9.json │ │ └── show-unicorn-0.9.json │ ├── data_bag_items │ │ ├── create.json │ │ ├── show.json │ │ └── update.json │ ├── data_bags │ │ ├── create.json │ │ ├── index.json │ │ └── show.json │ ├── environments │ │ ├── cookbook.json │ │ ├── cookbooks.json │ │ ├── create.json │ │ ├── delete.json │ │ ├── index.json │ │ ├── show.json │ │ └── update.json │ ├── nodes │ │ ├── cookbooks.json │ │ ├── create.json │ │ ├── delete.json │ │ ├── index.json │ │ ├── show.json │ │ └── update.json │ ├── roles │ │ ├── create.json │ │ ├── delete.json │ │ ├── index.json │ │ ├── show.json │ │ └── update.json │ └── search │ │ ├── client.json │ │ ├── data_bag.json │ │ ├── environment.json │ │ ├── node.json │ │ └── role.json ├── spec_helper.rb ├── spice │ ├── authentication_spec.rb │ ├── base_spec.rb │ ├── client_spec.rb │ ├── config_spec.rb │ ├── connection │ │ ├── clients_spec.rb │ │ ├── cookbooks_spec.rb │ │ └── data_bags_spec.rb │ ├── connection_spec.rb │ ├── cookbook_spec.rb │ ├── data_bag_item_spec.rb │ ├── data_bag_spec.rb │ ├── environment_spec.rb │ ├── node_spec.rb │ └── role_spec.rb ├── spice_spec.rb └── support │ └── helpers.rb └── spice.gemspec /.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /.document: -------------------------------------------------------------------------------- 1 | lib/**/*.rb 2 | bin/* 3 | - 4 | features/**/*.feature 5 | LICENSE.txt 6 | -------------------------------------------------------------------------------- /.documentup.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Spice", 3 | "twitter": [ 4 | "iamdanryan" 5 | ], 6 | "google_analytics": "UA-8670854-16", 7 | "travis": true, 8 | "issues": true 9 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | pkg/* 3 | examples/* 4 | doc/* 5 | doc/**/* 6 | .yardoc/**/* 7 | .yardoc/* 8 | Gemfile.lock 9 | fixture.rb 10 | coverage 11 | wiki 12 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color --format doc 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 1.8.7 4 | - 1.9.2 5 | - 1.9.3 6 | - jruby-18mode # JRuby in 1.8 mode 7 | - jruby-19mode # JRuby in 1.9 mode 8 | - rbx-18mode 9 | - rbx-19mode 10 | bundler_args: --without doc local 11 | script: "bundle exec rake spec:ci" 12 | branches: 13 | only: 14 | - master -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Release Notes - Spice - Version 1.0.6 2 | 3 | * Passing a string for the client key is now valid, as it should have been for a while. 4 | 5 | # Release Notes - Spice - Version 1.0.5 6 | 7 | * `mixlib-authentication` introduced a breaking change in their bump to 1.3.0. Pinned mixlib-auth to 1.1.4 so Spice works again. 8 | * `Spice.read_key_file` can now parse a relative path 9 | 10 | # Release Notes - Spice - Version 1.0.4 11 | 12 | * Fix hosted Chef usage - https://github.com/danryan/spice/pull/23 13 | * 14 | 15 | # Release Notes - Spice - Version 1.0.3 16 | 17 | * Fix create_node method. This was throwing a Spice::Error::NotFound exception because it was generating a wrong path (/nodes//nodes/...). The get_node() call at the end of the method returns the full node object. If just the attributes were used to create the node object, many things were missing. 18 | 19 | # Release Notes - Spice - Version 1.0.2 20 | 21 | * Fixed issue with bad variable when checking the format of a client key. 22 | 23 | # Release Notes - Spice - Version 1.0.1 24 | 25 | * Fixed an incompatibility with JRuby and Yajl by switching to MultiJson. 26 | 27 | # Release Notes - Spice - Version 1.0.0 28 | 29 | * Note: this version of Spice is a major version bump, which means it is *not* backwards-compatible with previous versions. 30 | 31 | ## Removed 32 | 33 | * Old-style connections are no longer supported. Please use the connection string like Chef uses (ex. http://chef.example.com:4000) 34 | 35 | ## Improvement 36 | 37 | * Complete rewrite! Returned results are now full interactive objects and not just hashes. Create, update, and destroy resources with an ORM-like interface. 38 | 39 | # Release Notes - Spice - Version 0.5.0 40 | 41 | ## Bug 42 | 43 | * url_path was not being reset 44 | * added `chef_version` config variable to fix some people's issues with require `chef/version` 45 | 46 | ## Improvement 47 | 48 | * Node support! 49 | 50 | # Release Notes - Spice - Version 0.3.0 51 | 52 | ## Bug 53 | 54 | * N/A 55 | 56 | ## Improvement 57 | 58 | * Improved specification of DataBag class 59 | * Added additional documentation to DataBag class 60 | * Add CHANGELOG 61 | 62 | ## New Feature 63 | 64 | * Data bag items can now be created 65 | 66 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec 4 | 5 | group :test do 6 | gem 'rake' 7 | end 8 | 9 | group :doc do 10 | gem 'yard' 11 | gem 'redcarpet' 12 | end 13 | 14 | group :local do 15 | gem 'guard', '>= 1.0.1' 16 | gem 'guard-rspec', '>= 0.6.0' 17 | gem 'guard-spork', '>= 0.5.2' 18 | gem 'rb-fsevent', '>= 0.9.0' 19 | gem 'growl', '>= 1.0.3' 20 | gem 'simplecov' 21 | end 22 | 23 | # JSON 24 | # gem 'json', :platform => :jruby 25 | gem 'yajl-ruby', :platform => [ :ruby_18, :ruby_19 ] 26 | 27 | # OpenSSL 28 | gem 'jruby-openssl', :platform => :jruby 29 | 30 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | guard 'rspec', :version => 2, :cli => "--format documentation" do 2 | watch(%r{^spec/.+_spec\.rb$}) 3 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } 4 | watch('spec/spec_helper.rb') { "spec" } 5 | end 6 | 7 | guard 'spork' do 8 | watch('spec/spec_helper.rb') 9 | end 10 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Dan Ryan 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spice Deprecation Notice 2 | 3 | Spice is no longer being actively maintained. 4 | 5 | Consider using [Ridley](https://github.com/RiotGames/ridley) instead. 6 | 7 | # Spice - Chef API Wrapper 8 | 9 | Spice lets you easily integrate your apps with a [Chef](http://opscode.com/chef) server. Spice provides support for the [Chef API](http://wiki.opscode.com/display/chef/Server+API) 10 | 11 | ### Installation 12 | 13 | Install this beast via Rubygems: 14 | 15 | gem install spice 16 | 17 | Of course, You can always grab the source from http://github.com/danryan/spice. 18 | 19 | ### Usage 20 | 21 | For configuration and usage patterns, [check out the wiki.](https://github.com/danryan/spice/wiki) 22 | 23 | ### Deprecation notice 24 | 25 | Explicitly setting a `host`, `port`, and `scheme` value has been removed in favor of setting a single variable, `server_url`, which matches the format of Chef's client config parameter, `chef_server_url`. 26 | 27 | ### Contributors 28 | 29 | * [Ian Meyer](https://github.com/imeyer) - Opscode Platform support 30 | * [Holger Just](https://github.com/meineerde) - Search functionality 31 | * [Sean Porter](https://github.com/portertech) - Platform bug fixes 32 | 33 | ### Hat tip 34 | 35 | The design and organization of Spice is very heavily inspired by the [Twitter gem](http://github.com/jnunemaker/twitter). Mad props to those folks. 36 | 37 | ### Contributing to spice 38 | 39 | * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet 40 | * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it 41 | * Fork the project 42 | * Start a feature/bugfix branch 43 | * Commit and push until you are happy with your contribution 44 | * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. 45 | * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it. 46 | 47 | ### Copyright 48 | 49 | Copyright (c) 2011 Dan Ryan. See LICENSE.txt for 50 | further details. 51 | 52 | Chef and related trademarks are Copyright (c) 2008-2012 Opscode, Inc. Chef is released under the Apache 2.0 license. 53 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | 3 | require 'rspec/core/rake_task' 4 | 5 | # task :default => :spec 6 | # desc "Run specs" 7 | # RSpec::Core::RakeTask.new do |task| 8 | # task.pattern = "spec/**/*_spec.rb" 9 | # end 10 | 11 | desc "Run specs" 12 | RSpec::Core::RakeTask.new(:spec) {|t|} 13 | 14 | namespace :spec do 15 | desc "Clean up rbx compiled files and run spec suite" 16 | RSpec::Core::RakeTask.new(:ci) { |t| Dir.glob("**/*.rbc").each {|f| FileUtils.rm_f(f) } } 17 | end 18 | 19 | desc "Run guard" 20 | task :guard do 21 | sh %{bundle exec guard start} 22 | end 23 | 24 | desc "Run spork" 25 | task :spork do 26 | sh %{bundle exec spork} 27 | end 28 | -------------------------------------------------------------------------------- /Rakefile.doc: -------------------------------------------------------------------------------- 1 | Bundler.require(:doc) 2 | desc "Generate documentation" 3 | YARD::Rake::YardocTask.new do |t| 4 | t.files = [ 'lib/**/*.rb' ] 5 | t.options = [ "--markup-provider=redcarpet", "--markup=markdown" ] 6 | end 7 | 8 | desc "Generate docs" 9 | task :doc do 10 | sh %{bundle exec rake yard} 11 | end -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | * Refactor classes that inherit from Spice::Chef to take config values 2 | * Add support for data bag item creation 3 | * implement GET /cookbooks?num_versions=all -------------------------------------------------------------------------------- /lib/spice.rb: -------------------------------------------------------------------------------- 1 | require 'spice/core_ext/mash' 2 | 3 | require 'spice/config' 4 | require 'spice/connection' 5 | 6 | module Spice 7 | extend Spice::Config 8 | 9 | class << self 10 | # Convience alias for Spice::Connection.new 11 | # 12 | # return [Spice::Connection] 13 | def new(options=Mash.new) 14 | Spice::Connection.new(options) 15 | end # def new 16 | 17 | # Delegate methods to Spice::Connection 18 | def method_missing(method, *args, &block) 19 | return super unless new.respond_to?(method) 20 | new.send(method, *args, &block) 21 | end # def method_missing 22 | 23 | def respond_to?(method, include_private=false) 24 | new.respond_to?(method, include_private) || super(method, include_private) 25 | end # def respond_to? 26 | 27 | def mock 28 | Spice.server_url = 'http://localhost:4000/organizations/spice' 29 | Spice.client_name = "testclient" 30 | Spice.client_key = Spice.read_key_file(File.expand_path("../../spec/fixtures/client.pem", __FILE__)) 31 | Spice.chef_version = "0.10.10" 32 | self 33 | end # def mock 34 | 35 | def read_key_file(path) 36 | key_file_path = File.expand_path(path) 37 | 38 | begin 39 | raw_key = File.read(key_file_path).strip 40 | rescue SystemCallError, IOError => e 41 | raise IOError, "Unable to read #{key_file_path}" 42 | end 43 | 44 | begin_rsa = "-----BEGIN RSA PRIVATE KEY-----" 45 | end_rsa = "-----END RSA PRIVATE KEY-----" 46 | 47 | unless (raw_key =~ /\A#{begin_rsa}$/) && (raw_key =~ /^#{end_rsa}\Z/) 48 | msg = "The file #{key_file_path} is not a properly formatted private key.\n" 49 | msg << "It must contain '#{begin_rsa}' and '#{end_rsa}'" 50 | raise ArgumentError, msg 51 | end 52 | return OpenSSL::PKey::RSA.new(raw_key) 53 | end # def read_key_file 54 | 55 | end # class << self 56 | end # module Spice 57 | 58 | -------------------------------------------------------------------------------- /lib/spice/authentication.rb: -------------------------------------------------------------------------------- 1 | require 'openssl' 2 | # require 'mixlib/authentication' 3 | require 'mixlib/authentication/signedheaderauth' 4 | 5 | module Spice 6 | module Authentication 7 | 8 | def signature_headers(method, path, json_body=nil) 9 | uri = URI(server_url) 10 | 11 | params = { 12 | :http_method => method, 13 | :path => path, 14 | :body => json_body || "", 15 | :host => "#{uri.host}:#{uri.port}", 16 | :timestamp => Time.now.utc.iso8601, 17 | :user_id => client_name 18 | } 19 | 20 | signing_object = Mixlib::Authentication::SignedHeaderAuth.signing_object(params) 21 | signed_headers = signing_object.sign(client_key) 22 | 23 | # Platform requires X-Chef-Version header 24 | signed_headers['X-Chef-Version'] = chef_version 25 | # signed_headers['Content-Length'] = json_body.bytesize.to_s if json_body 26 | signed_headers 27 | end # def signature_headers 28 | 29 | end # module Authentication 30 | end # module Spice 31 | -------------------------------------------------------------------------------- /lib/spice/base.rb: -------------------------------------------------------------------------------- 1 | require 'spice/identity_map' 2 | 3 | module Spice 4 | class Base 5 | attr_accessor :attrs 6 | alias :to_hash :attrs 7 | 8 | @@identity_map = IdentityMap.new 9 | 10 | def self.identity_map 11 | @@identity_map 12 | end # def self.identity_map 13 | 14 | # Define methods that retrieve the value from an initialized instance variable Hash, using the attribute as a key 15 | # 16 | # @overload self.attr_reader(attr) 17 | # @param attr [Symbol] 18 | # @overload self.attr_reader(attrs) 19 | # @param attrs [Array] 20 | def self.attr_reader(*attrs) 21 | attrs.each do |attribute| 22 | class_eval do 23 | define_method attribute do 24 | @attrs[attribute.to_s] 25 | end 26 | define_method "#{attribute}=" do |value| 27 | @attrs[attribute.to_s] = value 28 | end 29 | end 30 | end 31 | end # def self.attr_reader 32 | 33 | def self.get(attrs=Mash.new) 34 | @@identity_map[self] ||= {} 35 | if attrs['name'] 36 | @@identity_map[self][attrs['name']] && @@identity_map[self][attrs['name']].update(attrs) 37 | elsif attrs['id'] 38 | @@identity_map[self][attrs['id']] && @@identity_map[self][attrs['id']].update(attrs) 39 | else 40 | @@identity_map[self][Marshal.dump(attrs)] 41 | end 42 | end # def self.get 43 | 44 | # Retrieve an object from the identity map or initialize a new object 45 | def self.get_or_new(attrs=Mash.new) 46 | self.get(attrs) || self.new(attrs) 47 | end # def self.get_or_new 48 | 49 | # Initializes a new object 50 | # 51 | # @param attrs [Hash] 52 | # @return [Spice::Base] 53 | def initialize(attrs=Mash.new) 54 | self.class.attr_reader *attrs.keys 55 | attrs.stringify_keys! 56 | if attrs['name'] 57 | self.update(attrs) 58 | @@identity_map[self.class] ||= {} 59 | @@identity_map[self.class][attrs['name']] = self 60 | elsif attrs['id'] 61 | self.update(attrs) 62 | @@identity_map[self.class] ||= {} 63 | @@identity_map[self.class][attrs['id']] = self 64 | else 65 | self.update(attrs) 66 | @@identity_map[self.class] ||= {} 67 | @@identity_map[self.class][Marshal.dump(attrs)] = self 68 | end 69 | end # def initialize 70 | 71 | # Fetches an attribute of an object using hash notation 72 | # 73 | # @param method [String, Symbol] Message to send to the object 74 | def [](method) 75 | self.__send__(method.to_sym) 76 | rescue NoMethodError 77 | nil 78 | end # def [] 79 | 80 | # Update the attributes of an object 81 | # 82 | # @param attrs [Hash] 83 | # @return [Spice::Base] 84 | def update(attrs) 85 | @attrs = attrs 86 | self 87 | end # def update 88 | 89 | # @return [Integer] 90 | def id 91 | @attrs['id'] 92 | end # def id 93 | 94 | # @return [String] 95 | def name 96 | @attrs['name'] 97 | end # def name 98 | 99 | def keys 100 | @attrs.keys 101 | end # def keys 102 | 103 | end 104 | end -------------------------------------------------------------------------------- /lib/spice/client.rb: -------------------------------------------------------------------------------- 1 | require 'spice/base' 2 | 3 | module Spice 4 | class Client < Base 5 | attr_reader :name, :public_key, :private_key, :_rev, :admin 6 | 7 | def initialize(attrs=Mash.new) 8 | super 9 | @attrs['json_class'] ||= "Chef::ApiClient" 10 | @attrs['chef_type'] ||= 'client' 11 | @attrs['admin'] ||= false 12 | end 13 | 14 | end 15 | end -------------------------------------------------------------------------------- /lib/spice/config.rb: -------------------------------------------------------------------------------- 1 | require 'faraday' 2 | require 'spice/response/parse_json' 3 | require 'spice/response/client_error' 4 | require 'spice/version' 5 | 6 | module Spice 7 | module Config 8 | 9 | # The default Chef server URL 10 | DEFAULT_SERVER_URL = "http://localhost:4000" 11 | 12 | # The default Chef version (changing this will enable disable certain features) 13 | DEFAULT_CHEF_VERSION = "0.10.10" 14 | 15 | # The default Spice User-Agent header 16 | DEFAULT_USER_AGENT = "Spice #{Spice::VERSION}" 17 | 18 | # Default connection options 19 | DEFAULT_CONNECTION_OPTIONS = {} 20 | 21 | # Default client name 22 | DEFAULT_CLIENT_NAME = "" 23 | 24 | # Default key file 25 | DEFAULT_CLIENT_KEY = "" 26 | 27 | # An array of valid config options 28 | 29 | VALID_OPTIONS_KEYS = [ 30 | :server_url, 31 | :client_name, 32 | :client_key, 33 | :chef_version, 34 | :user_agent, 35 | :connection_options, 36 | :middleware 37 | ] 38 | 39 | # Default middleware stack 40 | DEFAULT_MIDDLEWARE = Proc.new do |builder| 41 | builder.use Spice::Response::ParseJSON 42 | builder.use Spice::Response::ClientError 43 | builder.adapter Faraday.default_adapter 44 | end 45 | 46 | VALID_OPTIONS_KEYS.each do |key| 47 | attr_accessor key 48 | end 49 | 50 | # Reset all config options to default when the module is extended 51 | def self.extended(base) 52 | base.reset 53 | end # def self.extended 54 | 55 | # Convenience method to configure Spice in a block 56 | # @example Configuring spice 57 | # Spice.setup do |s| 58 | # s.server_url = "http://chef.example.com:4000" 59 | # s.client_name = "admin" 60 | # s.client_key = Spice.read_key_file("/path/to/key_file.pem") 61 | # end 62 | # @yieldparam Spice 63 | # @yieldreturn Spice 64 | def setup 65 | yield self 66 | self 67 | end # def setup 68 | 69 | # Create an options hash from valid options keys 70 | def options 71 | options = {} 72 | VALID_OPTIONS_KEYS.each{|k| options[k] = send(k)} 73 | options 74 | end # def options 75 | 76 | # Reset all config options to their defaults 77 | def reset 78 | self.user_agent = DEFAULT_USER_AGENT 79 | self.server_url = DEFAULT_SERVER_URL 80 | self.chef_version = DEFAULT_CHEF_VERSION 81 | self.client_name = DEFAULT_CLIENT_NAME 82 | self.client_key = DEFAULT_CLIENT_KEY 83 | self.connection_options = DEFAULT_CONNECTION_OPTIONS 84 | self.middleware = DEFAULT_MIDDLEWARE 85 | self 86 | end # def reset 87 | 88 | end # module Config 89 | end # module Spice -------------------------------------------------------------------------------- /lib/spice/connection.rb: -------------------------------------------------------------------------------- 1 | require 'spice/config' 2 | require 'spice/request' 3 | require 'spice/authentication' 4 | require 'spice/error' 5 | 6 | require 'spice/core_ext/enumerable' 7 | 8 | require 'spice/connection/search' 9 | require 'spice/connection/clients' 10 | require 'spice/connection/cookbooks' 11 | require 'spice/connection/data_bags' 12 | require 'spice/connection/environments' 13 | require 'spice/connection/nodes' 14 | require 'spice/connection/roles' 15 | 16 | require 'spice/client' 17 | require 'spice/cookbook' 18 | require 'spice/cookbook_version' 19 | require 'spice/data_bag' 20 | require 'spice/data_bag_item' 21 | require 'spice/environment' 22 | require 'spice/node' 23 | require 'spice/role' 24 | 25 | module Spice 26 | class Connection 27 | include Spice::Connection::Clients 28 | include Spice::Connection::Cookbooks 29 | include Spice::Connection::DataBags 30 | include Spice::Connection::Environments 31 | include Spice::Connection::Nodes 32 | include Spice::Connection::Roles 33 | include Spice::Connection::Search 34 | include Spice::Request 35 | include Spice::Authentication 36 | 37 | # @private 38 | 39 | Config::VALID_OPTIONS_KEYS.each do |key| 40 | attr_accessor key 41 | end 42 | 43 | def initialize(attrs=Mash.new) 44 | attrs = Spice.options.merge(attrs) 45 | 46 | unless attrs[:client_key].is_a?(OpenSSL::PKey::RSA) 47 | attrs[:client_key] = OpenSSL::PKey::RSA.new(attrs[:client_key]) 48 | end 49 | 50 | Config::VALID_OPTIONS_KEYS.each do |key| 51 | instance_variable_set("@#{key}".to_sym, attrs[key]) 52 | end 53 | end # def initialize 54 | 55 | def sign_on_redirect 56 | @sign_on_redirect ||= true 57 | end # def sign_on_redirect 58 | 59 | def sign_request 60 | @sign_request ||= true 61 | end # def sign_request 62 | 63 | end # class Connection 64 | end # module Spice -------------------------------------------------------------------------------- /lib/spice/connection/clients.rb: -------------------------------------------------------------------------------- 1 | module Spice 2 | class Connection 3 | module Clients 4 | # A collection of clients 5 | # @param [Hash] options An options hash that is passed to {Search#search} 6 | # @return [Array, Array] 7 | # @see Search#search 8 | # @example Retrieve all clients with names that begin with "app" 9 | # Spice.clients(:q => "name:app*") 10 | def clients(options=Mash.new) 11 | search('client', options) 12 | end # def clients 13 | 14 | # Retrieve a single client 15 | # @param [String] name The client name 16 | # @return [Spice::Client] 17 | # @raise [Spice::Error::NotFound] raised when client does not exist 18 | # @example Retrieve the client named "admin" 19 | # Spice.client("name") 20 | def client(name) 21 | attributes = get("/clients/#{name}") 22 | Spice::Client.get_or_new(attributes) 23 | end # def client 24 | 25 | def create_client(params=Mash.new) 26 | attributes = post("/clients", params) 27 | Spice::Client.get_or_new(attributes) 28 | end # def create_client 29 | 30 | def update_client(name, params=Mash.new) 31 | attributes = put("/clients/#{name}", params) 32 | Spice::Client.get_or_new(attributes) 33 | end # def update_client 34 | 35 | def delete_client(name) 36 | client = delete("/clients/#{name}") 37 | nil 38 | end # def delete_client 39 | 40 | def reregister_client(name) 41 | attributes = put("/clients/#{name}", :private_key => true) 42 | Spice::Client.get_or_new(attributes) 43 | end # def reregister_client 44 | 45 | end # module Clients 46 | end # class Connection 47 | end # module Spice 48 | -------------------------------------------------------------------------------- /lib/spice/connection/cookbooks.rb: -------------------------------------------------------------------------------- 1 | module Spice 2 | class Connection 3 | module Cookbooks 4 | # Retrieve an array of all cookbooks 5 | # @return [Array] 6 | # @example Retrieve all cookbooks with versions 7 | # Spice.cookbooks 8 | def cookbooks 9 | if Gem::Version.new(Spice.chef_version) >= Gem::Version.new("0.10.0") 10 | cookbooks = [] 11 | get("/cookbooks?num_versions=all").each_pair do |key, value| 12 | versions = value['versions'].map{ |v| v['version'] } 13 | cookbooks << Spice::Cookbook.get_or_new(:name => key, :versions => versions) 14 | end 15 | cookbooks 16 | else 17 | get("/cookbooks").keys.map do |cookbook| 18 | cb = get("/cookbooks/#{cookbook}") 19 | Spice::Cookbook.get_or_new(:name => cookbook, :versions => cb[cookbook]) 20 | end 21 | end 22 | end # def cookbooks 23 | 24 | # Retrieve a single cookbook 25 | # @param [String] name The name of the cookbook 26 | # @return [Spice::Cookbook] 27 | # @raise [Spice::Error::NotFound] raised when cookbook does not exist 28 | def cookbook(name) 29 | if Gem::Version.new(Spice.chef_version) >= Gem::Version.new("0.10.0") 30 | cookbook = get("/cookbooks/#{name}") 31 | versions = cookbook[name]['versions'].map{ |v| v['version'] } 32 | 33 | Spice::Cookbook.get_or_new(:name => name, :versions => versions) 34 | else 35 | cookbook = get("/cookbooks/#{name}") 36 | Spice::Cookbook.get_or_new(:name => name, :versions => cookbook[name]) 37 | end 38 | end # def cookbook 39 | 40 | # Retrieve a single cookbook version 41 | # @param [String] name The cookbook name 42 | # @param [String] version The cookbook version 43 | # @return [Spice::CookbookVersion] 44 | # @raise [Spice::Error::NotFound] raised when cookbook version does not exist 45 | def cookbook_version(name, version) 46 | attributes = get("/cookbooks/#{name}/#{version}") 47 | duped_attributes = attributes.dup 48 | duped_attributes[:_attributes] = attributes['attributes'] 49 | Spice::CookbookVersion.get_or_new(duped_attributes) 50 | end # def cookbook_version 51 | 52 | def delete_cookbook_version(name, version) 53 | delete("/cookbooks/#{name}/#{version}") 54 | nil 55 | end # def delete_cookbook_version 56 | 57 | end # module Cookbooks 58 | end # class Connection 59 | end # module Spice -------------------------------------------------------------------------------- /lib/spice/connection/data_bags.rb: -------------------------------------------------------------------------------- 1 | module Spice 2 | class Connection 3 | module DataBags 4 | # Retrieve an array of all data bags 5 | # @return [Array] 6 | # @example Retrieve all data bags 7 | # Spice.data_bags 8 | def data_bags 9 | get("/data").keys.map do |data_bag| 10 | items = search(data_bag) 11 | Spice::DataBag.get_or_new(:name => data_bag, :items => items) 12 | end 13 | end # def data_bags 14 | 15 | alias :data :data_bags 16 | 17 | # Retrieve a single data bag and its items 18 | # @param [String] name The data bag name 19 | # @return [Spice::DataBag] 20 | # @raise [Spice::Error::NotFound] raised when data bag does not exist 21 | def data_bag(name) 22 | items = search(name) 23 | Spice::DataBag.get_or_new(:name => name, :items => items) 24 | end # def data_bag 25 | 26 | # Retrieve a single data bag item 27 | # @param [String] name The data bag name 28 | # @param [String] id The data bag item id 29 | # @return [Spice::DataBagItem] 30 | # @raise [Spice::Error::NotFound] raised when data bag item does not exist 31 | def data_bag_item(name, id) 32 | data_bag_item = get("/data/#{name}/#{id}") 33 | Spice::DataBagItem.get_or_new(data_bag_item) 34 | end # def data_bag_item 35 | 36 | def create_data_bag(name) 37 | attributes = post("/data", :name => name) 38 | Spice::DataBag.get_or_new(:name => name, :items => []) 39 | end # def create_data_bag 40 | 41 | def create_data_bag_item(name, params=Mash.new) 42 | attributes = post("/data/#{name}", params) 43 | Spice::DataBagItem.get_or_new(attributes) 44 | end # def create_data_bag_item 45 | 46 | def update_data_bag_item(name, id, params=Mash.new) 47 | params.merge!(:id => id) 48 | attributes = put("/data/#{name}/#{id}", params) 49 | Spice::DataBagItem.get_or_new(attributes) 50 | end # update_data_bag_item 51 | 52 | def delete_data_bag_item(name, id) 53 | delete("/data/#{name}/#{id}") 54 | nil 55 | end # def delete_data_bag_item 56 | 57 | end # module DataBags 58 | end # class Connection 59 | end # module Spice -------------------------------------------------------------------------------- /lib/spice/connection/environments.rb: -------------------------------------------------------------------------------- 1 | module Spice 2 | class Connection 3 | module Environments 4 | # A collection of environments 5 | # @param [Hash] options An options hash that is passed to {Search#search} 6 | # @return [Array] 7 | # @see Search#search 8 | # @example Retrieve all environments with names that begin with "prod" 9 | # Spice.environments(:q => "name:prod*") 10 | def environments(options=Mash.new) 11 | search('environment', options) 12 | end # def environment 13 | 14 | # Retrieve a single environment 15 | # @param [String] name The environment name 16 | # @return [Spice::Environment] 17 | # @raise [Spice::Error::NotFound] raised when environment does not exist 18 | # @example Retrieve the environment named "production" 19 | # Spice.environment("production") 20 | def environment(name) 21 | attributes = get("/environments/#{name}") 22 | Spice::Environment.get_or_new(attributes) 23 | end # def environment 24 | 25 | end # module Environments 26 | end # class Connection 27 | end # module Spice -------------------------------------------------------------------------------- /lib/spice/connection/nodes.rb: -------------------------------------------------------------------------------- 1 | module Spice 2 | class Connection 3 | module Nodes 4 | # A collection of nodes 5 | # @param [Hash] options An options hash that is passed to {Search#search} 6 | # @return [Array] 7 | # @see Search#search 8 | # @example Retrieve all nodes that have the role "base" 9 | # Spice.nodes(:q => "roles:base") 10 | # @example Retrieve nodes with role "base" in the "production" environment 11 | # Spice.nodes(:q => "roles:base AND environment:production") 12 | def nodes(options=Mash.new) 13 | search('node', options) 14 | end # def nodes 15 | 16 | # Retrieve a single client 17 | # @param [String] name The node name 18 | # @return [Spice::Node] 19 | # @raise [Spice::Error::NotFound] raised when node does not exist 20 | # @example Retrieve the node named "app.example.com" 21 | # Spice.node("app.example.com") 22 | def node(name) 23 | attributes = get("/nodes/#{name}") 24 | Spice::Node.get_or_new(attributes) 25 | end # def node 26 | 27 | alias :get_node :node 28 | 29 | def create_node(params=Mash.new) 30 | node = Spice::Node.new(params) 31 | post("/nodes", node.attrs) 32 | get_node(node.name) 33 | end # def create_node 34 | 35 | def update_node(params=Mash.new) 36 | node = get_node(params[:name]) 37 | node.attrs.update Spice::Node.new(params) 38 | put("/nodes/#{node.name}", node.attrs) 39 | get_node(node.name) 40 | end 41 | 42 | end # module Nodes 43 | end # class Connection 44 | end # module Spice -------------------------------------------------------------------------------- /lib/spice/connection/roles.rb: -------------------------------------------------------------------------------- 1 | module Spice 2 | class Connection 3 | module Roles 4 | # A collection of roles 5 | # @param [Hash] options An options hash that is passed to {Search#search} 6 | # @return [Array] 7 | # @see Search#search 8 | # @example Retrieve all roles that start with "app_" 9 | # Spice.roles(:q => "name:app_*") 10 | def roles(options=Mash.new) 11 | search('role', options) 12 | end # def roles 13 | 14 | # Retrieve a single role 15 | # @param [String] name The role name 16 | # @return [Spice::Role] 17 | # @raise [Spice::Error::NotFound] raised when role does not exist 18 | # @example Retrieve the role "app_server" 19 | # Spice.role("app_server") 20 | def role(name) 21 | attributes = get("/roles/#{name}") 22 | Spice::Role.new(attributes) 23 | end # def role 24 | 25 | end # module Roles 26 | end # class Connection 27 | end # module Spice -------------------------------------------------------------------------------- /lib/spice/connection/search.rb: -------------------------------------------------------------------------------- 1 | require 'cgi' 2 | 3 | module Spice 4 | class Connection 5 | module Search 6 | # @option options [String] :q The Solr search query string 7 | # @option options [String] :sort Order by which to sort the results 8 | # @option options [Numeric] :start The number by which to offset the results 9 | # @option options [Numeric] :rows The maximum number of rows to return 10 | def search(index, options=Mash.new) 11 | index = index.to_s 12 | options = {:q => options} if options.is_a? String 13 | options.symbolize_keys! 14 | 15 | options[:q] ||= '*:*' 16 | options[:sort] ||= "X_CHEF_id_CHEF_X asc" 17 | options[:start] ||= 0 18 | options[:rows] ||= 1000 19 | 20 | # clean up options hash 21 | options.delete_if{|k,v| !%w(q sort start rows).include?(k.to_s)} 22 | 23 | params = options.collect{ |k, v| "#{k}=#{CGI::escape(v.to_s)}"}.join("&") 24 | case index 25 | when 'node' 26 | get("/search/#{CGI::escape(index.to_s)}?#{params}")['rows'].map do |node| 27 | Spice::Node.get_or_new(node) 28 | end 29 | when 'role' 30 | get("/search/#{CGI::escape(index.to_s)}?#{params}")['rows'].map do |role| 31 | Spice::Role.get_or_new(role) 32 | end 33 | when 'client' 34 | get("/search/#{CGI::escape(index.to_s)}?#{params}")['rows'].map do |client| 35 | Spice::Client.get_or_new(client) 36 | end 37 | when 'environment' 38 | get("/search/#{CGI::escape(index.to_s)}?#{params}")['rows'].map do |env| 39 | env['attrs'] = env.delete('attributes') 40 | Spice::Environment.get_or_new(env) 41 | end 42 | else 43 | # assume it's a data bag 44 | get("/search/#{CGI::escape(index.to_s)}?#{params}")['rows'].map do |db| 45 | data = db['raw_data'] 46 | Spice::DataBagItem.get_or_new(data) 47 | end 48 | end 49 | end # def search 50 | 51 | end # module Search 52 | end # class Connection 53 | end # module Spice -------------------------------------------------------------------------------- /lib/spice/cookbook.rb: -------------------------------------------------------------------------------- 1 | require 'spice/base' 2 | 3 | module Spice 4 | class Cookbook < Base 5 | attr_reader :name, :versions 6 | 7 | end 8 | end -------------------------------------------------------------------------------- /lib/spice/cookbook_version.rb: -------------------------------------------------------------------------------- 1 | require 'spice/base' 2 | 3 | module Spice 4 | class CookbookVersion < Base 5 | attr_reader :name, :version, :definitions, :files, :providers, :metadata, 6 | :libraries, :templates, :resources, :attributes, :cookbook_name, 7 | :recipes, :root_files 8 | 9 | def initialize(attrs=Mash.new) 10 | super 11 | @attrs['json_class'] ||= "Chef::CookbookVersion" 12 | @attrs['chef_type'] ||= 'cookbook_version' 13 | end 14 | 15 | end 16 | end -------------------------------------------------------------------------------- /lib/spice/core_ext/array.rb: -------------------------------------------------------------------------------- 1 | class Array 2 | 3 | def extract_options! 4 | last.is_a?(::Hash) ? pop : {} 5 | end # def extract_options! 6 | 7 | end 8 | -------------------------------------------------------------------------------- /lib/spice/core_ext/enumerable.rb: -------------------------------------------------------------------------------- 1 | module Enumerable 2 | 3 | def threaded_map 4 | threads = [] 5 | each do |object| 6 | threads << Thread.new{yield object} 7 | end 8 | threads.map(&:value) 9 | end 10 | 11 | end -------------------------------------------------------------------------------- /lib/spice/core_ext/hash.rb: -------------------------------------------------------------------------------- 1 | class Hash 2 | 3 | # Return a hash that includes everything but the given keys. 4 | # 5 | # @param keys [Array, Set] 6 | # @return [Hash] 7 | def except(*keys) 8 | self.dup.except!(*keys) 9 | end 10 | 11 | # Replaces the hash without the given keys. 12 | # 13 | # @param keys [Array, Set] 14 | # @return [Hash] 15 | def except!(*keys) 16 | keys.each{|key| delete(key)} 17 | self 18 | end 19 | 20 | # Merges self with another hash, recursively 21 | # 22 | # @param hash [Hash] The hash to merge 23 | # @return [Hash] 24 | def deep_merge(hash) 25 | target = self.dup 26 | hash.keys.each do |key| 27 | if hash[key].is_a?(Hash) && self[key].is_a?(Hash) 28 | target[key] = target[key].deep_merge(hash[key]) 29 | next 30 | end 31 | target[key] = hash[key] 32 | end 33 | target 34 | end 35 | 36 | def stringify_keys! 37 | keys.each do |key| 38 | self[key.to_s] = delete(key) 39 | end 40 | self 41 | end unless defined? stringify_keys 42 | 43 | def symbolize_keys! 44 | keys.each do |key| 45 | self[(key.to_sym rescue key) || key] = delete(key) 46 | end 47 | self 48 | end unless defined? symbolize_keys 49 | end 50 | -------------------------------------------------------------------------------- /lib/spice/core_ext/mash.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2009 Dan Kubb 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the 5 | # "Software"), to deal in the Software without restriction, including 6 | # without limitation the rights to use, copy, modify, merge, publish, 7 | # distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so, subject to 9 | # the following conditions: 10 | 11 | # The above copyright notice and this permission notice shall be 12 | # included in all copies or substantial portions of the Software. 13 | 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | # --- 23 | # --- 24 | 25 | # Some portions of blank.rb and mash.rb are verbatim copies of software 26 | # licensed under the MIT license. That license is included below: 27 | 28 | # Copyright (c) 2005-2008 David Heinemeier Hansson 29 | 30 | # Permission is hereby granted, free of charge, to any person obtaining 31 | # a copy of this software and associated documentation files (the 32 | # "Software"), to deal in the Software without restriction, including 33 | # without limitation the rights to use, copy, modify, merge, publish, 34 | # distribute, sublicense, and/or sell copies of the Software, and to 35 | # permit persons to whom the Software is furnished to do so, subject to 36 | # the following conditions: 37 | 38 | # The above copyright notice and this permission notice shall be 39 | # included in all copies or substantial portions of the Software. 40 | 41 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 42 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 43 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 44 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 45 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 46 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 47 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 48 | 49 | # This class has dubious semantics and we only have it so that people can write 50 | # params[:key] instead of params['key']. 51 | class Mash < Hash 52 | 53 | # @param constructor 54 | # The default value for the mash. Defaults to an empty hash. 55 | # 56 | # [Alternatives] 57 | # If constructor is a Hash, a new mash will be created based on the keys of 58 | # the hash and no default value will be set. 59 | def initialize(constructor = {}) 60 | if constructor.is_a?(Hash) 61 | super() 62 | update(constructor) 63 | else 64 | super(constructor) 65 | end 66 | end 67 | 68 | # @param orig Mash being copied 69 | # 70 | # @return [Object] A new copied Mash 71 | def initialize_copy(orig) 72 | super 73 | # Handle nested values 74 | each do |k,v| 75 | if v.kind_of?(Mash) || v.is_a?(Array) 76 | self[k] = v.dup 77 | end 78 | end 79 | self 80 | end 81 | 82 | # @param key The default value for the mash. Defaults to nil. 83 | # 84 | # [Alternatives] 85 | # If key is a Symbol and it is a key in the mash, then the default value will 86 | # be set to the value matching the key. 87 | def default(key = nil) 88 | if key.is_a?(Symbol) && include?(key = key.to_s) 89 | self[key] 90 | else 91 | super 92 | end 93 | end 94 | 95 | alias_method :regular_writer, :[]= unless method_defined?(:regular_writer) 96 | alias_method :regular_update, :update unless method_defined?(:regular_update) 97 | 98 | # @param key The key to set. 99 | # @param value 100 | # The value to set the key to. 101 | # 102 | # @see Mash#convert_key 103 | # @see Mash#convert_value 104 | def []=(key, value) 105 | regular_writer(convert_key(key), convert_value(value)) 106 | end 107 | 108 | # @param other_hash 109 | # A hash to update values in the mash with. The keys and the values will be 110 | # converted to Mash format. 111 | # 112 | # @return [Mash] The updated mash. 113 | def update(other_hash) 114 | other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) } 115 | self 116 | end 117 | 118 | alias_method :merge!, :update 119 | 120 | # @param key The key to check for. This will be run through convert_key. 121 | # 122 | # @return [Boolean] True if the key exists in the mash. 123 | def key?(key) 124 | super(convert_key(key)) 125 | end 126 | 127 | # def include? def has_key? def member? 128 | alias_method :include?, :key? 129 | alias_method :has_key?, :key? 130 | alias_method :member?, :key? 131 | 132 | # @param key The key to fetch. This will be run through convert_key. 133 | # @param extras Default value. 134 | # 135 | # @return [Object] The value at key or the default value. 136 | def fetch(key, *extras) 137 | super(convert_key(key), *extras) 138 | end 139 | 140 | # @param indices 141 | # The keys to retrieve values for. These will be run through +convert_key+. 142 | # 143 | # @return [Array] The values at each of the provided keys 144 | def values_at(*indices) 145 | indices.collect {|key| self[convert_key(key)]} 146 | end 147 | 148 | # @param hash The hash to merge with the mash. 149 | # 150 | # @return [Mash] A new mash with the hash values merged in. 151 | def merge(hash) 152 | self.dup.update(hash) 153 | end 154 | 155 | # @param key 156 | # The key to delete from the mash.\ 157 | def delete(key) 158 | super(convert_key(key)) 159 | end 160 | 161 | # @return [Mash] A new mash without the selected keys. 162 | # 163 | # @example 164 | # { :one => 1, :two => 2, :three => 3 }.except(:one) 165 | # #=> { "two" => 2, "three" => 3 } 166 | def except(*keys) 167 | super(*keys.map {|k| convert_key(k)}) 168 | end 169 | 170 | # Used to provide the same interface as Hash. 171 | # 172 | # @return [Mash] This mash unchanged. 173 | def stringify_keys!; self end 174 | 175 | # @return [Hash] The mash as a Hash with symbolized keys. 176 | def symbolize_keys 177 | h = Hash.new(default) 178 | each { |key, val| h[key.to_sym] = val } 179 | h 180 | end 181 | 182 | # @return [Hash] The mash as a Hash with string keys. 183 | def to_hash 184 | Hash.new(default).merge(self) 185 | end 186 | 187 | # @return [Mash] Convert a Hash into a Mash 188 | # The input Hash's default value is maintained 189 | def self.from_hash(hash) 190 | mash = Mash.new(hash) 191 | mash.default = hash.default 192 | mash 193 | end 194 | 195 | protected 196 | # @param key The key to convert. 197 | # 198 | # @api private 199 | def convert_key(key) 200 | key.kind_of?(Symbol) ? key.to_s : key 201 | end 202 | 203 | # @param value The value to convert. 204 | # 205 | # @return [Object] 206 | # The converted value. A Hash or an Array of hashes, will be converted to 207 | # their Mash equivalents. 208 | # 209 | # @api private 210 | def convert_value(value) 211 | if value.class == Hash 212 | Mash.from_hash(value) 213 | elsif value.is_a?(Array) 214 | value.collect { |e| convert_value(e) } 215 | else 216 | value 217 | end 218 | end 219 | end -------------------------------------------------------------------------------- /lib/spice/data_bag.rb: -------------------------------------------------------------------------------- 1 | require 'spice/base' 2 | 3 | module Spice 4 | class DataBag < Base 5 | attr_reader :name, :items 6 | 7 | end 8 | end -------------------------------------------------------------------------------- /lib/spice/data_bag_item.rb: -------------------------------------------------------------------------------- 1 | require 'spice/base' 2 | 3 | module Spice 4 | class DataBagItem < Base 5 | attr_reader :name, :id, :chef_type 6 | 7 | def initialize(attrs=Mash.new) 8 | super 9 | @attrs['chef_type'] ||= 'data_bag_item' 10 | end 11 | 12 | end 13 | end -------------------------------------------------------------------------------- /lib/spice/environment.rb: -------------------------------------------------------------------------------- 1 | require 'spice/base' 2 | 3 | module Spice 4 | class Environment < Base 5 | attr_reader :name, :description, :attributes, :cookbook_versions, 6 | :chef_type, :json_class 7 | 8 | def initialize(attrs=Mash.new) 9 | super 10 | @attrs['json_class'] ||= "Chef::Environment" 11 | @attrs['chef_type'] ||= "environment" 12 | @attrs['attributes'] ||= Mash.new 13 | @attrs['cookbook_version'] ||= Mash.new 14 | end 15 | 16 | end 17 | end -------------------------------------------------------------------------------- /lib/spice/error.rb: -------------------------------------------------------------------------------- 1 | module Spice 2 | class Error < StandardError 3 | attr_reader :http_headers, :wrapped_exception 4 | 5 | def initialize(exception=$!, http_headers={}) 6 | @http_headers = http_headers 7 | if exception.respond_to?(:backtrace) 8 | super(exception.message) 9 | @wrapped_exception = exception 10 | else 11 | super(exception.to_s) 12 | end 13 | end 14 | 15 | def backtrace 16 | @wrapped_exception ? @wrapped_exception.backtrace : super 17 | end 18 | 19 | end 20 | 21 | class Error::BadRequest < Spice::Error 22 | end 23 | 24 | class Error::Unauthorized < Spice::Error 25 | end 26 | 27 | class Error::Forbidden < Spice::Error 28 | end 29 | 30 | class Error::NotFound < Spice::Error 31 | end 32 | 33 | class Error::NotAcceptable < Spice::Error 34 | end 35 | 36 | class Error::Conflict < Spice::Error 37 | end 38 | 39 | class Error::ClientError < Spice::Error 40 | end 41 | 42 | end -------------------------------------------------------------------------------- /lib/spice/identity_map.rb: -------------------------------------------------------------------------------- 1 | module Spice 2 | 3 | # Tracks objects to help ensure that each object gets loaded only once. 4 | # See: http://www.martinfowler.com/eaaCatalog/identityMap.html 5 | class IdentityMap < Hash 6 | end 7 | 8 | end 9 | -------------------------------------------------------------------------------- /lib/spice/node.rb: -------------------------------------------------------------------------------- 1 | require 'spice/base' 2 | 3 | module Spice 4 | class Node < Base 5 | attr_reader :name, :normal, :override, :default, :automatic, :run_list, 6 | :json_class, :_rev, :chef_type, :chef_environment, :attributes, 7 | :overrides, :defaults 8 | 9 | def initialize(attrs=Mash.new) 10 | super 11 | @attrs['json_class'] ||= "Chef::Node" 12 | @attrs['chef_type'] ||= 'node' 13 | @attrs['attributes'] ||= Mash.new 14 | @attrs['overrides'] ||= Mash.new 15 | @attrs['run_list'] ||= [] 16 | end 17 | 18 | end 19 | end -------------------------------------------------------------------------------- /lib/spice/request.rb: -------------------------------------------------------------------------------- 1 | require 'faraday' 2 | require 'spice/core_ext/hash' 3 | # require 'spice/request/auth' 4 | # require 'spice/response/parse_json' 5 | # require 'spice/response/client_error' 6 | 7 | module Spice 8 | module Request 9 | 10 | def get(path, params={}, options=Mash.new) 11 | request(:get, path, params, options) 12 | end 13 | 14 | def post(path, params={}, options=Mash.new) 15 | request(:post, path, params, options) 16 | end 17 | 18 | def put(path, params={}, options=Mash.new) 19 | request(:put, path, params, options) 20 | end 21 | 22 | def delete(path, params={}, options=Mash.new) 23 | request(:delete, path, params, options) 24 | end 25 | 26 | private 27 | 28 | def connection 29 | # return @connection if defined? @connection 30 | 31 | default_options = { 32 | :headers => { 33 | :accept => 'application/json', 34 | :content_type => 'application/json', 35 | :user_agent => user_agent 36 | } 37 | } 38 | 39 | options = default_options.deep_merge(connection_options) 40 | 41 | # @connection = Faraday.new(Spice.server_url, options, &Spice.middleware) 42 | Faraday.new(server_url, options, &middleware) 43 | end 44 | 45 | def request(method, path, params, options) 46 | json_params = params ? MultiJson.dump(params) : "" 47 | uri = server_url 48 | uri = URI(uri) unless uri.respond_to?(:host) 49 | full_path = uri.path + URI(path).path 50 | full_path_and_params = uri.path + path 51 | 52 | headers = signature_headers(method.to_s.upcase.to_sym, full_path, json_params) 53 | # puts headers.inspect 54 | # puts client_key.inspect 55 | response = connection.run_request(method.to_sym, full_path_and_params, nil, headers) do |request| 56 | request.options[:raw] = true if options[:raw] 57 | 58 | # puts request.inspect 59 | 60 | unless params.nil? 61 | if request.method == :post || :put 62 | request.body = json_params 63 | else 64 | request.params.update params 65 | end 66 | end 67 | yield request if block_given? 68 | end 69 | 70 | if options[:raw] 71 | return response 72 | end 73 | 74 | if options[:json] 75 | return MultiJson.dump(response.body) 76 | end 77 | 78 | return response.body 79 | rescue Faraday::Error::ClientError 80 | raise Spice::Error::ClientError 81 | end 82 | 83 | end 84 | end -------------------------------------------------------------------------------- /lib/spice/request/auth.rb: -------------------------------------------------------------------------------- 1 | module Spice 2 | module Request 3 | class Auth < Faraday::Middleware 4 | 5 | def initialize(app, options) 6 | @app, @options = app, options 7 | end 8 | 9 | def call(env) 10 | 11 | end 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /lib/spice/response/client_error.rb: -------------------------------------------------------------------------------- 1 | require 'faraday' 2 | 3 | module Spice 4 | module Response 5 | class ClientError < Faraday::Response::Middleware 6 | 7 | def on_complete(env) 8 | case env[:status].to_i 9 | when 400 10 | raise Spice::Error::BadRequest.new(error(env[:body]), env[:request_headers]) 11 | when 401 12 | raise Spice::Error::Unauthorized.new(error(env[:body]), env[:request_headers]) 13 | when 403 14 | raise Spice::Error::Forbidden.new(error(env[:body]), env[:request_headers]) 15 | when 404 16 | raise Spice::Error::NotFound.new(error(env[:body]), env[:request_headers]) 17 | when 406 18 | raise Spice::Error::NotAcceptable.new(error(env[:body]), env[:request_headers]) 19 | when 409 20 | raise Spice::Error::Conflict.new(error(env[:body]), env[:request_headers]) 21 | end 22 | end 23 | 24 | def error(body) 25 | if body["error"].kind_of?(Array) 26 | body["error"].join(',') 27 | else 28 | body["error"] 29 | end 30 | end 31 | 32 | end 33 | end 34 | end -------------------------------------------------------------------------------- /lib/spice/response/parse_json.rb: -------------------------------------------------------------------------------- 1 | require 'faraday' 2 | require 'multi_json' 3 | 4 | module Spice 5 | module Response 6 | class ParseJSON < Faraday::Response::Middleware 7 | 8 | def parse(body) 9 | case body 10 | when '' 11 | nil 12 | when 'true' 13 | true 14 | when 'false' 15 | false 16 | else 17 | MultiJson.load(body) 18 | end 19 | end # def parse 20 | 21 | def on_complete(env) 22 | if respond_to? :parse 23 | env[:body] = parse(env[:body]) unless env[:request][:raw] or [204,304].index env[:status] 24 | end 25 | end # def on_complete 26 | 27 | end 28 | end 29 | end -------------------------------------------------------------------------------- /lib/spice/role.rb: -------------------------------------------------------------------------------- 1 | require 'spice/base' 2 | 3 | module Spice 4 | class Role < Base 5 | attr_reader :name, :description, :override_attributes, :default_attributes, 6 | :run_list, :env_run_lists, :_rev, :json_class, :chef_type 7 | 8 | def initialize(attrs=Mash.new) 9 | super 10 | @attrs['json_class'] ||= "Chef::Role" 11 | @attrs['chef_type'] ||= 'role' 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /lib/spice/version.rb: -------------------------------------------------------------------------------- 1 | module Spice 2 | VERSION = "1.0.6" 3 | end 4 | -------------------------------------------------------------------------------- /spec/fixtures/client.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEA02709SMGcz6+yD5TXnO3s6MbGDLUeggXKUuuy41QLKxTOUKe 3 | IgANyrsaZEqzgWAspBV5i54eoV4XBF0fIaTiX/5OgV14q9wy2lcUSwfwLs+zntZR 4 | nG+iXxxzY6liosnq89Y24GZRFsaxwOIRtUWpmrHcRj3K5qqMVIw6Jq7er1RDbSxB 5 | QGtXGCA7zxVIs6OuesC+E8SmZElHIw0bmpE/X2EjwnOj0Vyr7SDU7wdvHmTLM/uu 6 | admlmv/jTyq6TvkSzLWAmXdvNF9VdXQD+Z+2Sip9jK4HPKPc+uE32WNeMbx8jikq 7 | 64XYnmgEoIjrvDqMv0Qn4Z9Z9FyeuWDbyU+QPQIDAQABAoIBAQCEpNiira2PQW75 8 | OzicSeaWmR4XaVQplsfJDYUEiIwi6p1vj96bIAOHCyi8gKzq3oZDDoHpWHKI8/lm 9 | sHEmb0XhyS5BNH3O+98fYYAGrl09UOq0HGqoxNRN7RBrFsodrKVtAhf9YdM/gbnk 10 | cy9+1MxZk3nIy11uptJQ/IRC3nQv833n4boFbbSpWIllblivKp1z/hR9sHyzVq80 11 | GAQF/lKtT9viK3XD4jV4twciglP1GsxwS0WGos+p/l4PEjoBho9aPRJDqfawAsos 12 | XpWv0D/KQ2ospCDcJcaG4gflFhmDXYJh2CTN+1wmwu/KjIIng8fKBT7UKwnLvgiD 13 | 6Rlc8izhAoGBAPT1qMmh/Dgd1xAH3hZpKO8wZOH0EWL4GN/XMXEDlmR0jZ5WA9hQ 14 | MaEhWn+dtuh1kPNdChs4LcUP7Jv0GydAy4A+/u90jPkGr4lcLIVXr7pKd6/ZpOlf 15 | ASfDagpytfZlihU+M2LyZKwjZ1yqeO38UYYXjFLNrtiZtqQFrjW9foWVAoGBANz2 16 | eRBAuL3p1Zdb2ThYl8vCG8yb5i5FwdGO3zIuzzdR5TcrZkAOHCV1RImygzmZMC8d 17 | com/xOFw6vfsHDN/8af+EQvjO1QTkFcT/QHm7tjvPzaTvvG39jARAFjfk35XNuT3 18 | RHXqBfHYaTnGbahJEQ9ix1yst+nVyJDIy6CcNeYJAoGADcYGYJgIG4up/Q/EMBRn 19 | pdNdyMq1c5ApreRmtkkSWsGm+OEzRCV2NO4wLX7LuxRpOxXfLJqjEDKOBlLaeAGP 20 | r71bDlHKX3I9yoZxRmogymPHiQ6+1BRwdzDotH+wtZMOCZW+w/CHMpqnBDHdrNwE 21 | JXsLe9hu3WT/PQfIvOLTy2UCgYEArPR3+pLOiEjaGSmZs5MU7ja3z8ZPu/R6K2+r 22 | oPom1GRUCIT9+jGRMnjB9w41zQXtQ1PHba8fil76bKjxwhXpr9ccvLNWow7VMnPA 23 | 3JZIaqGKV0v6b/XQy/rizK3kI0Zc/4fUnH9bu9vrMX/yIYFfyy1EY3QGWRAvd7TU 24 | PplHTxECgYEAiGRFqyo5kdGaC/36Op51qmMR+1z7Z6Y5DMUVR+55aooBIya7cOgJ 25 | NDTlUr5qpeRmKGDpltAyxrcZvh2s97lgV3537DC3KCEYM15/ATjcVqTPJb0iLGWC 26 | TidCXNMmKEvoH48bzl0E8PI61B9+0S2nmO8PH2RqMlscnQS+oudR+/A= 27 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /spec/fixtures/clients/create.json: -------------------------------------------------------------------------------- 1 | { 2 | "uri": "http://localhost:4000/clients/monkeypants", 3 | "private_key": "RSA PRIVATE KEY" 4 | } -------------------------------------------------------------------------------- /spec/fixtures/clients/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "chef-webui": "http://localhost:4000/clients/chef-webui", 3 | "chef-validator": "http://localhost:4000/clients/chef-validator", 4 | "adam": "http://localhost:4000/clients/adam" 5 | } -------------------------------------------------------------------------------- /spec/fixtures/clients/reregister.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "monkeypants", 3 | "private_key": "RSA PRIVATE KEY", 4 | "admin": false 5 | } -------------------------------------------------------------------------------- /spec/fixtures/clients/show.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "monkeypants", 3 | "chef_type": "client", 4 | "json_class": "Chef::ApiClient", 5 | "public_key": "RSA PUBLIC KEY", 6 | "_rev": "1-68532bf2122a54464db6ad65a24e2225", 7 | "admin": true 8 | } -------------------------------------------------------------------------------- /spec/fixtures/clients/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "monkeypants", 3 | "private_key": "RSA PRIVATE KEY", 4 | "admin": false 5 | } -------------------------------------------------------------------------------- /spec/fixtures/cookbook_versions/show.json: -------------------------------------------------------------------------------- 1 | { 2 | "definitions": [ 3 | { 4 | "name": "unicorn_config.rb", 5 | "url": "https://s3.amazonaws.com/opscode-platform-production-data/organization-(...)", 6 | "checksum": "c92b659171552e896074caa58dada0c2", 7 | "path": "definitions/unicorn_config.rb", 8 | "specificity": "default" 9 | } 10 | ], 11 | "name": "unicorn-0.1.2", 12 | "attributes": [ 13 | 14 | ], 15 | "files": [ 16 | 17 | ], 18 | "json_class": "Chef::CookbookVersion", 19 | "providers": [ 20 | 21 | ], 22 | "metadata": { 23 | "dependencies": { 24 | "ruby": [ 25 | 26 | ], 27 | "rubygems": [ 28 | 29 | ] 30 | }, 31 | "name": "unicorn", 32 | "maintainer_email": "ops@opscode.com", 33 | "attributes": { 34 | }, 35 | "license": "Apache 2.0", 36 | "suggestions": { 37 | }, 38 | "platforms": { 39 | }, 40 | "maintainer": "Opscode, Inc", 41 | "long_description": "= LICENSE AND AUTHOR:\n\nAuthor:: Adam Jacob \n\nCopyright 2009-2010, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (...)", 42 | "recommendations": { 43 | }, 44 | "version": "0.1.2", 45 | "conflicting": { 46 | }, 47 | "recipes": { 48 | "unicorn": "Installs unicorn rubygem" 49 | }, 50 | "groupings": { 51 | }, 52 | "replacing": { 53 | }, 54 | "description": "Installs/Configures unicorn", 55 | "providing": { 56 | } 57 | }, 58 | "libraries": [ 59 | 60 | ], 61 | "templates": [ 62 | { 63 | "name": "unicorn.rb.erb", 64 | "url": "https://s3.amazonaws.com/opscode-platform-production-data/organization-(...)", 65 | "checksum": "36a1cc1b225708db96d48026c3f624b2", 66 | "path": "templates/default/unicorn.rb.erb", 67 | "specificity": "default" 68 | } 69 | ], 70 | "resources": [ 71 | 72 | ], 73 | "cookbook_name": "unicorn", 74 | "version": "0.1.2", 75 | "recipes": [ 76 | { 77 | "name": "default.rb", 78 | "url": "https://s3.amazonaws.com/opscode-platform-production-data/organization-(...)", 79 | "checksum": "ba0dadcbca26710a521e0e3160cc5e20", 80 | "path": "recipes/default.rb", 81 | "specificity": "default" 82 | } 83 | ], 84 | "root_files": [ 85 | { 86 | "name": "README.rdoc", 87 | "url": "https://s3.amazonaws.com/opscode-platform-production-data/organization-(...)", 88 | "checksum": "d18c630c8a68ffa4852d13214d0525a6", 89 | "path": "README.rdoc", 90 | "specificity": "default" 91 | }, 92 | { 93 | "name": "metadata.rb", 94 | "url": "https://s3.amazonaws.com/opscode-platform-production-data/organization-(...)", 95 | "checksum": "967087a09f48f234028d3aa27a094882", 96 | "path": "metadata.rb", 97 | "specificity": "default" 98 | }, 99 | { 100 | "name": "metadata.json", 101 | "url": "https://s3.amazonaws.com/opscode-platform-production-data/organization-(...)", 102 | "checksum": "45b27c78955f6a738d2d42d88056c57c", 103 | "path": "metadata.json", 104 | "specificity": "default" 105 | } 106 | ], 107 | "chef_type": "cookbook_version" 108 | } -------------------------------------------------------------------------------- /spec/fixtures/cookbook_versions/update.json: -------------------------------------------------------------------------------- 1 | { "attributes" : [ ], 2 | "chef_type" : "cookbook_version", 3 | "cookbook_name" : "unicorn", 4 | "definitions" : [ { "checksum" : "c92b659171552e896074caa58dada0c2", 5 | "name" : "unicorn_config.rb", 6 | "path" : "definitions/unicorn_config.rb", 7 | "specificity" : "default" 8 | } ], 9 | "files" : [ ], 10 | "json_class" : "Chef::CookbookVersion", 11 | "libraries" : [ ], 12 | "metadata" : { "attributes" : { }, 13 | "conflicting" : { }, 14 | "dependencies" : { "ruby" : [ ], 15 | "rubygems" : [ ] 16 | }, 17 | "description" : "Installs/Configures unicorn", 18 | "groupings" : { }, 19 | "license" : "Apache 2.0", 20 | "long_description" : "= LICENSE AND AUTHOR:\n\nAuthor:: Adam Jacob \n\nCopyright 2009-2010, Opscode, Inc.\n\nLicensed under the Apache License, Version 2.0 (...)", 21 | "maintainer" : "Opscode, Inc", 22 | "maintainer_email" : "ops@opscode.com", 23 | "name" : "unicorn", 24 | "platforms" : { }, 25 | "providing" : { }, 26 | "recipes" : { "unicorn" : "Installs unicorn rubygem" }, 27 | "recommendations" : { }, 28 | "replacing" : { }, 29 | "suggestions" : { }, 30 | "version" : "0.1.2" 31 | }, 32 | "name" : "unicorn-0.1.2", 33 | "providers" : [ ], 34 | "recipes" : [ { "checksum" : "ba0dadcbca26710a521e0e3160cc5e20", 35 | "name" : "default.rb", 36 | "path" : "recipes/default.rb", 37 | "specificity" : "default" 38 | } ], 39 | "resources" : [ ], 40 | "root_files" : [ { "checksum" : "d18c630c8a68ffa4852d13214d0525a6", 41 | "name" : "README.rdoc", 42 | "path" : "README.rdoc", 43 | "specificity" : "default" 44 | }, 45 | { "checksum" : "967087a09f48f234028d3aa27a094882", 46 | "name" : "metadata.rb", 47 | "path" : "metadata.rb", 48 | "specificity" : "default" 49 | }, 50 | { "checksum" : "45b27c78955f6a738d2d42d88056c57c", 51 | "name" : "metadata.json", 52 | "path" : "metadata.json", 53 | "specificity" : "default" 54 | } 55 | ], 56 | "templates" : [ { "checksum" : "36a1cc1b225708db96d48026c3f624b2", 57 | "name" : "unicorn.rb.erb", 58 | "path" : "templates/default/unicorn.rb.erb", 59 | "specificity" : "default" 60 | } ], 61 | "version" : "0.1.2" 62 | } -------------------------------------------------------------------------------- /spec/fixtures/cookbooks/index-0.10.json: -------------------------------------------------------------------------------- 1 | { 2 | "apache2": { 3 | "url": "http://localhost:4000/cookbooks/apache2", 4 | "versions": [ 5 | { 6 | "url": "http://localhost:4000/cookbooks/apache2/5.1.0", 7 | "version": "5.1.0" 8 | }, 9 | { 10 | "url": "http://localhost:4000/cookbooks/apache2/4.2.0", 11 | "version": "4.2.0" 12 | } 13 | ] 14 | }, 15 | "nginx": { 16 | "url": "http://localhost:4000/cookbooks/nginx", 17 | "versions": [ 18 | { 19 | "url": "http://localhost:4000/cookbooks/nginx/1.0.0", 20 | "version": "1.0.0" 21 | }, 22 | { 23 | "url": "http://localhost:4000/cookbooks/nginx/0.3.0", 24 | "version": "0.3.0" 25 | } 26 | ] 27 | } 28 | } -------------------------------------------------------------------------------- /spec/fixtures/cookbooks/index-0.9.json: -------------------------------------------------------------------------------- 1 | { 2 | "unicorn": "http://localhost:4000/cookbooks/unicorn", 3 | "apache2": "http://localhost:4000/cookbooks/apache2" 4 | } -------------------------------------------------------------------------------- /spec/fixtures/cookbooks/show-0.10.json: -------------------------------------------------------------------------------- 1 | { 2 | "apache2": { 3 | "url": "http://localhost:4000/cookbooks/apache2", 4 | "versions": [ 5 | { 6 | "url": "http://localhost:4000/cookbooks/apache2/5.1.0", 7 | "version": "5.1.0" 8 | }, 9 | { 10 | "url": "http://localhost:4000/cookbooks/apache2/4.2.0", 11 | "version": "4.2.0" 12 | } 13 | ] 14 | } 15 | } -------------------------------------------------------------------------------- /spec/fixtures/cookbooks/show-apache2-0.9.json: -------------------------------------------------------------------------------- 1 | { 2 | "apache": [ 3 | "1.3.0" 4 | ] 5 | } -------------------------------------------------------------------------------- /spec/fixtures/cookbooks/show-unicorn-0.9.json: -------------------------------------------------------------------------------- 1 | { 2 | "unicorn": [ 3 | "0.1.2" 4 | ] 5 | } -------------------------------------------------------------------------------- /spec/fixtures/data_bag_items/create.json: -------------------------------------------------------------------------------- 1 | {"chef_type":"data_bag_item", "id":"adam", "real_name":"Adam Jacob", "data_bag":"users"} -------------------------------------------------------------------------------- /spec/fixtures/data_bag_items/show.json: -------------------------------------------------------------------------------- 1 | { 2 | "real_name": "Adam Jacob", 3 | "id": "adam" 4 | } -------------------------------------------------------------------------------- /spec/fixtures/data_bag_items/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "real_name": "Adam Brent Jacob", 3 | "id": "adam" 4 | } -------------------------------------------------------------------------------- /spec/fixtures/data_bags/create.json: -------------------------------------------------------------------------------- 1 | { 2 | "uri": "http://localhost:4000/data/users" 3 | } -------------------------------------------------------------------------------- /spec/fixtures/data_bags/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "users": "http://localhost:4000/data/users" 3 | } -------------------------------------------------------------------------------- /spec/fixtures/data_bags/show.json: -------------------------------------------------------------------------------- 1 | { 2 | "adam": "http://localhost:4000/data/users/adam" 3 | } -------------------------------------------------------------------------------- /spec/fixtures/environments/cookbook.json: -------------------------------------------------------------------------------- 1 | { 2 | "apache2": { 3 | "url": "http://localhost:4000/cookbooks/apache2", 4 | "versions": [ 5 | { 6 | "url": "http://localhost:4000/cookbooks/apache2/5.1.0", 7 | "version": "5.1.0" 8 | }, 9 | { 10 | "url": "http://localhost:4000/cookbooks/apache2/4.2.0", 11 | "version": "4.2.0" 12 | } 13 | ] 14 | } 15 | } -------------------------------------------------------------------------------- /spec/fixtures/environments/cookbooks.json: -------------------------------------------------------------------------------- 1 | { 2 | "apache2": { 3 | "url": "http://localhost:4000/cookbooks/apache2", 4 | "versions": [ 5 | { 6 | "url": "http://localhost:4000/cookbooks/apache2/5.1.0", 7 | "version": "5.1.0" 8 | }, 9 | { 10 | "url": "http://localhost:4000/cookbooks/apache2/4.2.0", 11 | "version": "4.2.0" 12 | } 13 | ] 14 | }, 15 | "nginx": { 16 | "url": "http://localhost:4000/cookbooks/nginx", 17 | "versions": [ 18 | { 19 | "url": "http://localhost:4000/cookbooks/nginx/1.0.0", 20 | "version": "1.0.0" 21 | }, 22 | { 23 | "url": "http://localhost:4000/cookbooks/nginx/0.3.0", 24 | "version": "0.3.0" 25 | } 26 | ] 27 | } 28 | } -------------------------------------------------------------------------------- /spec/fixtures/environments/create.json: -------------------------------------------------------------------------------- 1 | { "uri": "http://localhost:4000/environments/dev" } -------------------------------------------------------------------------------- /spec/fixtures/environments/delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dev", 3 | "attributes": {}, 4 | "json_class": "Chef::Environment", 5 | "description": "The Dev Environment", 6 | "cookbook_versions": {}, 7 | "chef_type": "environment" 8 | } -------------------------------------------------------------------------------- /spec/fixtures/environments/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "webserver": "http://localhost:4000/environments/webserver" 3 | } -------------------------------------------------------------------------------- /spec/fixtures/environments/show.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dev", 3 | "attributes": {}, 4 | "json_class": "Chef::Environment", 5 | "description": "", 6 | "cookbook_versions": {}, 7 | "chef_type": "environment" 8 | } -------------------------------------------------------------------------------- /spec/fixtures/environments/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dev", 3 | "attributes": {}, 4 | "json_class": "Chef::Environment", 5 | "description": "The Dev Environment", 6 | "cookbook_versions": {}, 7 | "chef_type": "environment" 8 | } -------------------------------------------------------------------------------- /spec/fixtures/nodes/cookbooks.json: -------------------------------------------------------------------------------- 1 | { 2 | "unicorn": { 3 | "definitions": [ 4 | { 5 | "name": "unicorn_config.rb", 6 | "uri": "http://localhost:4000/cookbooks/unicorn/definitions?id=unicorn_config.rb", 7 | "checksum": "72074229598611d7a6cdcc8176ffb76748adb4127be3a9651c89bb0afab6498f" 8 | } 9 | ], 10 | "files": [ 11 | 12 | ], 13 | "providers": [ 14 | 15 | ], 16 | "templates": [ 17 | { 18 | "name": "unicorn.rb.erb", 19 | "uri": "http://localhost:4000/cookbooks/unicorn/templates?id=unicorn.rb.erb", 20 | "checksum": "c1df9c8ec2bda7b14ecadc1c5a56802442cd8d7503f2524d4fa3f73dba1a1250", 21 | "specificity": "default" 22 | } 23 | ], 24 | "libraries": [ 25 | 26 | ], 27 | "resources": [ 28 | 29 | ], 30 | "attributes": [ 31 | 32 | ], 33 | "recipes": [ 34 | { 35 | "name": "default.rb", 36 | "uri": "http://localhost:4000/cookbooks/unicorn/recipes?id=default.rb", 37 | "checksum": "87a3f720bf5bbd9227228ba22946436db6598a59f8aedff37515ebd4e1157644" 38 | } 39 | ] 40 | } 41 | } -------------------------------------------------------------------------------- /spec/fixtures/nodes/create.json: -------------------------------------------------------------------------------- 1 | { "uri": "http://localhost:4000/nodes/latte" } -------------------------------------------------------------------------------- /spec/fixtures/nodes/delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": { 3 | 4 | }, 5 | "name": "latte", 6 | "chef_type": "node", 7 | "json_class": "Chef::Node", 8 | "attributes": { 9 | "hardware_type": "laptop" 10 | }, 11 | "run_list": [ 12 | "recipe[apache2]" 13 | ], 14 | "defaults": { 15 | 16 | } 17 | } -------------------------------------------------------------------------------- /spec/fixtures/nodes/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "latte": "http://localhost:4000/nodes/latte" 3 | } -------------------------------------------------------------------------------- /spec/fixtures/nodes/show.json: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": { 3 | 4 | }, 5 | "name": "latte", 6 | "chef_type": "node", 7 | "json_class": "Chef::Node", 8 | "attributes": { 9 | "hardware_type": "laptop" 10 | }, 11 | "run_list": [ 12 | "recipe[unicorn]" 13 | ], 14 | "defaults": { 15 | 16 | } 17 | } -------------------------------------------------------------------------------- /spec/fixtures/nodes/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": {}, 3 | "name": "latte", 4 | "chef_type": "node", 5 | "json_class": "Chef::Node", 6 | "attributes": { 7 | "hardware_type": "laptop" 8 | }, 9 | "run_list": [ 10 | "recipe[unicorn]" 11 | ], 12 | "defaults": {} 13 | } -------------------------------------------------------------------------------- /spec/fixtures/roles/create.json: -------------------------------------------------------------------------------- 1 | { "uri": "http://localhost:4000/roles/webserver" } -------------------------------------------------------------------------------- /spec/fixtures/roles/delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webserver", 3 | "chef_type": "role", 4 | "json_class": "Chef::Role", 5 | "default_attributes": {}, 6 | "description": "A webserver", 7 | "run_list": [ 8 | "recipe[apache2]" 9 | ], 10 | "override_attributes": {} 11 | } -------------------------------------------------------------------------------- /spec/fixtures/roles/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "webserver": "http://localhost:4000/roles/webserver" 3 | } -------------------------------------------------------------------------------- /spec/fixtures/roles/show.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webserver", 3 | "chef_type": "role", 4 | "json_class": "Chef::Role", 5 | "default_attributes": {}, 6 | "description": "A webserver", 7 | "run_list": [ 8 | "recipe[unicorn]", 9 | "recipe[apache2]" 10 | ], 11 | "override_attributes": {} 12 | } -------------------------------------------------------------------------------- /spec/fixtures/roles/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webserver", 3 | "chef_type": "role", 4 | "json_class": "Chef::Role", 5 | "default_attributes": {}, 6 | "description": "A webserver", 7 | "run_list": [ 8 | "recipe[apache2]" 9 | ], 10 | "override_attributes": {} 11 | } -------------------------------------------------------------------------------- /spec/fixtures/search/client.json: -------------------------------------------------------------------------------- 1 | {"total":48,"start":0,"rows":[{"admin":false,"chef_type":"client","name":"ci.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-32744ed8f2d69517ba3857a9187246c6","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAu6GAH3LLzoSke+0vHQB2ah2hERGMo+1yC0mBTZl5wOi4KXj3kgiU\nR9ufrx/2h0ZN2Ob/2ewDOS3UriQOxmzF3M/dGoUmpdy/MRTlp7o1+QU6EPa6/E2w\nD0kybQEOKwCSq4ZTIF/hjlld2HfsY5KS9XLtxf71NSbBkVpkFBZgor29+JPQv9wx\nP0CuNMESbSr3lJCM97UeklITjr+5AsnQWKgbI/KdLftlFFERhsd+9jyZqxSqQ6xO\nNifEXTZSWri+3FEtxNhCLbiYhbCGo5bDRQr45yDlTmVGSDRgGDxo7Npgrhc8PsTy\nlFXvJd5rPFoWTj/GcW3tI8YLYdb8pem6bQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":true,"chef_type":"client","name":"ubuntu","json_class":"Chef::ApiClient","_rev":"1-5d40921e44ab8a89458f42c84b023d08","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAloJ2oqr7SRZqvaC2qsSUPbRTulBnfrrdXrZcLZTre8K0VjhRTOP8\nMnnHz48WAGVRn1+/5X001IKusf+45xkUVztV95WHkvhaOwfxnAbSeJOeNEqiUZXv\nz/cIkY3gxJMOUsh5RuQGlCyQYh8S3vJf6HoLOkckTS6I+TQGKqeBB2XoQB4uAmpb\ndvwPnxApKY5qcyfZaLLKR6hkjBa8LMbB2+1MXozpr82TFlhTW4YfDo2MqZ/yiuaS\nN0BNlWpqkmmVQ2xpK3FPKhLPmOyVERd54EGlxX153cwWNjoRuDrZO6sX4rQLCHbI\nF2WJ2xHp/A8Xs8Wv51IaX+eQcB+DMl/3XQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"monitoring.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-02749c4debd0e23993e9a532db596444","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAzxy6wGWJ92Vjyi3TeLsoZKQvo54F3p3B5urWqrM4W2/3s+my/Icm\ntXdEmMQxC98XbROOGe0YY0OUmAD8Q9HiWuHK3YcGMx9v4w6rBsnZ7uK+OEYQfbEv\nFNCpZYfjtB0OPiXrgJF1toSnPhfnYhLFf50qsVYlOPAqTuje6+d+vJgRks+MyDKm\n7l4C/I11k3gFbpOzxRHhMwyvqKU5xfRof0ADsxi3uom/hVa5SVR30R/STQjHqe2R\nhL7USwpscmi5DBwdXMlFcF/GnLaRvtIvKzRGaTRO43U8ejmWYAICL/Tvf23yZDYj\nSYOyDw4g0KogW7VGYyUrCC21uSoXIDMyuwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":true,"chef_type":"client","name":"dave","json_class":"Chef::ApiClient","_rev":"1-62e59b66e24a3d1b82ed28dc114cf249","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAv1+gh3ilXeiDlPPe0fsCGjn8dWGNPhmI3EVF9NnQYpR7VEwvn5ig\nHqYVE15BwvKxHwTCAxTlQ4V87erIT7vljdON04SVjNoH4aLweCYDySwNyUfxPbka\nK9USt2FN+TGcP+jdPYshsJLJZJ2ualBQ8mk2c9wgoVY2L/tpyHtutw7JroNNWgtU\nhuC42BfqEnkh+VIDCYzqsGMxhA63EO1pIEY8+HpjxhE+G+xcuTrlLCSoCFFlZDYx\nV6D+bkxxB/AJG+bKLR6o2iV7AE+/eCaUNQBA7ta+T+IXQnLPjQb2zxXTFJrgvvzW\nS7sYz0JUBvc/IcqjBn8bZGs7iB38EwWYbwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"kafka1.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-6bd54123bee75478158655ff4c201aeb","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAsTckcIGogA0l0fAXj4eZr4k4VwVV+qBDC1FKp46Cl+mGYZ7DVR/1\nMervtUf/afw8DMtq5Uu+9wIVY6SxRa82k5Tw2bKvZFt3dcdod7vfnUNbwpN1Wi8b\n0ewm0Cf6po6G28JA1NlaI/YXZfO6ocJb309QrqMmB4vpZBpDtoDzcZ83aFKufn7e\n1UQjmGvI+3Vh7kZBX85Djz76o1/waOPW+VXUQcOgVBEZ29H/bcMN2/oF+NQ9JKZQ\nTFRrilk6aFslvmJC9j0DPkmQDR/YeMhOpBaaPUQrCPyv017rzFkiIQFjeR4jMb+u\nt6vUpaG0AtpMvFpSjXBrUj2KAzAbnl/PBwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"redis0-service.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-fd7a04f2bd8ecf38a69ee6b2df15ce90","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAvpQY4LASwRFbsQJmL98lEupUY6KQKAThyhbvMsvgOpXvJCR32cga\nCMDAJ7hXF+JXbqq+YQTCLeJELuvf8t65CJye/usdGksrEnz+lSsGo22iFxKewmkB\nrn2kPXXz0ErZs4QxrIrXX9/x6O9z17k83vQQATqV2is4J20nPk3LaFfR6qfkdf15\nwyL8B7zbJqc01nucmca9X7GDuwL6q64zP0ICZkVOF2u0e20/mUSVV6VxionpYPI9\nxHjJSlOdj0/AOrDp6v3ITgDF5FJOrbmdOqUbURjOb6n1PYVkEGxIoSd9CFDdliN3\n18g3zggr6IIRwQrRAgPeWXBPS4UC9nxyIwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":true,"chef_type":"client","name":"dryan","json_class":"Chef::ApiClient","_rev":"1-5c36eb9aeea0ce5ba580bc14fb439c58","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAuShetE7EcVfZ9KW4N7fr9TU+lQEmhKjLGS71CjdkfYRrJoMSkkBY\noSjGIczWSHtdXuT/TXaoOfswFuuuyUZKUBqs5y14jUsfiqjtFe47CVpnvZEDnIXP\n+JWVBHo0Y8L5syFeFGM4dlfbYbloBwqRyxuDVAQs5gzBczNZBFIElYub3DDWpVtn\nBBc97WyCpeNlNz6ZS4G4zhCUnhGb3M3//tNF5Ds72aZn1ZorWBMyu2sZ3fFXqChM\nWY7CgtBqWG7klqSUvQnBOIbsNyuk+zbY4W0MkQx3JGDDtqVJnuoVnh02nQzX3TDq\niBjJdxqqDZTEvuJCX0HgoCIBi1VX6bZBiQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":true,"chef_type":"client","name":"brian","json_class":"Chef::ApiClient","_rev":"1-5a3dbf6b4a478fa40b7b287b0e970225","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAnstqKwfDGXtsbg3owtw6G4TXTbxdjpi/vdIOOs/dtxMJM/v85ddA\n/cHDFDMxA87CX09OJL4TfDF88/TnWlssSkk9wNr7/nUS5Y+3SsZhPK3UsgePCOgk\nMs/LefEWOz+QVd3FlMvqWr5w8wjUsmUYIvIcetqf5S3Ove+iBW65hekVdwSD7rBw\n+i+fS3hqg4Z2B2VL7/l/rLCRbtvh5NYKCaECOcGDmGCTHRXtIsZA9XPLFsKOoZYO\nwl9oKi/8q9uKSILcC4U530OuZCHu8Oh1xcOosMiVFwjrGM5gL+kE4ymBVyW/5j74\nWg0fTeRuqKyXGF0vJsfzengFkkvK4VSnlQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"pg1.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-1ffd9bae1b9f2ec6d658b5d53e1bf054","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAqBAZ+b8JL5rREzDspp5SjDhoshuVQ+ZpjPo46IMjE9W7B/NgDnws\ntX8GumvNmsf+MwbQ3xHcdTe7hhPOQTGh+SaLtrLYChIgoGEjRJdUymeqeV9sk3DZ\nJBtJb+GROGVRejWQdzbRvLHyGMnXoLOQIhbfhqbfCtBJHrr6U8wctdQhUjUkRIJi\nS6pwUqDeUZZ5ulMbYofzzZR3iLWj5b7Q45XuYo0B0yxFGgmgFhBG94AaofsuDRTu\nwGGR0IyCwUfSOfrmtVKWi0V9o0tYSGw6Xett4HraOFTFrkPsfp9TO5tUXjxqSUX6\n3bDROo0226ZeFUxErBMlDawboob4B/rOWQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"service0-aggregator.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-e6197d416f4866acab69756427f93fb5","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA1J3hK/r/Cry/p5p+M7QMbmOfSaqBV5kX8lY0EoOwJeeBDbpLLJ0p\n1H7n29ld3tKXaxlY4aWyp0hfpyVTfoMI5gB9g4rN24IZ3hehgYKMfyUxoCM2YfsK\na8cX2DtVdSRpFKScuOCyhMLh+/4DgVLVRo2WqD/74f11cziN/Z79g9SezPNqZ/AF\nAqbVpZThG1qb90WLZVr5ZvxGfU90lEdLmZiBOjG6iKxRr3+QTOoluArlwAF6Dz38\nNOcmPxu4Vo7EHY4Sa/rfE54tCXfMbVTO9zevGoUEE9+BCuj+mrq9bK3i1jJxXIL7\nX0zpjNCPUwfToMTQreuQEuJd6kIc/TmpDwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"service1-aggregator.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-f37f4ad6a06e56c3599dc502a18a9333","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAxSLQSXY9KLNpnbb9mTB2y3ksmxeQGwIx43JBtAbqj0k5q0CwEwdX\ntPdK3jNIdRuyWi7eiHcpUsvJD7lvM5WIDPYjQ9AtMLYPK/WQA17+uKnq+RVla96o\n0Z8Mry2s0bjsF6RV4uCts6qPKCxwDI+1sWGGN2eiDiRvxaOtt/iu8wBcyoGqzVfl\nhyZBqJp6q2hmcBhJHGS3bw6NI5nk4N5KDjQdRjD9rqMIrPdFscQESHW3eeskGWBr\nBpkuwUqBYcbJqbk600N0cpJblC3h4JpPhk9gOXiKsGNWV1d98j3051sr3vpGWAzw\npEaIdjS8ZZKozP1V+C8ggXXfAMJ4nweNMQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"quz","json_class":"Chef::ApiClient","_rev":"1-a827533dc7fd189c19af9e3e89f1b635","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAtQvIPhbJUjBeQxTbf977AuPH9IyIWrxK+IqZDZl7Q4zp3PUz0hWq\n0MH+Wk9sAVpkI38K7MNNfxzDNua4USep0Ha1qW0zISOl03xLfoTJDobQW8u+NmeD\nPv4foptJgPEFd3eD5SYayGnzvq/Y5Oi14k1GAUrNKJvP2HWP5zkwqHe5XhLe98Wu\nJRYUpUGyRHNHsIUGK48teQ8ev+Q5E8vG5urHyqOyAsVAKmsxbI8bWvXlJ5Zu+d6F\nm5BPeywTVqOxfwOl5wOLiFWMUeX7dPFSoYh/dDwiRJ+kdpWtdd7LXaJ2I5XUkQjt\nwkim86VG3zgs6lVtRAfPWbDUvQlU9bVMqQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"redis0-builder.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-252a148858e1257e18829e2118c87f82","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA5PusII5Vmzib4OWZKnb5sOnm4rK9Sq4YsLIwMrnrYkDaPIRwk8VP\n2mhaBTOhq2+DFQFbQfc7kHU9tlSkLZgsrYglp0NY4boPjpfyNYy9wIKWjLqO+Xfd\nGxVi93TeFMH04ZJIV3eScC+RE/a44ra5Co/pwgH4VuftIns+TNpjcmcJvNbSLVhs\nwLhgMSiIiMMWNgbF89+LVY6E5lc0pwlvBDYqve5q5ENXJXwK8cqgH7z/EDN5ooBk\nwUwmWeLBx3sLmzD20kR+LuR3BJW8VKXuTFDwvQV1xQC+nO+SlWXWzGTQ33h2wqDn\n//pL3cgJhMjdcTfyf5kuSV/9W8fLPKY6BwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"pg0.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-d7aa4f4fc414b22c5426bdea0f79d2ec","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAm2DST8sGJKq28OsximXXZ91DwPIqXIs4S+ppfs5Io8hR/B3mpmOd\neMmFDklfyISgyHcFB6y7sRfgVSgajSjAJ7xDVTFf3YwamPXUJMagmtRxR8YVq8tQ\nZT3tBgMCeJJgdb7Ba1vPDgBFDijdkVa2Zu3Hu5FwgTDneZzDjHTpkhT60GpDq5Uy\nbvLYUDQXqgWSeppF24WJ1IhuzbzYNSaXpAz6j30DgfNoeefQrbg4xa509w+V6mPC\nIf93jbCZA1jCmJ1ksXueMTWYWNVlO7OTm1kBIGWK1pyh+pWpZAg4pXBZvqP9ciqk\nyaNkvWrMFd9Caj9O7XxI/JwnbaL8Ba79yQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":true,"chef_type":"client","name":"alex","json_class":"Chef::ApiClient","_rev":"1-6d176f51f5c1607476b704dd3edf980d","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAxcxafpcw0QYq+GmNonAEzHOJAicpCyk9RXz6hRbvbHVPc9AeFaoZ\nARTtCCUX7TutjU8fTRqmQxTArLoMCmI+5mcUhlvUTI/I/4/1bWqXXQFQC8IEkVDN\n5rzmo/okhCDV6tr83ZL4JtPg8MurFIwm1ps3g45pJd38w+WRKJxHHG0oY3jqhCv7\nS/C68tOTwCv9rHmZVRUXcMhLeFuLTSmhWdkrQsdUMnILBxtoOKMbSqeJfj9tfOP8\nJrrE7HMryhHWHBs5eUGwuEwBMfYwZH3jaBE5lR7Cy7fyRKCCHGDKtnjM5/enWzC8\n8/FmOa3NRc47vhopWddvzTFbwkIa+0DZEwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"mongo0-builder.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-c785ed2c2608bc4d3ca930ed1a7b9123","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA1vIzDosgKYiHc2VZt5Lh8BKtFLq0qh/MhUdsODDAi1t6vXY6DgsA\nmOr4CdzxKMxBmqfJvTl6OlwPKpDCZ/w1TC6S51AG3Hf88PZL3shEjsp7UwiifRwj\nHstcO6Ff7klkRNawTMEiO+5hs740VTh6/sr+8w+4NGfQgHgJmCbRZzClRwNqbHSP\njFAdw0JJXwEaaSqQQACtasiqq8h79GsF4bvmsLoOss8/WJshUeZtfpbyxf6p7VTv\ntxF0oJtn3TNRwCoc0kQQYwoGTmQQyEDIaG5edrxqdX1o8sBzR8+YiWQQrE9nrzyg\nuBDn9ZtQO1NRnw1frJzG1jSXvp+KjYPuKQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"app0.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-c704f6aa5f8b645861880be6e805595f","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA3EwO2hclyKS03JQ7qyGw7fyRdPMQ065JpJliv3FcJsu8096/KF73\n1iJl1HEnr6noFSh0kk7hfvbiloHkmLTmEZ69VhVpSKm7Ml3IzG261x3g6rACs9q2\nAmTVQGiu2Gsw8tvSZUAglQtFCDIs80fTTu/S6GpU9hzoFkJwfG1fhhJvCPFWHWla\nhIpKG8n+HPYdUBP1jVJL+kr5CzzaEiRhmb8Xks8TCF5Xnws4LHIVMEaNZGdvyGHU\nEoPyThaqAYMrsNexPu/Strxvklh0eVznrxgj6zgTC8WdBY7YAMxk7hIrzfOI5/E1\n7L+l7ZzwrbYokgVEcV/xM9ifkBzKtUuZ1QIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"logging.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-eaac1748f7e98477b1c20e162956d8ea","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA1XQGKURCpShJoCSGHRPXmtCNk8fZPzN+FNPm5o7A/lvPU+/NwN75\nFKhW7PCfWjTsGuVqwALO+/BFIxDov47lbXy1/UpRYcokRMbCZt0xeUOfSN552Q7D\nkIQebQeBf1YE+5dmXbHbxc/h0OFNT+ZLZZanuGOa5lrluGALdSY1fVHUCjWNvy5B\nYkh0mwMJ1egjaZdH7eo+qJT69Nwh5SWaOInDPjhdVnXaN5zvj0fj/CgxKKqWAxvi\n418cyF5Y5dzpMPuyD27dh2+BlFzrLZDl28FUvAoRQ1/FbudXpbwaYJOVtxxVnQd+\nvoMNxzI/TthNyBGe+mvNqLE39DHtKWtP3wIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"web1.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-9cf4cee1dbd3ba22438bcf21d35c04c0","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA5GzSfTpBoh0maBWsO84vFMN7e0MiiRnPbUPOWd89xIlWrenpSCuy\nUYzgVVlYjbzRZo4+JT/tVCT+5QC9I49mex+jogEpRcHn9ENIttesQJ9eHY+Tvq7q\n4IJmVdXBb1DZvpeJ5ZiooAgNjjpD8eu4ihNdXfdyIKWLO12i89hme+8vDePrr80/\n6dreTBd04bujt/lWyNWrwAtxxFrHKPWXLFkd9ZmwvcD39jQUvuLE4s2gAbziYWdl\na9eHUnIFtAcxXam8WhXOXcn8lG6bMhE4bbYIDfA46jPiwzvdv4Cuj5G9b68/gWdf\ni1KAMQ1i+VdODKQsLkLoo+IVpWiAhjYR6QIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"judgments-pg1-builder.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-b83a39bdbdf1e8184304150a551fee0c","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAy9WJhyxv47YxROhpyNmusafk41ozVvNCabIs2vUwd+HUDgqUp2RN\njjKigYOVJ24CdBPVmrUEM41sAagnAEp/cFA8Rpe+1pMOzWCx/4RG2h5lX7L5vMYA\ndZJMhPWBF9pu3BYt4s4hv2kvvUoUkBemt6BmD2uk7k1TLAIDtqLHWeP+yNaF+tLV\nu+XRMjBLrFtF5SROFBqZKnDZnj2AKTa727GeM1Szw2/BdQbrr3DCa51z2j2UIwC8\n6fR1mKBG7Bo7/iZU0jSSfAiNN8j63RfOW9wGIsPJvXCzSP51HJWo4AjFK5KXtulu\nL/hkVCGzVYMvNX9w6kHskUym/Uf+HDTiQQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"pg1-builder.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-49ad072c09f2d72d82f052ffc5d707b4","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAybeJahwiZEjKJVuV9lZfHQHHi5DziqmwOFZxQoshlcHREGxfBe2H\nPNc+K5tWI/if4iyIJMT7LE9qOWS3gnVaqhkTC+crZrK1bwd6D3lGrdpS9rRzkujm\ndHIW3Bq+nTgo0rQjn7GvGaK53xahoiGnN3AnqvqArpBiR/mge9RJqJ9pFkp06srv\nJZaWOhfu93HDX7ZynQ8ZK5AKApZWfs60+gUo50A1FAkfj7OglUOY9wLcA58qP0bn\n+MqhU6nmXBPnJkWRT/YGFvcYyH8npJAMfAZ3RjUeWe5JbCTc49eXppYopck39CzL\n+EqQv6xeOEiegS38u3x/xgrgxhyMr+BUJwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"web0.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-533ef62f11459dcec4863dd8626dc702","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEArmLnMZsAHJPIcG6DeVPQr469WTmTS6HCib/KqO5kL/fSR0lM73NI\n4htrjv6FrN2kFZBEnp7l0uM+tacT6hzSD8Qzxeqjke+euGt56bTualzwc7x8G9U3\nRSTEM6deaLfug9WUuu5fd76wC2a7wVhM1p/kT33lKEDJTn95gqT+VffDW4JmJRVm\nSJ2SFaF5bklrUSC3FZ0URD03NYj10+7t2O5Gwif8AT4i2gkSRIxGRUTkkkTdsYpz\niqIgwJWKJLgocqVTMgSiRb9ac6eFXdsXobM7CZX+UMCj+MAHEkzZJrjhW/WE3nFz\nY2wzwRdZUIyXUAI8RiU5UVSbkXSiJk5AAQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"redis0.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-9cdd3052553d00a54d6dc9159d6fe055","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA4ccpX9sRCKB3Cnj1Sz/ihzSTKiCGMSIMKEntP2+fCyJsBFB0rlqJ\nahfHLCVwe/SAx1EdAU48qSmUebmhSTjcTee16/94DxnkWC+wpxfk41pJjOn08huO\nHRTh0kxgLJyeZWbm69eR0x6i+zXacGjUd3oC2Pp4GNJRaQcfHNTTP3ZOYpB+QvY7\ncYI89lPU2eIvt1+WkYsR7ETci6tbiwgGoeJMafB7cuzgWTJPgbEk0GQwzIUSnE8a\n5qo/2OnzLiBYN25l69WTcu2ODRt+ZCZ9qlqgEMkCKNk/GG+mnN5AdOTETD9Ow6r9\n24c8GFFqrC2QHZrPFNN8HKQUGka/Xo612QIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"mongo2-builder.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-d3ce051a74914125c9b46c471f36dd4d","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAtXTmPM8vusmn1oberHSkVPBMnPqanc8bQGNgOrBFtyXDCxxdqeDV\nayc6fmMmjTWlQpsLLAPQCTiFVvdXpEK+EdF2cUKUE8KDsJ6GrWo3s1v4TrBVDigR\na1E9kPhram8kcB+4djtY2WJogFlyx9lUWhhqsg1CI4VpamhxbNyYYM/yoJlkPFoQ\n5OcScFkGyLDTTIyR6VfGqXzVnii4y8V3UokAqM+pcQUPzuEq6pqIdQyE7/Fzo4pX\nyah6EkuiLDhGsiykeULKLbRSChAO1/2/Kg418p+X/PCreq6QpEs3rZBbQM6d/i5n\n1AnuhVb6aMWd77NoKzT4xANoYwTwIwZWkwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"judgments-pg0-builder.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-03516b6a21f0ec81ef74845ff5b7d586","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA+ZAhqiWj35gvf1n+G/MPo0vgLCCubqMjMTLyLIA7hEpvU1ROH5LV\nENk3/7jzC7iFMLZMJJiEfBYGehTYnSh/3V7GPkanOLi9bCYK3NJ6G8PXIic48kLS\njXkCL6gGwAhu6fd6khQfjz35MA0ta31dBRsSnk3i+GCzNfouo8GeszolC4zNbQ2Y\nWH+6TnO4GFttW1nKUWprslI73LP7ySMQF1aRgvZSSpSST6wREXGj+GdnNgwFOJAr\n9yGVBOJVe5fqokwywyPQ0LXdCW0esfam677xGGbYvcCzS8BB11uOQqAcpGUE3HN0\nlDPGidSEgVoQKi7rry1E1D50rO5CGIaQIwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"redis1-builder.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-17b5a2703b7cc084ca109eef62f1ab06","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAu+ZabVcbQmc5TmoJuK2NlDY/1Xa6IJ/SZSRhiHnx9NYq0rt2/4Wq\n6nl/QzGZAgvoTe20CdrMek3JdfJ5Wd/ZXpQEvuqOtLvOV9ID792XriQas+GRumVJ\nZg2A0BM57iMvRBnbLXpyYSB6BAlS7X8no9gFvMtZkBqlq4eQbq0MT76iCNOGnPHV\n0/b+EdqF1QFgVAa9EeZ6uKOJb1EsdOk/sTGljEgnPNXf2KP84qtUao3nRaZ6hMEq\nZYO0Ar2Ks/4kumCG02T4tdiIfd9Pc26mbHyjVw1uylXyEY9HSHLl6EAz2ZylJdl6\n3y/Lhj9DiPzkGRiMUqKEH+UiKUXEy531YwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"zookeeper0.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-ef5a661ec424b6de9570a3eb267c2c89","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAu2K7BXWxVzkkFCgF4BQpiuH4H6FgbKxPcPd83SDMPTr3Gm5JB8Qr\nHxMPo5nqgk8X1fsVNDhiry+AMBzeDsDYpvYDXJntX8VxMXOVDtJTzJdroPtFd+g8\nTwJHTh373+5thuEUH5MgtqzX+BBTz2XFfjLeHphusDoifHMV/UxYIsiFbVyySM3D\n07sV//FUChY/Qyms21SgF6UJsZnB5hofSW7r3qsNyTf5XjiUj09KVHaTdUympRZX\nl/IKu1aw8DPZIc2ogDrVnl+aw5817990+aRECdxO0N61F6caaMWb+2Z49EpLeaoS\n8nzzTMJA/WTVWwusSDarIyFo0Ja4oi1EVQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":true,"chef_type":"client","name":"terrace","json_class":"Chef::ApiClient","_rev":"1-91f8a6cb55ba82036decc3166d6e0a9a","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAwQP63Hs/2j58iwBaW5p/ZFfStMBqj7PeYX07Uj5/60T7kc7aK+VU\nTYjwBKFu3Sgo5ziTpXk/Yn4/DUqXYYk9qFhrk1IS9YsBBV/w+Ci00jV5ltDP7ixp\nIB95vZg790+dlar/j6pY3DEF4eV8b0k6meqyC7hUAJFiobYb7jidkmo1wDgA8YUx\nZOxsAZCqd8U78FG4x+sSXWXey9Ohu5/p/ypcabi6ELfdT7KKGFX7HSfp+WgA902g\nHWLPXM91eN+q/eWmPWCjINYLgRb8g3zmQvatCcaVe2g23e8MucMyFUbH91JJpnr7\nd7KQat6w/5M8IfuDNAj3bVEB2wxxmH87EQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"sensu.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-70be9d265b531fa24a69e539b36a83f9","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA2ZwxipMN6FPUgXnPq/qqM8xPCrOalhjUqR8UX8TJFoO2glFjRBwE\nXZLXC7x++StngwW6EBHESk0MR+0pW/eH3MVWYnYhLx7fT9d2uowxSOj/aJ4FjXQn\nshRctSR8C4vvFFkSJZ/VEHqOO05DlO18EAfqAUlXdYCgJKcYHdNzU/Nvy1qdx+Hx\nl2Jh7l11IAq0Ai/IYQFGDhlKpw7SjrgP5IJHjgb0Qekj503hnX1efn5xy8TBmeay\nYDXz240l+shUKE/Y7/Gu27HduaDIbspAggGjaejyoOIgoI1guU2KoP1uL7izZeSQ\nnJU+lrfKJiFRHPgLRvI9uMlsV5+TvU/zywIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"foo","json_class":"Chef::ApiClient","_rev":"8-34e0387b552f55dc8e58cf12a8316b83","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEApa7yLrrVQjYod7MDrgQSy811oJ/6faLAntfSEekfQAsT3sYFFA4z\nISYC+1Y+xoomqsc2HPD3hr33FTbsImnEGs731Hoyxm6lszA7AkZE/xnfZL13LZZ0\ntIZg6IABGCPYbJcbmDHBXHUXhdc2+f5J9Rx83QEW4O2qR0YlQMgIXNoL6Z7cewRN\nFQgQ8LI+YadgAfqkNz2UGJC/kGUx6M8XU7uDsMbqfMVLXZPsBnRbyRxblA71arua\nYa3bqd4KsRMcqRIvK2/Ug3JfqDgCoEtHTAfBubXKcbrOrBG1WHsM7jegRtpqdxO+\nTYBd+hhIR8MqUDs4mf3nNTF/ittrlJ4CnwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":true,"chef_type":"client","name":"vpn-east.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"2-6f7ded2284a773494cc55fb39d25755f","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAt7/uZxxNPUGxXtOHDnkqDZcTsKG/jCIyjlsqZTq0daCVCnZ3L/vS\numo0AgvSYiAjT+59sAXObhuuksFdbmw0Z6VEbVIjCfgieHo0oG6r/bWPbWAUpzYU\ntSflyxkjWTPt+dpKJNJfbaqqJz64ed0x68ImNM9N2xBCMwag2XEDKQ5zJUUB/L54\nAVyaKbppjEdoBxe8xEZwCdPCGp16C1XX5N7PtFhA4duQCJcb1qPq/IdjGVX4otFz\nQvu/2bYWXkkjOBx7taW4wQjXMq+UWcz4j30KS11Te+5DOEXdH0PvZ/efLf/frXSV\nICbYpBE1/E0QIAww0gpw2pbwMGrM3eyvlQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"bar","json_class":"Chef::ApiClient","_rev":"1-9874daf5f2e292d93f10a4c02d5a5f41","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAy0imGvEuF+PsIxgPTHnpWytGptSMwIz0Ro9cJXurqmEJkQLQEPia\ng2wQpmiGWEOzqFZnnpGM1Zi5MsCUecteA0tGXFLA/9iLYHLY5nKcsj4vhIMDMRfi\n3RhAdsaGBv54rOch/zPJW6xdx9431DQ4WDKk0vyGsXcyjaxeC88eZO+bG/LS9uJl\n6Yvpwsp/q4AGd8D5MTmDiDz18yPK0CHIugRzY80Dkif6sM+BknhhX3hAj9fwUKDd\n+di6lmT1aJJVHIAsZIB4NRsWSRS9PYnfqoJIdBl8e3Oiwl/RcSQPuQ65VWLzMA8t\nyKpWVzDo6lGecfH7Fx2nL1SsJ31Yj4D3iQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"app1-builder.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-72fefddea6bddfae2d080b8948fd9dfa","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAnuI/IxIWHUfnyBDiqEgtrQJx9Vu19symSsZzxMZDzzdqrPm39BwM\nu8xc1M60I2FccOdZlFcHVh1QmCWXbE5czPrzjhFH3iGUquPnpCavY+b3/mX0oYPy\nVtkvnCapliLKCySA43lGlpeZEI9rutZdU8Ioe/vjyLsXK4eMTB/Plix89bw17gpY\nqJvZ1ZGOeQ8ucM38bl2pfyrYpFFhCgdjYJGsZ+FtiRye1uaMrVw0W5qHSB0n3huS\nJTTfctIfzaOTPKxo69QPU/Iop7DjEvuHAWEQH9ZruJf9nmdcrkH8ySWHUBuloBDA\nWO9gjHnUZyUcQqe9x2obZPFpuGG33Fy+uQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"asdf","json_class":"Chef::ApiClient","_rev":"1-127545f2a81a9a99a1528e2a0c77ba6b","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA02709SMGcz6+yD5TXnO3s6MbGDLUeggXKUuuy41QLKxTOUKeIgAN\nyrsaZEqzgWAspBV5i54eoV4XBF0fIaTiX/5OgV14q9wy2lcUSwfwLs+zntZRnG+i\nXxxzY6liosnq89Y24GZRFsaxwOIRtUWpmrHcRj3K5qqMVIw6Jq7er1RDbSxBQGtX\nGCA7zxVIs6OuesC+E8SmZElHIw0bmpE/X2EjwnOj0Vyr7SDU7wdvHmTLM/uuadml\nmv/jTyq6TvkSzLWAmXdvNF9VdXQD+Z+2Sip9jK4HPKPc+uE32WNeMbx8jikq64XY\nnmgEoIjrvDqMv0Qn4Z9Z9FyeuWDbyU+QPQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"baz","json_class":"Chef::ApiClient","_rev":"1-7185c78e758b6f9c3368146db09b997e","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAmk9X63Ms24HHa/BIrrgb4j4mgl/YJgyorKob1CbIl4JiJ63bhzZ9\nAU7IDqXnEf6oYQGogvwZv7Rlh9SL3U4qF5REK5YCwSFlpk7lKGgKtc/TsCjgn4lB\nfIlTMbNYYo3wpHMck52T6Xs046TkzU37QcETYEENzXqi4FdNpAZdBhZiamMpE2YB\nwUaINEhKrbkewuCGy/CjCsImjyDifwBuidjYf/fdKXhgRtE6W8f74FJjYQ6UluFu\nDvX84SMORpeEvrJRmrZon662wDFarJCfAacyzPBcqXdRVonL5wd4In8X6jAJh6xA\n7SEFuCPPW2TQ3PZ5cjmziGvld4T5uxerhQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"app0-builder.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-3a1f9ae0d8a0d51335b0845d3c9e5a8d","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAspuP7n+ANIekZtOY8e7jiUk2W4eIYYx/e+FoczrVEhvLMlPWOFSo\nacbqrs9lgeD8xkbgbENTYz9xgRUnepgTqZ62H4NXrjcDhXD0682caiOB7WEnijkR\ninUnEZk6reum+Z7R4EkY1FVhQ5ksIXTaPGltE0HQ5PKsX/2B+SDjXfz9hAwwgR70\noxdF0Kr50/gxBAJvP5VgyMKtR911ScVu9ozaU3yESAZtnlVc6Jo2ZM6QCke64CAn\noDJUF/y47okLQZMGeGLROzLOZ/XCd44gfyfqC1FqnBo2Y7v1LQSklCN4vEUg4eIL\nnnrdeKrfoFBPhuj/ae4DzyTw8nkThLRx5wIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"pg0-builder.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-9f7142bfc7cf4c6ff703b88d42277103","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAnPOl04yttBGbYiOTxPRxnA6L/4Hh5yZCg8EKxsBjYoCszCtaQ0rS\nDTq1rYENWWJGkM1ZcRE0kMNCGttPvnftsKoHIo/jNypZydP16hUbXNSR18x0Wvke\nJs1Ei+Zbo2ZDusGhBx+Tk9HdXSLdPYluEXWsfmxPYSjbh4jG4ZQuFMBrDpQVIdVF\nHG/z4Z2gQ9yJ24p9rVuVJm/CcxXkNB32i1BFWvgj/jk5rXDEHH/AMOMb+NmzwEuk\nqCp/B5+XUdlp04DAFykRmMpXXDNs+UVG13529JXqHIMCWdc7TCD+U2ZV/FP/xVfX\nOQjMv949GNmzXDNvq5wpr9Ecm83Thbn00wIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"graphite.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-4a80450c863705eb4c795137ba7cfec0","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAl+fB+FDuIqBJyCD3Vpa6G8SjQBXwghfSDUWrSf0Io+W/pKMc2oEV\nNIlw7GoNWC2/IwGezrWTtyjbNH/p0IspZY+FxwzZXlL8gg4/Xfmvvf0lz1ZDcgId\n/RIZ5JqATLjHeDDLkdF7Qlw55dks09n3nPAZkYPw/HC3gXO68mb53a4qlEQxkRfh\nmdN5tnx9FpXsdrxf13VY/7jG3ApFL5gjGhJEODkI84Nt/TCasd7Lke3/mx3viWO6\nWa5B1Y2E51LSRxCHWk7bb20kJrXW1ONYa6LOr1kl0YassJCekbotxvT/zTosbMTU\ndiXh3ev7X2AHNxJLJG9L4dCIoeyDZi1+3wIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"collectd.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-7d12849eaa83f1f37a09d754cbec5a60","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAtEc/HE7G9fSJ2oP1BfVS5akn2t/9nM1hnFQ2kYnKqfJBXl8Lt9Nu\nfHUyU/HUy6RXCq68V8CZlCJ+GzkjnPv6TTuuqOCwVxhOcsFGAbazuXVq+sQRTWGU\nnf+oTDQGcwVTejgh5IKdRuD60pUFNaBmeSfOa35pSzqCEe+yTgANRcXKJhKbWgPT\nwrn4VnwCiozYmbQ6nMirZhDiQizkXDmYnzkyinVvX+Bq5Y5rGqybEtXZgFJPyi1o\nxt97+gHx/lBn2geQERldcpBoqcZLrkquTmOszIzQBFTPTg8Mu3SaP50gKehsTDAC\nIYr77G9IKIMUQMZpxG5xVBFBeimem4rNBwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"mongo1-builder.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-2414dee99343d598868b9ff73024c6c7","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAz2AxcLSMvYc3211DbMUjQ47O+qINigme3HAhp5dXf3vq8Ks11GMN\nTfh0ZqKUcIrZY/MveHxmSZEaXQObsqCA52wl9NzPnnjWGSZsI0t+VpD9Y5C5A53u\nMHtE5VBPlTvWWDVI0UH8R8ediiw3ATlY+Oo4cFKfjmgAIfKj9Aqhwwyy7OXqR2Rm\nSmpi+OqVy/IWZYfubDxhu0dir6DksmnbJ3+HHN56OFLhMtNeFxSnc7t5/4eX5aWA\noxZO+zI4eocHWJ306TEsFgU4QoIDjJACA/UBgz1yZLRZU4NmCzdxZULJr/qkk6Y0\nmn6uBuh01/6HgcSY/VZxbvPWeQJ2oqEwGQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"kafka2.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-680f7ea41bb84d0ec185776fcfc2345b","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAuR0IGKpPwwlVq6UUZhfwofsTGW+eiWeOtNhjz9VUQLvGgM2cRI5D\nEydTp3sU85nn53Eewu0SDtrEqU1awC6Rio37MLsDhKF4Ic5JMjQnO1QAeh1Uw43h\nRy45dkVimIhVedguHeHu2Cq1D1TByn8Wd0Pxxwsr4WdfQxfVixCNaDQXNng/nGZU\nWfLPeKMMK+Z/1ChZxUfyHza4c2p5Du29HmnLQtnw6cgCPrnQt0X+GCsVM7GboyAn\n5FBsvUP3AjgAlLBRsHLLAu01g1wmNe9Xjnz9Lv6f6fDZ3y+4sBlK4RNSIEtIBFK8\nzCtD7mbcAFF0DeTnLRM+5sqtqqI55qeElQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":true,"chef_type":"client","name":"matthew","json_class":"Chef::ApiClient","_rev":"1-c439df0b3db9b749bf60d910a2451db1","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAurPo7ZEY4dmb7RaOxFa9qZieVD6Tf7hTB3O5ElCaQteNM8gPluT/\nzXD/By8ax0cqJV+ZUxl1UHkSj6Y1hY0Csf7ZGGnQzQNq3lEJpTp3+9CYMx3xGxSV\nCRTnrzKygebCPwnc9yV39ZxCudnQoPPKWmQ+DHooK4bS8sZ75RI3bu3FWii0EIWC\nTpsOQkQx/kq2PTc5FJPXlItuAVw/rY124UMZFKUI00V31C+Ka01E+eXwVWKxIf6U\nwDtL0eJkN5W9ZSXdg16argcvaHwIN9xMrfcP84goJidvMnbUz7YlbsQs58sm+tdu\n/SAM/nokMLiJu0KSE/R8yGb9vbiXmAIQCQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"tasseo.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"2-158b54bce362669a40f0ee6c9f4893ff","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAx/BH2UDKp/MaGp8yhPU/ILWHgn8fhrozG4ukMeMaoEoDUIHJ4y5G\nvoO9iSqKHTOLjGWR0T+U5rQ95sC/fDt1cmj9P575vKToP0EWiXPbelHsGxKvcU1e\nzdqSkIf/u0XZLq0G74RizP430dDseKJ+kRQe2o7+6KYX8qzfVtUpxOzHzwAfCcq0\nu1TPzU0wCPnJyolEZzBamkhfqvTfI74HifMGrxjoqiGdUiRp+AoDazWHbwcu+nBC\nOjuqEbGDl28euCnkWuoQvaEozt3htLSxPvvNW6X+LeLbSnW7pPghal0rRqlHlMwK\n6PGWQ6UBOY/cEckLWxSUZlWT5gOK/Q7EdQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"kafka0.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-0e6ded9dbc22067bcc6010310a312cca","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA75Oi7kIQjdhstiMTzZR36J9K8fJ7qEcRhBg/hw5OFd4B0foCxSSh\nhTcavxY2WyNKBxVPZxUkMeVJZlwTD7d+odU0vZzJaSdKCdxWAYUMhqEhyrH6f3vX\nAI/JPzwd/3bT9CMGisf35skjWNq/110TA0Q2RJYJeCODzbXFuuvtg/v6Crv1TIxm\nVHWyS0NPpb9nbL123vA0HoI+ueVT259NCVlIjXQDlSF3U7eXsW8T08kQBqPWsSZt\nTuNt4GIhXiZdgppm+bSY9lkkHW4eepcLrCZJltmtFccXjanu9Z3eaZeRJibdtMyG\ndUUDwiM5DviIczC+5JglVRHOwyDT06uipwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"app2-builder.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-2d292a47f7c5bf5833bd8af608b847c0","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA7JFkPkGmUeDOLI2lSpm63PGEQsLUSgf81bBN4dKThIVu3Owt2J5u\nS/JvUrFwr3pF7knXeMB7oQdEyd2LXrv1RDbiyezW9D2XTX76M6wZIxzv8UxhnYt+\nzqAiPM7ngMkR9oop2srlh/4vmQiSc0yj1NABT1w7T5DRysqWGTXclczCGpyX7gpX\nfGIJ0gagW+Yi72x2igG2YrR51I2wfjqy8GSRyJfo0h1TUysmCWY+pacYbFcJQwOi\nu7b8YoxrZwcsuSCs+DkL8U2X3Ufpv1KMDDjOuCmabCgS15UEr7cUubsDjOH70KjS\nOQeS1GOlcvLUh7PVmzJhBYHcN4DXYnUYZQIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":true,"chef_type":"client","name":"chef-webui","json_class":"Chef::ApiClient","_rev":"1-5eb223d3194ba8fbaa838ddaf0e1f033","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEArFQgU714LOG1k3EH0LllbGaElrdFEowcnU5KOtbmoK/EDa4Hgd4s\nnMhBOpdH46zw9006KzoyVw1nfZzN0bSX5FpAn6bSisghzGnJ+ePns2BLo6D5HylS\nHMnr6fEoyCf1A2c39XpsFTn9/QRcupqFUB8Adr5DDxMZFfhAxTtNzAhOrUbfXWCC\n86I+HSEnIG/Oe9JxbCbasx9iqCN5CVhLoWcWx1aViDuVEyHitK60VkZKXBMEWNRc\nZC4oZfJcVs9G0/xTvkUz1GOMn/9TR8Hp8abOp1vCHRTw+E441FO8IzG3uu+xlemm\nuB6i6+MQ73CzRKoNNPcWrQK0twOKWi3IiwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"chef-validator","json_class":"Chef::ApiClient","_rev":"1-94e4564f25fc2231730067cfdc385dd3","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAzPGx86k1/XSMu4LQldhCg1EydaC04tiBnuxzjLfS7qbzMb0QLWqy\nTu5Zrq8cIkzx+b+z1bYBTU7Lcq9ED+AY0Qcsrhlkfl5tFgQkfr77m4y2v3sWEDGP\nqyar2HFDgyclKC4PkMGOUGPZpADG8uUTE9WJlwl/JiFpzCcL2hEQYET7HjwYE+Rc\nHX9qUDmztwN+vxTHigKM16MLXMrx/7aAmQqdUGzvRZS9tPpA14akc5oixl2BHGF0\nS+Wz45rHlXBIToUd0OSyJVSqnfg+XuJnzk/wKHN+1MO/IsO7d/tRCFniYMCuwFyQ\nW6H0le+R5loJ14yt4ymw+8NObEpjI9IURwIDAQAB\n-----END RSA PUBLIC KEY-----\n"},{"admin":false,"chef_type":"client","name":"app3-builder.storm.doloreslabs.com","json_class":"Chef::ApiClient","_rev":"1-0804676c3c1802f3b0ce0ae775ba6c33","public_key":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAwGVCKvC74IV6XG3KL54ztVHaSpi8RR4UJwEkiCy8AkMsUonhB0FQ\n/BFJtIdcCuVkw+Y/hwqRXL3g0Ep5cgvFQ7K/myKzQgwAXXpW0LN/OOnSE3AvlN5z\nyWFhuibBXOkaWgjYou4+RNmh7E2qBVJuBs7/h9AY/wHQjOuHeipCXMuKgnX6eUpC\npXEkEl1rY+cP2OPQpxErBn+vuXKFkrZb12UlA0f5fKOPps4+jyLikW6Uml+CRnxp\nNEaAGzRDStFDsVPa2xGbNOw7j9HzER7CVdJxKIC9O4Oqd79UZA/br5F6CwgR04Ig\nFO0kVWM9LDscejsGu+KMEbEh53v8w+sOlwIDAQAB\n-----END RSA PUBLIC KEY-----\n"}]} -------------------------------------------------------------------------------- /spec/fixtures/search/data_bag.json: -------------------------------------------------------------------------------- 1 | {"total":1,"start":0,"rows":[{"chef_type":"data_bag_item","data_bag":"users","name":"data_bag_item_users_adam","json_class":"Chef::DataBagItem","raw_data":{"id":"adam","real_name":"Adam Jacob"},"_rev":"1-fd9bfe8b8b5367bc441cf07a06578126"}]} -------------------------------------------------------------------------------- /spec/fixtures/search/environment.json: -------------------------------------------------------------------------------- 1 | {"total":0,"start":0,"rows":[]} -------------------------------------------------------------------------------- /spec/fixtures/search/role.json: -------------------------------------------------------------------------------- 1 | {"total":21,"start":0,"rows":[{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"base","json_class":"Chef::Role","run_list":["recipe[chef-client::patch]","recipe[sudo]","role[chef_client]","recipe[apt]","recipe[sysctl]","recipe[dolores]","role[monitoring]","recipe[deployer]","recipe[users::admins]","recipe[dolores::hosts]","recipe[ssh_known_hosts]","recipe[munin]","recipe[firewall]","recipe[postfix]","role[log_client]","role[collectd_client]"],"description":"Base server definition","_rev":"6-7771af4b89c99592a5f61525e97193a7","override_attributes":{"chef_client":{"failure_notification_email":"stacktraces+storm@crowdflower.com"},"server_density":{"track_free_plus_cache":true},"postgresql":{"version":"9.0"},"builder_cluster":"storm","aws_env":"storm"}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"sensu_dashboard","json_class":"Chef::Role","run_list":["recipe[sensu::dashboard]","role[sensu_client]"],"description":"sensu role for the monitoring dashboard","_rev":"1-3e76dfc509676401bafee9d1b75ff870","override_attributes":{}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"unit_distribution_aggregator","json_class":"Chef::Role","run_list":["role[service_base]","recipe[unit_distribution_aggregator]"],"description":"installs unit_distribution_aggregator as a service","_rev":"4-5376473d12355f03b8523985427889c4","override_attributes":{"server_density":{"server_template":"app"},"unit_distribution_aggregator":{"kafka":{"partitions":1,"group":"uda-calculator","topic":"judgment-data-stream"},"zookeeper_prefix":"kafka7","version":"0.2.0","pager_enabled":false,"mongo":{"database":"crowdflower","port":27017,"host":"mongo0-builder.storm.doloreslabs.com","collection":"unit_answer_distributions"},"pager_service_key":"d24af7701df2012fb6bf22000a9040cf"}}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"storm_test","json_class":"Chef::Role","run_list":["recipe[apt]","role[base]","role[vpn_client]","recipe[storm]","recipe[storm::nimbus]","recipe[storm::supervisor]"],"description":"Storm Installation Test for Builder","_rev":"6-74f6f7b83ce811b5b2cadcc4237eea6b","override_attributes":{"storm":{"version":"0.8.0-SNAPSHOT","pager_enabled":false,"pager_service_key":"d24af7701df2012fb6bf22000a9040cf","zookeeper_role":"zookeeper"}}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"rtfm_web","json_class":"Chef::Role","run_list":["recipe[phash]"],"description":"RTFM Web Server","_rev":"2-1e55e1c15a49cbc9b30a0e6451870b39","override_attributes":{}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"job_conversion_aggregator","json_class":"Chef::Role","run_list":["role[service_base]","recipe[job_conversion_aggregator]"],"description":"installs job_conversion_aggregator as a service","_rev":"3-d0b0d5b83058255e0f3a18d6fea0d9bd","override_attributes":{"server_density":{"server_template":"app"},"job_conversion_aggregator":{"kafka":{"partitions":1,"group":"jca-aggregator","topic":"job-conversion-stream"},"zookeeper_prefix":"kafka7","version":"0.2.0","pager_enabled":false,"pager_service_key":"d24af7701df2012fb6bf22000a9040cf","redis":{"port":6379,"host":"redis0-service.storm.doloreslabs.com"}}}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"unit_count_aggregator","json_class":"Chef::Role","run_list":["role[service_base]","recipe[unit_count_aggregator]"],"description":"installs unit_count_aggregator as a service","_rev":"5-2e4d539e28dc29f947a159fe29566310","override_attributes":{"unit_count_aggregator":{"kafka":{"partitions":1,"group":"uca-calculator","topic":"judgment-event-stream"},"zookeeper_prefix":"kafka7","version":"0.4.0","pager_enabled":false,"pager_service_key":"d24af7701df2012fb6bf22000a9040cf","redis":{"port":6379,"host":"redis0-service.storm.doloreslabs.com"}},"server_density":{"server_template":"app"}}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"sensu_client","json_class":"Chef::Role","run_list":["recipe[sensu::client]","role[sensu]"],"description":"sensu role for the monitoring client","_rev":"2-739c3961afa85351401dca6382fa4d98","override_attributes":{}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"workset_null_detector","json_class":"Chef::Role","run_list":["role[service_base]","recipe[workset_null_detector]"],"description":"installs workset_null_detector as a service","_rev":"8-649e30d2e66e3bdb9ec20c6b19d1a184","override_attributes":{"server_density":{"server_template":"app"},"workset_null_detector":{"kafka":{"partitions":1,"group":"wnd-consumer","topic":"judgment-data-stream"},"zookeeper_prefix":"kafka7","version":"0.2.0","pager_enabled":false,"pager_service_key":"d24af7701df2012fb6bf22000a9040cf","api_key":"ca84f5cf09a982ade0456b2e9a1342c1dfc11c35"}}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"rtfm_munin_monitors","json_class":"Chef::Role","run_list":["recipe[munin::app_status]","recipe[server_density::shared_apps]","recipe[collectd_plugins::shared_apps]"],"description":"RTFM App Status Munin Monitors","_rev":"2-1805533ccc1e70df7e6be5bddac22ac4","override_attributes":{}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"collectd_client","json_class":"Chef::Role","run_list":["recipe[collectd::client]","recipe[collectd_plugins::default]"],"description":"Collectd Client","_rev":"2-4642a1c9a33f39579fb62b919ab18c93","override_attributes":{}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"sensu_server","json_class":"Chef::Role","run_list":["role[base]","recipe[drives::ebs_drives]","recipe[sensu::server]"],"description":"sensu role for the monitoring server","_rev":"8-8c57aa9fbb11d921a3a14184e01f409a","override_attributes":{"ebs_drives":[{"group":"rabbitmq","mount":"/var/lib/rabbitmq/mnesia","user":"rabbitmq","size":100,"device":"/dev/sdf"}],"aws_env":"storm"}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"sensu_api","json_class":"Chef::Role","run_list":["recipe[sensu::api]","role[sensu_client]"],"description":"sensu role for the monitoring api","_rev":"1-c19e39e653c85c073c89afc258060e64","override_attributes":{}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"sensu","json_class":"Chef::Role","run_list":[],"description":"base sensu role for attribute overrides","_rev":"3-78f14a0932f3bf3648088f6390bd2dba","override_attributes":{}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"monitoring_server","json_class":"Chef::Role","run_list":["recipe[dolores::builder_config]","role[base]","recipe[nginx]","recipe[dolores::crowdflower_certs]","recipe[users::htpasswd]","recipe[munin::server]","recipe[munin::crowdflower]","recipe[server_density]","recipe[server_density::crowdflower]","recipe[collectd_plugins::crowdflower]","recipe[collectd_plugins::ec2]"],"description":"The monitoring server: munin server, logs, etc","_rev":"6-e088ddd1d2df063cb3e8de1dc1d0b854","override_attributes":{"server_density":{"server_template":"monitoring"},"aws_env":"storm"}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"job_distribution_aggregator","json_class":"Chef::Role","run_list":["role[service_base]","recipe[job_distribution_aggregator]"],"description":"installs job_distribution_aggregator as a service","_rev":"4-17a22929f914ad4fbf7456898c77846e","override_attributes":{"server_density":{"server_template":"app"},"job_distribution_aggregator":{"kafka":{"partitions":1,"group":"uja-consumer","topic":"judgment-data-stream"},"zookeeper_prefix":"kafka7","version":"0.2.0","pager_enabled":false,"mongo":{"database":"crowdflower","port":27017,"host":"mongo0-builder.storm.doloreslabs.com","collection":"job_answer_distributions"},"pager_service_key":"d24af7701df2012fb6bf22000a9040cf"}}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"chef_client","json_class":"Chef::Role","run_list":["recipe[chef-client::config]","recipe[chef-client::delete_validation]","recipe[chef-client::no_autorun]","recipe[chef-client::monitor]","recipe[chef-client::lock]"],"description":"Chef Client","_rev":"4-3c92c9b16f8201ac23d7be09d88644ff","override_attributes":{}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"rtfm_collectd_monitors","json_class":"Chef::Role","run_list":["recipe[collectd_plugins::shared_apps]"],"description":"RTFM App Status Collectd Monitors","_rev":"2-d86242df00a6cf6bf8b812749e01c3c8","override_attributes":{}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"workset_distribution_aggregator","json_class":"Chef::Role","run_list":["role[service_base]","recipe[workset_distribution_aggregator]"],"description":"installs workset_distribution_aggregator as a service","_rev":"4-d51b11954217ef2f99cfabba5608b3e9","override_attributes":{"server_density":{"server_template":"app"},"workset_distribution_aggregator":{"kafka":{"partitions":1,"group":"wda-calculator","topic":"judgment-data-stream"},"zookeeper_prefix":"kafka7","version":"0.2.0","pager_enabled":false,"mongo":{"database":"crowdflower","port":27017,"host":"mongo0-builder.storm.doloreslabs.com","collection":"workset_answer_distributions"},"pager_service_key":"d24af7701df2012fb6bf22000a9040cf"}}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"monitoring","json_class":"Chef::Role","run_list":["recipe[server_density]","role[collectd_client]","role[sensu_client]"],"description":"Monitoring suite","_rev":"6-07ce89ef07d652f54a618c098d54924c","override_attributes":{"server_density":{"enabled":true}}},{"chef_type":"role","env_run_lists":{},"default_attributes":{},"name":"storm_base","json_class":"Chef::Role","run_list":["recipe[apt]","role[base]","role[vpn_client]","recipe[storm]","recipe[storm::nimbus]","recipe[storm::drpc]","recipe[storm::supervisor]","recipe[storm::ui]"],"description":"Storm Installation Core for Builder","_rev":"1-85c3237398a50e848c796eeb279df774","override_attributes":{"storm":{"num_supervisor_slots":4,"version":"0.8.0-SNAPSHOT","pager_enabled":false,"pager_service_key":"d24af7701df2012fb6bf22000a9040cf","zookeeper_role":"zookeeper_builder"}}}]} -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'spork' 3 | 4 | Spork.prefork do 5 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 6 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 7 | require 'bundler' 8 | Bundler.require 9 | 10 | require 'rspec' 11 | require 'webmock/rspec' 12 | require 'timecop' 13 | # require 'fakefs' 14 | require 'fileutils' 15 | 16 | require 'spice' 17 | 18 | # Requires supporting files with custom matchers and macros, etc, 19 | # in ./support/ and its subdirectories. 20 | 21 | RSpec.configure do |config| 22 | config.before do 23 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f} 24 | 25 | Timecop.freeze 26 | # Spice.mock 27 | # FakeFS.activate! 28 | end 29 | config.after do 30 | Timecop.return 31 | # FakeFS.deactivate! 32 | end 33 | end 34 | end 35 | 36 | Spork.each_run do 37 | require 'spice' 38 | require File.expand_path("../support/helpers.rb", __FILE__) 39 | # This code will be run each time you run your specs. 40 | end -------------------------------------------------------------------------------- /spec/spice/authentication_spec.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danryan/spice/6fbe443d95cc31e398a8e56d87b176afe8514b0b/spec/spice/authentication_spec.rb -------------------------------------------------------------------------------- /spec/spice/base_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Spice::Base do 4 | let(:base) { Spice::Base.new(:id => 1) } 5 | 6 | describe "#[]" do 7 | it "should be able to call methods using [] with a symbol" do 8 | base[:object_id].should be_an Integer 9 | end 10 | 11 | it "should be able to call methods using [] with a string" do 12 | base['object_id'].should be_an Integer 13 | end 14 | 15 | it "should return nil for missing methods" do 16 | base[:foo].should be_nil 17 | base['foo'].should be_nil 18 | end 19 | end 20 | 21 | describe "#to_hash" do 22 | it "should return a hash" do 23 | base.to_hash.should be_a Hash 24 | base.to_hash['id'].should == 1 25 | end 26 | end 27 | 28 | describe "identical objects" do 29 | it "should have the same object_id" do 30 | base.object_id.should == Spice::Base.get('id' => 1).object_id 31 | end 32 | end 33 | 34 | end 35 | -------------------------------------------------------------------------------- /spec/spice/client_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Spice 4 | describe Client do 5 | 6 | end 7 | end -------------------------------------------------------------------------------- /spec/spice/config_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Spice 4 | describe Config do 5 | describe ".default values" do 6 | it 'should have default values' do 7 | Spice::Config::DEFAULT_SERVER_URL.should == "http://localhost:4000" 8 | Spice::Config::DEFAULT_CHEF_VERSION.should == "0.10.10" 9 | Spice::Config::DEFAULT_USER_AGENT.should == "Spice #{Spice::VERSION}" 10 | Spice::Config::DEFAULT_CONNECTION_OPTIONS.should == {} 11 | end 12 | end 13 | end 14 | end -------------------------------------------------------------------------------- /spec/spice/connection/clients_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Spice::Connection do 4 | 5 | before do 6 | Spice.mock 7 | end 8 | 9 | after do 10 | Spice.reset 11 | end 12 | 13 | let(:connection) do 14 | Spice::Connection.new 15 | end 16 | 17 | describe "#clients" do 18 | before do 19 | stub_get("/search/client?q=%2A%3A%2A&sort=X_CHEF_id_CHEF_X+asc&start=0&rows=1000"). 20 | # with(:query => { "q"=>"*:*", "sort"=>"X_CHEF_id_CHEF_X asc", "start"=>"0", "rows"=>"1000"}). 21 | to_return(:body => fixture('search/client.json')) 22 | end 23 | 24 | it "should return an array of clients" do 25 | clients = connection.clients 26 | clients.should be_an Array 27 | end 28 | end 29 | 30 | describe "#client" do 31 | before do 32 | stub_get("/clients/name").to_return(:body => fixture('clients/show.json')) 33 | end 34 | 35 | it "should return a single client" do 36 | client = connection.client("name") 37 | client.should be_a Spice::Client 38 | client.name.should == "monkeypants" 39 | client.admin.should == true 40 | end 41 | end 42 | 43 | describe "#update_client" do 44 | before do 45 | stub_put("/clients/name"). 46 | with(:body => {:admin => false }). 47 | to_return(:body => fixture('clients/update.json')) 48 | end 49 | 50 | it "should update and return a single client" do 51 | client = connection.update_client("name", :admin => false) 52 | client.should be_a Spice::Client 53 | client.admin.should == false 54 | end 55 | end 56 | 57 | describe "#reregister_client" do 58 | before do 59 | stub_put("/clients/name"). 60 | with(:body => { :private_key => true }). 61 | to_return(:body => fixture('clients/reregister.json')) 62 | end 63 | 64 | it "should update and return a single client with a new private key" do 65 | client = connection.reregister_client("name") 66 | client.should be_a Spice::Client 67 | client.private_key.should_not be_nil 68 | end 69 | end 70 | 71 | describe "#delete_client" do 72 | before do 73 | stub_delete("/clients/name") 74 | end 75 | 76 | it "should delete a client and return nil" do 77 | client = connection.delete_client("name") 78 | client.should be_nil 79 | end 80 | end 81 | 82 | end -------------------------------------------------------------------------------- /spec/spice/connection/cookbooks_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Spice::Connection do 4 | 5 | before do 6 | Spice.mock 7 | end 8 | 9 | after do 10 | Spice.reset 11 | end 12 | 13 | let(:connection) do 14 | Spice::Connection.new 15 | end 16 | 17 | describe "#cookbooks" do 18 | context "version 0.9.x" do 19 | before do 20 | Spice.chef_version = "0.9.18" 21 | stub_get("/cookbooks"). 22 | to_return(:body => fixture('cookbooks/index-0.9.json')) 23 | stub_get("/cookbooks/unicorn"). 24 | to_return(:body => fixture('cookbooks/show-unicorn-0.9.json')) 25 | stub_get("/cookbooks/apache2"). 26 | to_return(:body => fixture('cookbooks/show-apache2-0.9.json')) 27 | end 28 | 29 | it "should return an array of clients" do 30 | cookbooks = connection.cookbooks 31 | cookbooks.should be_an Array 32 | cookbooks.first.should be_a Spice::Cookbook 33 | # cookbooks.first.name.should == "unicorn" 34 | end 35 | end 36 | 37 | context "version 0.10.x" do 38 | 39 | before do 40 | Spice.chef_version = "0.10.10" 41 | stub_get("/cookbooks"). 42 | with(:query => { :num_versions => 'all' }). 43 | to_return(:body => fixture('cookbooks/index-0.10.json')) 44 | end 45 | 46 | it "should return an array of cookbooks" do 47 | cookbooks = connection.cookbooks 48 | cookbooks.should be_an Array 49 | cookbooks.first.should be_a Spice::Cookbook 50 | # cookbooks.first.name.should == "apache2" 51 | end 52 | end 53 | end 54 | 55 | describe "#cookbook" do 56 | context "version 0.9.x" do 57 | before do 58 | Spice.chef_version = "0.9.18" 59 | stub_get("/cookbooks/unicorn"). 60 | to_return(:body => fixture('cookbooks/show-unicorn-0.9.json')) 61 | end 62 | 63 | it "should return a single cookbook" do 64 | cookbook = connection.cookbook("unicorn") 65 | cookbook.should be_a Spice::Cookbook 66 | cookbook.name.should == "unicorn" 67 | end 68 | end 69 | 70 | context "version 0.10.x" do 71 | before do 72 | Spice.chef_version = "0.10.10" 73 | stub_get("/cookbooks/apache2"). 74 | to_return(:body => fixture('cookbooks/show-0.10.json')) 75 | end 76 | 77 | it "should return a single cookbook" do 78 | cookbook = connection.cookbook("apache2") 79 | cookbook.should be_a Spice::Cookbook 80 | cookbook.name.should == "apache2" 81 | end 82 | end 83 | 84 | end 85 | end -------------------------------------------------------------------------------- /spec/spice/connection/data_bags_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Spice::Connection do 4 | 5 | before do 6 | Spice.mock 7 | end 8 | 9 | after do 10 | Spice.reset 11 | end 12 | 13 | let(:connection) do 14 | Spice::Connection.new 15 | end 16 | 17 | describe "#data_bags" do 18 | before do 19 | stub_get("/search/users?q=%2A%3A%2A&sort=X_CHEF_id_CHEF_X+asc&start=0&rows=1000"). 20 | to_return(:body => fixture('search/data_bag.json')) 21 | stub_get("/data"). 22 | to_return(:body => fixture('data_bags/index.json')) 23 | end 24 | 25 | it "should return an array of data bags" do 26 | data_bags = connection.data_bags 27 | data_bags.should be_an Array 28 | data_bags.first.should be_a Spice::DataBag 29 | # puts data_bags.first.inspect 30 | data_bags.first.name.should == "users" 31 | data_bags.first.items.should be_an Array 32 | data_bags.first.items.first.should be_a Spice::DataBagItem 33 | data_bags.first.items.first.id.should == "adam" 34 | data_bags.first.items.first.real_name.should == "Adam Jacob" 35 | # data_bags.first.items.first.should respond_to(:real_name) 36 | # data_bags.first.items.first.should_not respond_to(:bad_method) 37 | end 38 | end 39 | 40 | describe "#data_bag" do 41 | before do 42 | stub_get("/search/users?q=%2A%3A%2A&sort=X_CHEF_id_CHEF_X+asc&start=0&rows=1000"). 43 | to_return(:body => fixture('search/data_bag.json')) 44 | stub_get("/data/users").to_return(:body => fixture('data_bags/show.json')) 45 | end 46 | 47 | it "should return a single data bag" do 48 | data_bag = connection.data_bag("users") 49 | data_bag.should be_a Spice::DataBag 50 | data_bag.name.should == "users" 51 | data_bag.items.first.should be_a Spice::DataBagItem 52 | data_bag.items.first.id.should == "adam" 53 | data_bag.items.first.real_name.should == "Adam Jacob" 54 | end 55 | end 56 | 57 | describe "#data_bag_item" do 58 | before do 59 | stub_get("/data/users/adam"). 60 | to_return(:body => fixture('data_bags/show.json')) 61 | end 62 | 63 | it "should return a data bag item" do 64 | data_bag_item = connection.data_bag_item("users", "adam") 65 | data_bag_item.should be_a Spice::DataBagItem 66 | end 67 | end 68 | 69 | describe "#create_data_bag" do 70 | before do 71 | stub_post("/data"). 72 | with(:body => { :name => "users"}). 73 | to_return(:body => fixture('data_bags/create.json')) 74 | end 75 | 76 | it "should create a client" do 77 | data_bag = connection.create_data_bag("users") 78 | data_bag.should be_a Spice::DataBag 79 | data_bag.name.should == "users" 80 | data_bag.items.should be_an Array 81 | data_bag.items.should be_empty 82 | end 83 | end 84 | 85 | describe "#create_data_bag_item" do 86 | before do 87 | stub_post("/data/users"). 88 | with(:body => { :id => "adam", :real_name => "Adam Jacob" }). 89 | to_return(:body => fixture('data_bag_items/create.json')) 90 | end 91 | 92 | it "should create a data bag item and return it" do 93 | data_bag_item = connection.create_data_bag_item("users", :id => "adam", :real_name => "Adam Jacob") 94 | data_bag_item.should be_a Spice::DataBagItem 95 | data_bag_item.id.should == "adam" 96 | data_bag_item.real_name.should == "Adam Jacob" 97 | end 98 | end 99 | 100 | describe "#update_data_bag_item" do 101 | before do 102 | stub_put("/data/users/adam"). 103 | with(:body => {:id => "adam", :real_name => "Adam Brent Jacob"} ). 104 | to_return(:body => fixture('data_bag_items/update.json')) 105 | end 106 | 107 | it "should update a data bag item and return it" do 108 | data_bag_item = connection.update_data_bag_item("users", "adam", :id => "adam", :real_name => "Adam Brent Jacob") 109 | data_bag_item.should be_a Spice::DataBagItem 110 | data_bag_item.id.should == "adam" 111 | data_bag_item.real_name.should == "Adam Brent Jacob" 112 | end 113 | end 114 | 115 | describe "#delete_data_bag_item" do 116 | before do 117 | stub_delete("/data/users/adam") 118 | end 119 | 120 | it "should delete a data bag item and return nil" do 121 | data_bag_item = connection.delete_data_bag_item("users", "adam") 122 | data_bag_item.should be_nil 123 | end 124 | end 125 | 126 | end -------------------------------------------------------------------------------- /spec/spice/connection_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Spice::Connection do 4 | let(:keys) { Spice::Config::VALID_OPTIONS_KEYS } 5 | 6 | after do 7 | Spice.reset 8 | end 9 | 10 | context "with module configuration" do 11 | before do 12 | Spice.setup do |s| 13 | keys.each do |key| 14 | s.send("#{key}=", key) 15 | end 16 | end 17 | end 18 | 19 | it "should inherit module configuration" do 20 | connection = Spice::Connection.new 21 | keys.each do |key| 22 | connection.send(key).should == key 23 | end 24 | end 25 | end 26 | 27 | context "with class configuration" do 28 | before do 29 | @configuration = { 30 | :user_agent => "Spice Fake User Agent", 31 | :server_url => "http://chef.example.com:4000", 32 | :chef_version => "0.9.18", 33 | :client_name => "admin", 34 | :client_key => Spice.read_key_file(fake_key), 35 | :connection_options => { :timeout => 10 }, 36 | :middleware => Proc.new {} 37 | } 38 | end 39 | 40 | context "during initialization" do 41 | it "should override module configuration" do 42 | connection = Spice::Connection.new(@configuration) 43 | keys.each do |key| 44 | connection.send(key).should == @configuration[key] 45 | end 46 | end 47 | end 48 | 49 | context "after initialization" do 50 | it "should override module configuration after initialization" do 51 | connection = Spice::Connection.new 52 | @configuration.each do |key, value| 53 | connection.send("#{key}=", value) 54 | end 55 | keys.each do |key| 56 | connection.send(key).should == @configuration[key] 57 | end 58 | end 59 | 60 | end 61 | end 62 | 63 | end -------------------------------------------------------------------------------- /spec/spice/cookbook_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Spice 4 | describe Cookbook do 5 | 6 | end 7 | end -------------------------------------------------------------------------------- /spec/spice/data_bag_item_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Spice 4 | describe DataBagItem do 5 | 6 | end 7 | end -------------------------------------------------------------------------------- /spec/spice/data_bag_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Spice 4 | describe DataBag do 5 | 6 | end 7 | end -------------------------------------------------------------------------------- /spec/spice/environment_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Spice 4 | describe Environment do 5 | 6 | end 7 | end -------------------------------------------------------------------------------- /spec/spice/node_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Spice 4 | describe Node do 5 | 6 | end 7 | end -------------------------------------------------------------------------------- /spec/spice/role_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | module Spice 4 | describe Role do 5 | 6 | end 7 | end -------------------------------------------------------------------------------- /spec/spice_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Spice do 4 | after do 5 | Spice.reset 6 | end 7 | 8 | describe '.new' do 9 | it "returns a Spice::Connection" do 10 | Spice.new.should be_a Spice::Connection 11 | end 12 | end 13 | end -------------------------------------------------------------------------------- /spec/support/helpers.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # unless ENV['CI'] 3 | # require 'simplecov' 4 | # SimpleCov.start do 5 | # add_filter 'spec' 6 | # end 7 | # end 8 | 9 | require 'spice' 10 | require 'rspec' 11 | require 'timecop' 12 | require 'webmock/rspec' 13 | 14 | def a_delete(path, server_url=Spice.server_url) 15 | a_request(:delete, server_url + path) 16 | end 17 | 18 | def a_get(path, server_url=Spice.server_url) 19 | a_request(:get, server_url + path) 20 | end 21 | 22 | def a_post(path, server_url=Spice.server_url) 23 | a_request(:post, server_url + path) 24 | end 25 | 26 | def a_put(path, server_url=Spice.server_url) 27 | a_request(:put, server_url + path) 28 | end 29 | 30 | def stub_delete(path, server_url=Spice.server_url) 31 | stub_request(:delete, server_url + path) 32 | end 33 | 34 | def stub_get(path, server_url=Spice.server_url) 35 | stub_request(:get, server_url + path) 36 | end 37 | 38 | def stub_post(path, server_url=Spice.server_url) 39 | stub_request(:post, server_url + path) 40 | end 41 | 42 | def stub_put(path, server_url=Spice.server_url) 43 | stub_request(:put, server_url + path) 44 | end 45 | 46 | def fixture_path 47 | File.expand_path("../../fixtures", __FILE__) 48 | end 49 | 50 | def fixture(file) 51 | File.new(fixture_path + '/' + file) 52 | end 53 | 54 | def fake_key 55 | File.expand_path("../../fixtures/client.pem", __FILE__) 56 | end 57 | 58 | alias :fake_key_path :fake_key 59 | -------------------------------------------------------------------------------- /spice.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "spice/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "spice" 7 | s.version = Spice::VERSION 8 | s.platform = Gem::Platform::RUBY 9 | s.authors = [ "Dan Ryan" ] 10 | s.email = [ "hi@iamdanryan.com" ] 11 | s.homepage = "https://github.com/danryan/spice" 12 | s.summary = %q{Chef API wrapper} 13 | s.description = %q{Spice is a zesty Chef API wrapper. Its primary purpose is to let you easily integrate your apps with a Chef server easily. Spice provides support for the entire released Chef API} 14 | s.license = "MIT" 15 | 16 | s.rubyforge_project = "spice" 17 | s.add_dependency "faraday", "~> 0.8.0" 18 | s.add_dependency "mixlib-authentication", '~> 1.1.4' 19 | s.add_dependency "multi_json", '~> 1.3.6' 20 | 21 | s.add_development_dependency 'rspec', '>= 2.8.0' 22 | s.add_development_dependency "webmock", ">= 1.8.2" 23 | s.add_development_dependency "timecop", ">= 0.3.5" 24 | s.add_development_dependency 'spork', '>= 1.0.0.rc2' 25 | 26 | s.files = `git ls-files`.split("\n") 27 | s.test_files = `git ls-files -- spec/*`.split("\n") 28 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 29 | s.require_paths = ["lib"] 30 | end 31 | --------------------------------------------------------------------------------