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.
- data/.gitignore +17 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/Guardfile +20 -0
- data/LICENSE +201 -0
- data/README.md +273 -0
- data/Thorfile +48 -0
- data/lib/ridley.rb +48 -0
- data/lib/ridley/connection.rb +131 -0
- data/lib/ridley/context.rb +25 -0
- data/lib/ridley/dsl.rb +58 -0
- data/lib/ridley/errors.rb +82 -0
- data/lib/ridley/log.rb +10 -0
- data/lib/ridley/middleware.rb +19 -0
- data/lib/ridley/middleware/chef_auth.rb +45 -0
- data/lib/ridley/middleware/chef_response.rb +28 -0
- data/lib/ridley/middleware/parse_json.rb +107 -0
- data/lib/ridley/resource.rb +305 -0
- data/lib/ridley/resources/client.rb +75 -0
- data/lib/ridley/resources/cookbook.rb +27 -0
- data/lib/ridley/resources/data_bag.rb +75 -0
- data/lib/ridley/resources/data_bag_item.rb +186 -0
- data/lib/ridley/resources/environment.rb +45 -0
- data/lib/ridley/resources/node.rb +34 -0
- data/lib/ridley/resources/role.rb +33 -0
- data/lib/ridley/version.rb +3 -0
- data/ridley.gemspec +39 -0
- data/spec/acceptance/client_resource_spec.rb +135 -0
- data/spec/acceptance/cookbook_resource_spec.rb +46 -0
- data/spec/acceptance/data_bag_item_resource_spec.rb +171 -0
- data/spec/acceptance/data_bag_resource_spec.rb +51 -0
- data/spec/acceptance/environment_resource_spec.rb +171 -0
- data/spec/acceptance/node_resource_spec.rb +218 -0
- data/spec/acceptance/role_resource_spec.rb +200 -0
- data/spec/fixtures/reset.pem +27 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/each_matcher.rb +12 -0
- data/spec/support/shared_examples/ridley_resource.rb +237 -0
- data/spec/support/spec_helpers.rb +11 -0
- data/spec/unit/ridley/connection_spec.rb +167 -0
- data/spec/unit/ridley/errors_spec.rb +34 -0
- data/spec/unit/ridley/middleware/chef_auth_spec.rb +14 -0
- data/spec/unit/ridley/middleware/chef_response_spec.rb +213 -0
- data/spec/unit/ridley/middleware/parse_json_spec.rb +74 -0
- data/spec/unit/ridley/resource_spec.rb +214 -0
- data/spec/unit/ridley/resources/client_spec.rb +47 -0
- data/spec/unit/ridley/resources/cookbook_spec.rb +5 -0
- data/spec/unit/ridley/resources/data_bag_item_spec.rb +42 -0
- data/spec/unit/ridley/resources/data_bag_spec.rb +15 -0
- data/spec/unit/ridley/resources/environment_spec.rb +73 -0
- data/spec/unit/ridley/resources/node_spec.rb +5 -0
- data/spec/unit/ridley/resources/role_spec.rb +5 -0
- data/spec/unit/ridley_spec.rb +32 -0
- 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,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
|