ridley 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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