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,12 @@
1
+ RSpec::Matchers.define :each do |check|
2
+ match do |actual|
3
+ actual.each_with_index do |index, o|
4
+ @object = o
5
+ index.should check
6
+ end
7
+ end
8
+
9
+ failure_message_for_should do |actual|
10
+ "at[#{@object}] #{check.failure_message_for_should}"
11
+ end
12
+ end
@@ -0,0 +1,237 @@
1
+ shared_examples_for "a Ridley Resource" do |resource_klass|
2
+ let(:connection) { double('connection') }
3
+ let(:active_connection) { double('active-connection') }
4
+ let(:response) { double('response') }
5
+
6
+ describe "ClassMethods" do
7
+ subject { resource_klass }
8
+
9
+ describe "::all" do
10
+ it "sends a get request for the class' resource_path using the given connection" do
11
+ response.stub(:body) { Hash.new }
12
+ connection.should_receive(:get).with(subject.resource_path).and_return(response)
13
+
14
+ subject.all(connection)
15
+ end
16
+ end
17
+
18
+ describe "::find" do
19
+ it "delegates to find!" do
20
+ id = double('id')
21
+ subject.should_receive(:find!).with(connection, id)
22
+
23
+ subject.find(connection, id)
24
+ end
25
+
26
+ context "when the resource is not found" do
27
+ it "returns nil" do
28
+ pending
29
+ end
30
+ end
31
+ end
32
+
33
+ describe "::find!" do
34
+ it "sends a get request to the given connection to the resource_path of the class for the given chef_id" do
35
+ chef_id = "ridley_test"
36
+ response.stub(:body) { Hash.new }
37
+ connection.should_receive(:get).with("#{subject.resource_path}/#{chef_id}").and_return(response)
38
+
39
+ subject.find(connection, chef_id)
40
+ end
41
+
42
+ context "when the resource is not found" do
43
+ it "raises a Ridley::Errors::HTTPNotFound error" do
44
+ pending
45
+ end
46
+ end
47
+ end
48
+
49
+ describe "::create" do
50
+ it "sends a post request to the given connection using the includer's resource_path" do
51
+ attrs = {
52
+ first_name: "jamie",
53
+ last_name: "winsor"
54
+ }
55
+
56
+ response.stub(:body) { attrs }
57
+ connection.should_receive(:post).with(subject.resource_path, duck_type(:to_json)).and_return(response)
58
+
59
+ subject.create(connection, attrs)
60
+ end
61
+ end
62
+
63
+ describe "::delete" do
64
+ it "sends a delete request to the given connection using the includer's resource_path for the given string" do
65
+ response.stub(:body) { Hash.new }
66
+ connection.should_receive(:delete).with("#{subject.resource_path}/ridley-test").and_return(response)
67
+
68
+ subject.delete(connection, "ridley-test")
69
+ end
70
+
71
+ it "accepts an object that responds to 'chef_id'" do
72
+ object = double("obj")
73
+ object.stub(:chef_id) { "hello" }
74
+ response.stub(:body) { Hash.new }
75
+ connection.should_receive(:delete).with("#{subject.resource_path}/#{object.chef_id}").and_return(response)
76
+
77
+ subject.delete(connection, object)
78
+ end
79
+ end
80
+
81
+ describe "::delete_all" do
82
+ it "sends a delete request for every object in the collection" do
83
+ pending
84
+ end
85
+ end
86
+
87
+ describe "::update" do
88
+ it "sends a put request to the given connection using the includer's resource_path with the given object" do
89
+ subject.stub(:chef_id) { :name }
90
+ subject.attribute(:name)
91
+ object = subject.new(name: "hello")
92
+ response.stub(:body) { Hash.new }
93
+ connection.should_receive(:put).with("#{subject.resource_path}/#{object.chef_id}", duck_type(:to_json)).and_return(response)
94
+
95
+ subject.update(connection, object)
96
+ end
97
+ end
98
+ end
99
+
100
+ subject { resource_klass.new(connection) }
101
+
102
+ describe "#attribute" do
103
+ it "returns the value of the attribute of the corresponding identifier" do
104
+ subject.attributes.each do |attr, value|
105
+ subject.attribute(attr).should eql(value)
106
+ end
107
+ end
108
+ end
109
+
110
+ describe "#attribute=" do
111
+ it "assigns the desired to the attribute of the corresponding identifier" do
112
+ subject.attributes.each do |attr, value|
113
+ subject.send(:attribute=, attr, "testval")
114
+ end
115
+
116
+ subject.attributes.each do |attr, value|
117
+ subject.attribute(attr).should eql("testval")
118
+ end
119
+ end
120
+ end
121
+
122
+ describe "#attributes" do
123
+ it "returns a hash of attributes" do
124
+ subject.attributes.should be_a(Hash)
125
+ end
126
+
127
+ it "includes attribute_defaults in the attributes" do
128
+ subject.class.stub(:attributes).and_return(Set.new([:val_one]))
129
+ subject.class.stub(:attribute_defaults).and_return(val_one: "value")
130
+
131
+ subject.attributes.should have_key(:val_one)
132
+ subject.attributes[:val_one].should eql("value")
133
+ end
134
+ end
135
+
136
+ describe "#save" do
137
+ context "when the object is valid" do
138
+ before(:each) { subject.stub(:valid?).and_return(true) }
139
+
140
+ it "sends a create message to the implementing class" do
141
+ updated = double('updated')
142
+ updated.stub(:attributes).and_return(Hash.new)
143
+ subject.class.should_receive(:create).with(connection, subject).and_return(updated)
144
+
145
+ subject.save
146
+ end
147
+
148
+ context "when there is an HTTPConflict" do
149
+ it "sends an update message to the implemeneting class" do
150
+ updated = double('updated')
151
+ updated.stub(:[]).and_return(Hash.new)
152
+ updated.stub(:attributes).and_return(Hash.new)
153
+ subject.class.should_receive(:create).and_raise(Ridley::Errors::HTTPConflict.new(updated))
154
+ subject.class.should_receive(:update).with(connection, subject).and_return(updated)
155
+
156
+ subject.save
157
+ end
158
+ end
159
+ end
160
+
161
+ context "when the object is invalid" do
162
+ before(:each) { subject.stub(:valid?).and_return(false) }
163
+
164
+ it "raises an InvalidObject error" do
165
+ lambda {
166
+ subject.save
167
+ }.should raise_error(Ridley::Errors::InvalidResource)
168
+ end
169
+ end
170
+ end
171
+
172
+ describe "#chef_id" do
173
+ it "returns the value of the chef_id attribute" do
174
+ subject.class.attribute(:name)
175
+ subject.class.stub(:chef_id) { :name }
176
+ subject.attributes = { name: "reset" }
177
+
178
+ subject.chef_id.should eql("reset")
179
+ end
180
+ end
181
+
182
+ describe "#from_hash" do
183
+ before(:each) do
184
+ subject.class.attribute(:name)
185
+ @object = subject.from_hash(name: "reset")
186
+ end
187
+
188
+ it "returns an instance of the implementing class" do
189
+ @object.should be_a(subject.class)
190
+ end
191
+
192
+ it "assigns the attributes to the values of the corresponding keys in the given Hash" do
193
+ @object.name.should eql("reset")
194
+ end
195
+ end
196
+
197
+ describe "#to_hash" do
198
+ it "returns a hash" do
199
+ subject.to_hash.should be_a(Hash)
200
+ end
201
+
202
+ it "delegates to .attributes" do
203
+ subject.should_receive(:attributes)
204
+
205
+ subject.to_hash
206
+ end
207
+ end
208
+
209
+ describe "#to_json" do
210
+ it "serializes the objects attributes using MultiJson" do
211
+ MultiJson.should_receive(:dump).with(subject.attributes, kind_of(Hash))
212
+
213
+ subject.to_json
214
+ end
215
+
216
+ it "returns the seralized value" do
217
+ MultiJson.stub(:dump).and_return("{}")
218
+
219
+ subject.to_json.should eql("{}")
220
+ end
221
+ end
222
+
223
+ describe "#from_json" do
224
+ before(:each) do
225
+ subject.class.attribute(:name)
226
+ @object = subject.from_json(%({"name": "reset"}))
227
+ end
228
+
229
+ it "returns an instance of the implementing class" do
230
+ @object.should be_a(subject.class)
231
+ end
232
+
233
+ it "assigns the attributes to the values of the corresponding keys in the given JSON" do
234
+ @object.name.should eql("reset")
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,11 @@
1
+ module Ridley
2
+ module SpecHelpers
3
+ def app_root_path
4
+ Pathname.new(File.expand_path('../../../', __FILE__))
5
+ end
6
+
7
+ def fixtures_path
8
+ app_root_path.join('spec/fixtures')
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,167 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ridley::Connection do
4
+ let(:server_url) { "https://api.opscode.com" }
5
+ let(:client_name) { "reset" }
6
+ let(:client_key) { fixtures_path.join("reset.pem").to_s }
7
+ let(:organization) { "vialstudios" }
8
+
9
+ let(:config) do
10
+ {
11
+ server_url: server_url,
12
+ client_name: client_name,
13
+ client_key: client_key,
14
+ organization: organization
15
+ }
16
+ end
17
+
18
+ describe "ClassMethods" do
19
+ subject { Ridley::Connection }
20
+
21
+ describe "::initialize" do
22
+ let(:server_url) { "https://api.opscode.com/some_path" }
23
+
24
+ describe "parsing the 'server_url' option" do
25
+ before(:each) do
26
+ @conn = subject.new(
27
+ server_url: server_url,
28
+ client_name: client_name,
29
+ client_key: client_key
30
+ )
31
+ end
32
+
33
+ it "assigns a 'host' attribute from the given 'server_url' option" do
34
+ @conn.host.should eql("api.opscode.com")
35
+ end
36
+
37
+ it "assigns a 'scheme' attribute from the given 'server_url' option" do
38
+ @conn.scheme.should eql("https")
39
+ end
40
+
41
+ it "sets a 'path_prefix' to the root of the given 'server_url' option" do
42
+ @conn.path_prefix.should eql("/")
43
+ end
44
+ end
45
+
46
+ describe "specifying an 'organization' option" do
47
+ before(:each) do
48
+ @conn = subject.new(
49
+ server_url: server_url,
50
+ client_name: client_name,
51
+ client_key: client_key,
52
+ organization: organization
53
+ )
54
+ end
55
+
56
+ it "assigns the value of the 'organization' option to an 'organization' attribute" do
57
+ @conn.organization.should eql(organization)
58
+ end
59
+
60
+ it "sets the 'path_prefix' of the connection the organization sub URI" do
61
+ @conn.path_prefix.should eql("/organizations/#{organization}")
62
+ end
63
+ end
64
+
65
+ it "raises 'ArgumentError' if a value for server_url is not given" do
66
+ lambda {
67
+ subject.new(
68
+ client_name: client_name,
69
+ client_key: client_key
70
+ )
71
+ }.should raise_error(ArgumentError, "missing required option(s): 'server_url'")
72
+ end
73
+
74
+ it "raises if a value for client_name is not given" do
75
+ lambda {
76
+ subject.new(
77
+ server_url: server_url,
78
+ client_key: client_key
79
+ )
80
+ }.should raise_error(ArgumentError, "missing required option(s): 'client_name'")
81
+ end
82
+
83
+ it "raises if a value for client_key is not given" do
84
+ lambda {
85
+ subject.new(
86
+ server_url: server_url,
87
+ client_name: client_name
88
+ )
89
+ }.should raise_error(ArgumentError, "missing required option(s): 'client_key'")
90
+ end
91
+ end
92
+
93
+ describe "::sync" do
94
+ it "raises a Ridley::Errors::InternalError if no block is given" do
95
+ lambda {
96
+ subject.sync(config)
97
+ }.should raise_error(Ridley::Errors::InternalError)
98
+ end
99
+ end
100
+ end
101
+
102
+ subject do
103
+ Ridley::Connection.new(config)
104
+ end
105
+
106
+ describe "#sync" do
107
+ it "raises a Ridley::Errors::InternalError if no block is given" do
108
+ lambda {
109
+ subject.sync
110
+ }.should raise_error(Ridley::Errors::InternalError)
111
+ end
112
+
113
+ describe "HTTP Request" do
114
+ describe "#get" do
115
+ it "appends the given path to the connection's server_uri path and sends a get request to it" do
116
+ stub_request(:get, subject.build_url("cookbooks")).
117
+ to_return(status: 200, body: "{}")
118
+
119
+ subject.get("cookbooks")
120
+ end
121
+ end
122
+
123
+ describe "#put" do
124
+ it "appends the given path to the connection's server_uri path and sends a put request to it" do
125
+ stub_request(:put, subject.build_url("cookbooks")).
126
+ with(body: "content").
127
+ to_return(status: 200, body: "{}")
128
+
129
+ subject.put("cookbooks", "content")
130
+ end
131
+ end
132
+
133
+ describe "#post" do
134
+ it "appends the given path to the connection's server_uri path and sends a post request to it" do
135
+ stub_request(:post, subject.build_url("cookbooks")).
136
+ with(body: "content").
137
+ to_return(status: 200, body: "{}")
138
+
139
+ subject.post("cookbooks", "content")
140
+ end
141
+ end
142
+
143
+ describe "#delete" do
144
+ it "appends the given path to the connection's server_uri path and sends a delete request to it" do
145
+ stub_request(:delete, subject.build_url("cookbooks/nginx")).
146
+ to_return(status: 200, body: "{}")
147
+
148
+ subject.delete("cookbooks/nginx")
149
+ end
150
+ end
151
+ end
152
+
153
+ describe "api_type" do
154
+ it "returns :foss if the organization is not set" do
155
+ subject.stub(:organization).and_return(nil)
156
+
157
+ subject.api_type.should eql(:foss)
158
+ end
159
+
160
+ it "returns :hosted if the organization is set" do
161
+ subject.stub(:organization).and_return("vialstudios")
162
+
163
+ subject.api_type.should eql(:hosted)
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ridley::Errors do
4
+ describe Ridley::Errors::HTTPError do
5
+ describe "ClassMethods" do
6
+ subject { Ridley::Errors::HTTPError }
7
+
8
+ before(:each) do
9
+ @original = Ridley::Errors::HTTPError.class_variable_get :@@error_map
10
+ Ridley::Errors::HTTPError.class_variable_set :@@error_map, Hash.new
11
+ end
12
+
13
+ after(:each) do
14
+ Ridley::Errors::HTTPError.class_variable_set :@@error_map, @original
15
+ end
16
+
17
+ describe "::register_error" do
18
+ it "adds an item to the error map" do
19
+ subject.register_error(400)
20
+
21
+ subject.error_map.should have(1).item
22
+ end
23
+
24
+ it "adds a key of the given status code with a value of the class inheriting from HTTPError" do
25
+ class RidleyTestHTTPError < Ridley::Errors::HTTPError
26
+ register_error(400)
27
+ end
28
+
29
+ subject.error_map[400].should eql(RidleyTestHTTPError)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end