ridley 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. data/.gitignore +17 -0
  2. data/.travis.yml +5 -0
  3. data/Gemfile +3 -0
  4. data/Guardfile +20 -0
  5. data/LICENSE +201 -0
  6. data/README.md +273 -0
  7. data/Thorfile +48 -0
  8. data/lib/ridley.rb +48 -0
  9. data/lib/ridley/connection.rb +131 -0
  10. data/lib/ridley/context.rb +25 -0
  11. data/lib/ridley/dsl.rb +58 -0
  12. data/lib/ridley/errors.rb +82 -0
  13. data/lib/ridley/log.rb +10 -0
  14. data/lib/ridley/middleware.rb +19 -0
  15. data/lib/ridley/middleware/chef_auth.rb +45 -0
  16. data/lib/ridley/middleware/chef_response.rb +28 -0
  17. data/lib/ridley/middleware/parse_json.rb +107 -0
  18. data/lib/ridley/resource.rb +305 -0
  19. data/lib/ridley/resources/client.rb +75 -0
  20. data/lib/ridley/resources/cookbook.rb +27 -0
  21. data/lib/ridley/resources/data_bag.rb +75 -0
  22. data/lib/ridley/resources/data_bag_item.rb +186 -0
  23. data/lib/ridley/resources/environment.rb +45 -0
  24. data/lib/ridley/resources/node.rb +34 -0
  25. data/lib/ridley/resources/role.rb +33 -0
  26. data/lib/ridley/version.rb +3 -0
  27. data/ridley.gemspec +39 -0
  28. data/spec/acceptance/client_resource_spec.rb +135 -0
  29. data/spec/acceptance/cookbook_resource_spec.rb +46 -0
  30. data/spec/acceptance/data_bag_item_resource_spec.rb +171 -0
  31. data/spec/acceptance/data_bag_resource_spec.rb +51 -0
  32. data/spec/acceptance/environment_resource_spec.rb +171 -0
  33. data/spec/acceptance/node_resource_spec.rb +218 -0
  34. data/spec/acceptance/role_resource_spec.rb +200 -0
  35. data/spec/fixtures/reset.pem +27 -0
  36. data/spec/spec_helper.rb +25 -0
  37. data/spec/support/each_matcher.rb +12 -0
  38. data/spec/support/shared_examples/ridley_resource.rb +237 -0
  39. data/spec/support/spec_helpers.rb +11 -0
  40. data/spec/unit/ridley/connection_spec.rb +167 -0
  41. data/spec/unit/ridley/errors_spec.rb +34 -0
  42. data/spec/unit/ridley/middleware/chef_auth_spec.rb +14 -0
  43. data/spec/unit/ridley/middleware/chef_response_spec.rb +213 -0
  44. data/spec/unit/ridley/middleware/parse_json_spec.rb +74 -0
  45. data/spec/unit/ridley/resource_spec.rb +214 -0
  46. data/spec/unit/ridley/resources/client_spec.rb +47 -0
  47. data/spec/unit/ridley/resources/cookbook_spec.rb +5 -0
  48. data/spec/unit/ridley/resources/data_bag_item_spec.rb +42 -0
  49. data/spec/unit/ridley/resources/data_bag_spec.rb +15 -0
  50. data/spec/unit/ridley/resources/environment_spec.rb +73 -0
  51. data/spec/unit/ridley/resources/node_spec.rb +5 -0
  52. data/spec/unit/ridley/resources/role_spec.rb +5 -0
  53. data/spec/unit/ridley_spec.rb +32 -0
  54. metadata +451 -0
@@ -0,0 +1,27 @@
1
+ module Ridley
2
+ # @author Jamie Winsor <jamie@vialstudios.com>
3
+ class Cookbook
4
+ include Ridley::Resource
5
+
6
+ set_chef_id "name"
7
+ set_chef_type "cookbook"
8
+ set_chef_json_class "Chef::Cookbook"
9
+ set_resource_path "cookbooks"
10
+
11
+ attribute :name
12
+ validates_presence_of :name
13
+ end
14
+
15
+ module DSL
16
+ # Coerces instance functions into class functions on Ridley::Cookbook. This coercion
17
+ # sends an instance of the including class along to the class function.
18
+ #
19
+ # @see Ridley::Context
20
+ #
21
+ # @return [Ridley::Context]
22
+ # a context object to delegate instance functions to class functions on Ridley::Cookbook
23
+ def cookbook
24
+ Context.new(Ridley::Cookbook, self)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,75 @@
1
+ module Ridley
2
+ # @api private
3
+ # @author Jamie Winsor <jamie@vialstudios.com>
4
+ class DBIContext
5
+ attr_reader :data_bag
6
+ attr_reader :connection
7
+
8
+ # @param [Ridley::DataBag] data_bag
9
+ def initialize(data_bag, connection)
10
+ @data_bag = data_bag
11
+ @connection = connection
12
+ end
13
+
14
+ def new(*args)
15
+ Ridley::DataBagItem.send(:new, connection, data_bag, *args)
16
+ end
17
+
18
+ def method_missing(fun, *args, &block)
19
+ Ridley::DataBagItem.send(fun, connection, data_bag, *args, &block)
20
+ end
21
+ end
22
+
23
+ # @author Jamie Winsor <jamie@vialstudios.com>
24
+ class DataBag
25
+ include Ridley::Resource
26
+
27
+ class << self
28
+ # @param [Ridley::Connection] connection
29
+ # @param [String, #chef_id] object
30
+ #
31
+ # @return [nil, Ridley::DataBag]
32
+ def find(connection, object)
33
+ find!(connection, object)
34
+ rescue Errors::HTTPNotFound
35
+ nil
36
+ end
37
+
38
+ # @param [Ridley::Connection] connection
39
+ # @param [String, #chef_id] object
40
+ #
41
+ # @raise [Errors::HTTPNotFound]
42
+ # if a resource with the given chef_id is not found
43
+ #
44
+ # @return [Ridley::DataBag]
45
+ def find!(connection, object)
46
+ chef_id = object.respond_to?(:chef_id) ? object.chef_id : object
47
+ name, uri = connection.get("#{self.resource_path}/#{chef_id}").body.first
48
+ new(connection, name: name)
49
+ end
50
+ end
51
+
52
+ set_chef_id "name"
53
+ set_resource_path "data"
54
+
55
+ attribute :name
56
+ validates_presence_of :name
57
+
58
+ def item
59
+ @dbi_context ||= DBIContext.new(self, connection)
60
+ end
61
+ end
62
+
63
+ module DSL
64
+ # Coerces instance functions into class functions on Ridley::DataBag. This coercion
65
+ # sends an instance of the including class along to the class function.
66
+ #
67
+ # @see Ridley::Context
68
+ #
69
+ # @return [Ridley::Context]
70
+ # a context object to delegate instance functions to class functions on Ridley::DataBag
71
+ def data_bag
72
+ Context.new(Ridley::DataBag, self)
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,186 @@
1
+ module Ridley
2
+ # @author Jamie Winsor <jamie@vialstudios.com>
3
+ class DataBagItem
4
+ include ActiveModel::Validations
5
+ include ActiveModel::Serialization
6
+
7
+ class << self
8
+ # @param [Ridley::Connection] connection
9
+ #
10
+ # @return [Array<Object>]
11
+ def all(connection, data_bag)
12
+ connection.get("#{data_bag.class.resource_path}/#{data_bag.name}").body.collect do |id, location|
13
+ new(connection, data_bag, id: id)
14
+ end
15
+ end
16
+
17
+ # @param [Ridley::Connection] connection
18
+ # @param [Ridley::DataBag] data_bag
19
+ # @param [String, #chef_id] object
20
+ #
21
+ # @return [nil, Ridley::DataBagItem]
22
+ def find(connection, data_bag, object)
23
+ find!(connection, data_bag, object)
24
+ rescue Errors::HTTPNotFound
25
+ nil
26
+ end
27
+
28
+ # @param [Ridley::Connection] connection
29
+ # @param [Ridley::DataBag] data_bag
30
+ # @param [String, #chef_id] object
31
+ #
32
+ # @raise [Errors::HTTPNotFound]
33
+ # if a resource with the given chef_id is not found
34
+ #
35
+ # @return [Ridley::DataBagItem]
36
+ def find!(connection, data_bag, object)
37
+ chef_id = object.respond_to?(:chef_id) ? object.chef_id : object
38
+ new(connection, data_bag).from_hash(connection.get("#{data_bag.class.resource_path}/#{data_bag.name}/#{chef_id}").body)
39
+ end
40
+
41
+ # @param [Ridley::Connection] connection
42
+ # @param [Ridley::DataBag] data_bag
43
+ # @param [#to_hash] object
44
+ #
45
+ # @return [Ridley::DataBagItem]
46
+ def create(connection, data_bag, object)
47
+ resource = new(connection, data_bag, object.to_hash)
48
+ unless resource.valid?
49
+ raise Errors::InvalidResource.new(resource.errors)
50
+ end
51
+
52
+ new_attributes = connection.post("#{data_bag.class.resource_path}/#{data_bag.name}", resource.to_json).body
53
+ resource.from_hash(resource.attributes.merge(new_attributes))
54
+ resource
55
+ end
56
+
57
+ # @param [Ridley::Connection] connection
58
+ # @param [Ridley::DataBag] data_bag
59
+ # @param [String, #chef_id] object
60
+ #
61
+ # @return [Ridley::DataBagItem]
62
+ def delete(connection, data_bag, object)
63
+ chef_id = object.respond_to?(:chef_id) ? object.chef_id : object
64
+ new(connection, data_bag).from_hash(connection.delete("#{data_bag.class.resource_path}/#{data_bag.name}/#{chef_id}").body)
65
+ end
66
+
67
+ # @param [Ridley::Connection] connection
68
+ # @param [Ridley::DataBag] data_bag
69
+ #
70
+ # @return [Array<Ridley::DataBagItem>]
71
+ def delete_all(connection, data_bag)
72
+ mutex = Mutex.new
73
+ deleted = []
74
+ resources = all(connection, data_bag)
75
+
76
+ connection.thread_count.times.collect do
77
+ Thread.new(connection, data_bag, resources, deleted) do |connection, data_bag, resources, deleted|
78
+ while resource = mutex.synchronize { resources.pop }
79
+ result = delete(connection, data_bag, resource)
80
+ mutex.synchronize { deleted << result }
81
+ end
82
+ end
83
+ end.each(&:join)
84
+
85
+ deleted
86
+ end
87
+
88
+ # @param [Ridley::Connection] connection
89
+ # @param [Ridley::DataBag] data_bag
90
+ # @param [#to_hash] object
91
+ #
92
+ # @return [Ridley::DataBagItem]
93
+ def update(connection, data_bag, object)
94
+ resource = new(connection, data_bag, object.to_hash)
95
+ new(connection, data_bag).from_hash(
96
+ connection.put("#{data_bag.class.resource_path}/#{data_bag.name}/#{resource.chef_id}", resource.to_json).body
97
+ )
98
+ end
99
+ end
100
+
101
+ attr_reader :data_bag
102
+
103
+ attr_accessor :attributes
104
+ validates_presence_of :id
105
+
106
+ def initialize(connection, data_bag, attributes = {})
107
+ @connection = connection
108
+ @data_bag = data_bag
109
+ @attributes = attributes
110
+ end
111
+
112
+ # Alias for accessing the value of the 'id' attribute
113
+ #
114
+ # @return [String]
115
+ def chef_id
116
+ @attributes[:id]
117
+ end
118
+ alias_method :id, :chef_id
119
+
120
+ # @param [String, Symbol] key
121
+ #
122
+ # @return [Object]
123
+ def attribute(key)
124
+ @attributes[key]
125
+ end
126
+ alias_method :[], :attribute
127
+
128
+ # @param [String, Symbol] key
129
+ # @param [Object] value
130
+ #
131
+ # @return [Object]
132
+ def attribute=(key, value)
133
+ @attributes[key] = value
134
+ end
135
+ alias_method :[]=, :attribute=
136
+
137
+ # Creates a resource on the target remote or updates one if the resource
138
+ # already exists.
139
+ #
140
+ # @raise [Errors::InvalidResource]
141
+ # if the resource does not pass validations
142
+ #
143
+ # @return [Boolean]
144
+ # true if successful and false for failure
145
+ def save
146
+ raise Errors::InvalidResource.new(self.errors) unless valid?
147
+
148
+ self.attributes = self.class.create(connection, data_bag, self).attributes
149
+ true
150
+ rescue Errors::HTTPConflict
151
+ self.attributes = self.class.update(connection, data_bag, self).attributes
152
+ true
153
+ end
154
+
155
+ # @param [#to_hash] hash
156
+ #
157
+ # @return [Object]
158
+ def from_hash(hash)
159
+ hash = hash.to_hash
160
+
161
+ self.attributes = hash.has_key?(:raw_data) ? hash[:raw_data] : hash
162
+ self
163
+ end
164
+
165
+ # @option options [Boolean] :symbolize_keys
166
+ # @option options [Class, Symbol, String] :adapter
167
+ #
168
+ # @return [String]
169
+ def to_json(options = {})
170
+ MultiJson.dump(self.attributes, options)
171
+ end
172
+ alias_method :as_json, :to_json
173
+
174
+ def to_hash
175
+ self.attributes
176
+ end
177
+
178
+ def to_s
179
+ self.attributes
180
+ end
181
+
182
+ private
183
+
184
+ attr_reader :connection
185
+ end
186
+ end
@@ -0,0 +1,45 @@
1
+ module Ridley
2
+ # @author Jamie Winsor <jamie@vialstudios.com>
3
+ class Environment
4
+ include Ridley::Resource
5
+
6
+ class << self
7
+ # Delete all of the environments on the remote connection. The
8
+ # '_default' environment will never be deleted.
9
+ #
10
+ # @param [Ridley::Connection] connection
11
+ #
12
+ # @return [Array<Ridley::Environment>]
13
+ def delete_all(connection)
14
+ envs = all(connection).reject { |env| env.name.to_s == '_default' }
15
+ envs.collect { |obj| delete(connection, obj) }
16
+ end
17
+ end
18
+
19
+ set_chef_id "name"
20
+ set_chef_type "environment"
21
+ set_chef_json_class "Chef::Environment"
22
+ set_resource_path "environments"
23
+
24
+ attribute :name
25
+ validates_presence_of :name
26
+
27
+ attribute :description, default: String.new
28
+ attribute :default_attributes, default: Hash.new
29
+ attribute :override_attributes, default: Hash.new
30
+ attribute :cookbook_versions, default: Hash.new
31
+ end
32
+
33
+ module DSL
34
+ # Coerces instance functions into class functions on Ridley::Environment. This coercion
35
+ # sends an instance of the including class along to the class function.
36
+ #
37
+ # @see Ridley::Context
38
+ #
39
+ # @return [Ridley::Context]
40
+ # a context object to delegate instance functions to class functions on Ridley::Environment
41
+ def environment
42
+ Context.new(Ridley::Environment, self)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,34 @@
1
+ module Ridley
2
+ # @author Jamie Winsor <jamie@vialstudios.com>
3
+ class Node
4
+ include Ridley::Resource
5
+
6
+ set_chef_id "name"
7
+ set_chef_type "node"
8
+ set_chef_json_class "Chef::Node"
9
+ set_resource_path "nodes"
10
+
11
+ attribute :name
12
+ validates_presence_of :name
13
+
14
+ attribute :chef_environment, default: "_default"
15
+ attribute :automatic, default: Hash.new
16
+ attribute :normal, default: Hash.new
17
+ attribute :default, default: Hash.new
18
+ attribute :override, default: Hash.new
19
+ attribute :run_list, default: Array.new
20
+ end
21
+
22
+ module DSL
23
+ # Coerces instance functions into class functions on Ridley::Node. This coercion
24
+ # sends an instance of the including class along to the class function.
25
+ #
26
+ # @see Ridley::Context
27
+ #
28
+ # @return [Ridley::Context]
29
+ # a context object to delegate instance functions to class functions on Ridley::Node
30
+ def node
31
+ Context.new(Ridley::Node, self)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ module Ridley
2
+ # @author Jamie Winsor <jamie@vialstudios.com>
3
+ class Role
4
+ include Ridley::Resource
5
+
6
+ set_chef_id "name"
7
+ set_chef_type "role"
8
+ set_chef_json_class "Chef::Role"
9
+ set_resource_path "roles"
10
+
11
+ attribute :name
12
+ validates_presence_of :name
13
+
14
+ attribute :description, default: String.new
15
+ attribute :default_attributes, default: Hash.new
16
+ attribute :override_attributes, default: Hash.new
17
+ attribute :run_list, default: Array.new
18
+ attribute :env_run_lists, default: Hash.new
19
+ end
20
+
21
+ module DSL
22
+ # Coerces instance functions into class functions on Ridley::Role. This coercion
23
+ # sends an instance of the including class along to the class function.
24
+ #
25
+ # @see Ridley::Context
26
+ #
27
+ # @return [Ridley::Context]
28
+ # a context object to delegate instance functions to class functions on Ridley::Role
29
+ def role
30
+ Context.new(Ridley::Role, self)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module Ridley
2
+ VERSION = "0.0.1"
3
+ end
data/ridley.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/ridley/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.authors = ["Jamie Winsor"]
6
+ s.email = ["jamie@vialstudios.com"]
7
+ s.description = %q{A reliable Chef API client with a clean syntax}
8
+ s.summary = s.description
9
+ s.homepage = "https://github.com/reset/ridley"
10
+
11
+ s.files = `git ls-files`.split($\)
12
+ s.executables = Array.new
13
+ s.test_files = s.files.grep(%r{^(spec)/})
14
+ s.name = "ridley"
15
+ s.require_paths = ["lib"]
16
+ s.version = Ridley::VERSION
17
+
18
+ s.add_runtime_dependency 'yajl-ruby'
19
+ s.add_runtime_dependency 'mixlib-log'
20
+ s.add_runtime_dependency 'mixlib-authentication'
21
+ s.add_runtime_dependency 'addressable'
22
+ s.add_runtime_dependency 'faraday'
23
+ s.add_runtime_dependency 'multi_json'
24
+ s.add_runtime_dependency 'activemodel'
25
+
26
+ s.add_development_dependency 'rake'
27
+ s.add_development_dependency 'rspec'
28
+ s.add_development_dependency 'fuubar'
29
+ s.add_development_dependency 'spork'
30
+ s.add_development_dependency 'yard'
31
+ s.add_development_dependency 'guard'
32
+ s.add_development_dependency 'guard-rspec'
33
+ s.add_development_dependency 'guard-spork'
34
+ s.add_development_dependency 'guard-yard'
35
+ s.add_development_dependency 'coolline'
36
+ s.add_development_dependency 'redcarpet'
37
+ s.add_development_dependency 'json_spec'
38
+ s.add_development_dependency 'webmock'
39
+ end