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,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
|
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
|