ridley 0.7.0.beta → 0.7.0.rc1

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/README.md +51 -54
  2. data/lib/ridley.rb +7 -13
  3. data/lib/ridley/client.rb +251 -0
  4. data/lib/ridley/connection.rb +32 -188
  5. data/lib/ridley/middleware/chef_auth.rb +4 -1
  6. data/lib/ridley/resource.rb +36 -42
  7. data/lib/ridley/resources.rb +3 -0
  8. data/lib/ridley/resources/{client.rb → client_resource.rb} +7 -20
  9. data/lib/ridley/resources/cookbook_resource.rb +121 -0
  10. data/lib/ridley/resources/{data_bag_item.rb → data_bag_item_resource.rb} +52 -63
  11. data/lib/ridley/resources/data_bag_resource.rb +74 -0
  12. data/lib/ridley/resources/encrypted_data_bag_item_resource.rb +55 -0
  13. data/lib/ridley/resources/{environment.rb → environment_resource.rb} +8 -21
  14. data/lib/ridley/resources/{node.rb → node_resource.rb} +24 -37
  15. data/lib/ridley/resources/{role.rb → role_resource.rb} +1 -14
  16. data/lib/ridley/resources/sandbox_resource.rb +86 -0
  17. data/lib/ridley/resources/search.rb +24 -55
  18. data/lib/ridley/sandbox_uploader.rb +118 -0
  19. data/lib/ridley/ssh.rb +2 -2
  20. data/lib/ridley/ssh/worker.rb +2 -1
  21. data/lib/ridley/version.rb +1 -1
  22. data/ridley.gemspec +1 -1
  23. data/spec/acceptance/bootstrapping_spec.rb +1 -1
  24. data/spec/acceptance/client_resource_spec.rb +18 -20
  25. data/spec/acceptance/cookbook_resource_spec.rb +4 -22
  26. data/spec/acceptance/data_bag_item_resource_spec.rb +5 -7
  27. data/spec/acceptance/data_bag_resource_spec.rb +4 -6
  28. data/spec/acceptance/environment_resource_spec.rb +14 -16
  29. data/spec/acceptance/node_resource_spec.rb +12 -14
  30. data/spec/acceptance/role_resource_spec.rb +13 -15
  31. data/spec/acceptance/sandbox_resource_spec.rb +7 -9
  32. data/spec/acceptance/search_resource_spec.rb +6 -8
  33. data/spec/support/shared_examples/ridley_resource.rb +23 -22
  34. data/spec/unit/ridley/client_spec.rb +153 -0
  35. data/spec/unit/ridley/connection_spec.rb +8 -221
  36. data/spec/unit/ridley/resources/{client_spec.rb → client_resource_spec.rb} +4 -4
  37. data/spec/unit/ridley/resources/cookbook_resource_spec.rb +5 -0
  38. data/spec/unit/ridley/resources/{data_bag_item_spec.rb → data_bag_item_resource_spec.rb} +2 -2
  39. data/spec/unit/ridley/resources/{data_bag_spec.rb → data_bag_resource_spec.rb} +3 -3
  40. data/spec/unit/ridley/resources/{environment_spec.rb → environment_resource_spec.rb} +4 -4
  41. data/spec/unit/ridley/resources/{node_spec.rb → node_resource_spec.rb} +4 -4
  42. data/spec/unit/ridley/resources/{role_spec.rb → role_resource_spec.rb} +3 -3
  43. data/spec/unit/ridley/resources/sandbox_resource_spec.rb +172 -0
  44. data/spec/unit/ridley/resources/search_spec.rb +34 -30
  45. data/spec/unit/ridley/sandbox_uploader_spec.rb +99 -0
  46. data/spec/unit/ridley/ssh_spec.rb +2 -2
  47. data/spec/unit/ridley_spec.rb +4 -12
  48. metadata +36 -28
  49. data/lib/ridley/dsl.rb +0 -58
  50. data/lib/ridley/resources/cookbook.rb +0 -51
  51. data/lib/ridley/resources/data_bag.rb +0 -81
  52. data/lib/ridley/resources/encrypted_data_bag_item.rb +0 -54
  53. data/lib/ridley/resources/sandbox.rb +0 -154
  54. data/spec/unit/ridley/resources/cookbook_spec.rb +0 -5
@@ -1,116 +1,24 @@
1
1
  module Ridley
2
2
  # @author Jamie Winsor <jamie@vialstudios.com>
3
- class Connection
4
- class << self
5
- def sync(options, &block)
6
- conn = new(options)
7
- conn.sync(&block)
8
- ensure
9
- conn.terminate if conn && conn.alive?
10
- end
11
-
12
- # @raise [ArgumentError]
13
- #
14
- # @return [Boolean]
15
- def validate_options(options)
16
- missing = (REQUIRED_OPTIONS - options.keys)
17
-
18
- unless missing.empty?
19
- missing.collect! { |opt| "'#{opt}'" }
20
- raise ArgumentError, "Missing required option(s): #{missing.join(', ')}"
21
- end
22
-
23
- missing_values = options.slice(*REQUIRED_OPTIONS).select { |key, value| !value.present? }
24
- unless missing_values.empty?
25
- values = missing_values.keys.collect { |opt| "'#{opt}'" }
26
- raise ArgumentError, "Missing value for required option(s): '#{values.join(', ')}'"
27
- end
28
- end
29
-
30
- # A hash of default options to be used in the Connection initializer
31
- #
32
- # @return [Hash]
33
- def default_options
34
- {
35
- thread_count: DEFAULT_THREAD_COUNT,
36
- ssh: Hash.new
37
- }
38
- end
39
- end
40
-
41
- extend Forwardable
42
-
3
+ class Connection < Faraday::Connection
43
4
  include Celluloid
44
- include Ridley::DSL
45
- include Ridley::Logging
46
-
47
- attr_reader :organization
48
5
 
49
- attr_accessor :client_name
50
- attr_accessor :client_key
51
- attr_accessor :validator_client
52
- attr_accessor :validator_path
53
- attr_accessor :encrypted_data_bag_secret_path
6
+ VALID_OPTIONS = [
7
+ :params,
8
+ :headers,
9
+ :request,
10
+ :ssl,
11
+ :proxy
12
+ ]
54
13
 
55
- attr_accessor :ssh
56
- attr_accessor :thread_count
57
-
58
- def_delegator :conn, :build_url
59
- def_delegator :conn, :scheme
60
- def_delegator :conn, :host
61
- def_delegator :conn, :port
62
- def_delegator :conn, :path_prefix
63
-
64
- def_delegator :conn, :url_prefix=
65
- def_delegator :conn, :url_prefix
66
-
67
- def_delegator :conn, :get
68
- def_delegator :conn, :put
69
- def_delegator :conn, :post
70
- def_delegator :conn, :delete
71
- def_delegator :conn, :head
72
-
73
- def_delegator :conn, :in_parallel
74
-
75
- OPTIONS = [
76
- :server_url,
77
- :client_name,
78
- :client_key,
79
- :organization,
80
- :validator_client,
81
- :validator_path,
82
- :encrypted_data_bag_secret_path,
83
- :thread_count,
84
- :ssl
85
- ].freeze
86
-
87
- REQUIRED_OPTIONS = [
88
- :server_url,
89
- :client_name,
90
- :client_key
91
- ].freeze
92
-
93
- DEFAULT_THREAD_COUNT = 8
14
+ attr_reader :organization
15
+ attr_reader :client_key
16
+ attr_reader :client_name
94
17
 
95
- # @option options [String] :server_url
96
- # URL to the Chef API
97
- # @option options [String] :client_name
98
- # name of the client used to authenticate with the Chef API
99
- # @option options [String] :client_key
100
- # filepath to the client's private key used to authenticate with the Chef API
101
- # @option options [String] :organization
102
- # the Organization to connect to. This is only used if you are connecting to
103
- # private Chef or hosted Chef
104
- # @option options [String] :validator_client (nil)
105
- # @option options [String] :validator_path (nil)
106
- # @option options [String] :encrypted_data_bag_secret_path (nil)
107
- # @option options [Integer] :thread_count (DEFAULT_THREAD_COUNT)
108
- # @option options [Hash] :ssh (Hash.new)
109
- # * :user (String) a shell user that will login to each node and perform the bootstrap command on (required)
110
- # * :password (String) the password for the shell user that will perform the bootstrap
111
- # * :keys (Array, String) an array of keys (or a single key) to authenticate the ssh user with instead of a password
112
- # * :timeout (Float) [5.0] timeout value for SSH bootstrap
113
- # * :sudo (Boolean) [true] bootstrap with sudo
18
+ # @param [String] :server_url
19
+ # @param [String] :client_name
20
+ # @param [String] :client_key
21
+ #
114
22
  # @option options [Hash] :params
115
23
  # URI query unencoded key/value pairs
116
24
  # @option options [Hash] :headers
@@ -121,63 +29,37 @@ module Ridley
121
29
  # * :verify (Boolean) [true] set to false to disable SSL verification
122
30
  # @option options [URI, String, Hash] :proxy
123
31
  # URI, String, or Hash of HTTP proxy options
124
- def initialize(options = {})
125
- log.info { "Ridley starting..." }
126
- configure(options)
127
- end
128
-
129
- # Configure this instance of Ridley::Connection
130
- #
131
- # @param [Hash] options
132
- def configure(options)
133
- options = self.class.default_options.merge(options)
134
- self.class.validate_options(options)
32
+ def initialize(server_url, client_name, client_key, options = {})
33
+ @client_name = client_name
34
+ @client_key = client_key
135
35
 
136
- @client_name = options[:client_name]
137
- @client_key = File.expand_path(options[:client_key])
138
- @organization = options[:organization]
139
- @thread_count = options[:thread_count]
140
- @ssh = options[:ssh]
141
- @validator_client = options[:validator_client]
142
- @validator_path = options[:validator_path]
143
- @encrypted_data_bag_secret_path = options[:encrypted_data_bag_secret_path]
36
+ options = options.reverse_merge(
37
+ builder: Faraday::Builder.new { |b|
38
+ b.request :chef_auth, client_name, client_key
39
+ b.response :chef_response
40
+ b.response :json
144
41
 
145
- unless @client_key.present? && File.exist?(@client_key)
146
- raise Errors::ClientKeyFileNotFound, "client key not found at: '#{@client_key}'"
147
- end
42
+ b.adapter :net_http_persistent
43
+ }
44
+ )
148
45
 
149
- faraday_options = options.slice(:params, :headers, :request, :ssl, :proxy)
150
- uri_hash = Addressable::URI.parse(options[:server_url]).to_hash.slice(:scheme, :host, :port)
46
+ uri_hash = Addressable::URI.parse(server_url).to_hash.slice(:scheme, :host, :port)
151
47
 
152
48
  unless uri_hash[:port]
153
49
  uri_hash[:port] = (uri_hash[:scheme] == "https" ? 443 : 80)
154
50
  end
155
51
 
156
- if org_match = options[:server_url].match(/.*\/organizations\/(.*)/)
157
- @organization ||= org_match[1]
52
+ if org_match = server_url.match(/.*\/organizations\/(.*)/)
53
+ @organization = org_match[1]
158
54
  end
159
55
 
160
- unless organization.nil?
161
- uri_hash[:path] = "/organizations/#{organization}"
56
+ unless @organization.nil?
57
+ uri_hash[:path] = "/organizations/#{@organization}"
162
58
  end
163
59
 
164
60
  server_uri = Addressable::URI.new(uri_hash)
165
61
 
166
- @conn = Faraday.new(server_uri, faraday_options) do |c|
167
- c.request :chef_auth, client_name, client_key
168
- c.response :chef_response
169
- c.response :json
170
-
171
- c.adapter :net_http_persistent
172
- end
173
- end
174
-
175
- def sync(&block)
176
- unless block
177
- raise Errors::InternalError, "A block must be given to synchronously process requests."
178
- end
179
-
180
- evaluate(&block)
62
+ super(server_uri, options)
181
63
  end
182
64
 
183
65
  # @return [Symbol]
@@ -198,43 +80,5 @@ module Ridley
198
80
  def server_url
199
81
  self.url_prefix.to_s
200
82
  end
201
-
202
- # The encrypted data bag secret for this connection.
203
- #
204
- # @raise [Ridley::Errors::EncryptedDataBagSecretNotFound]
205
- #
206
- # @return [String, nil]
207
- def encrypted_data_bag_secret
208
- return nil if encrypted_data_bag_secret_path.nil?
209
-
210
- IO.read(encrypted_data_bag_secret_path).chomp
211
- rescue Errno::ENOENT => e
212
- raise Errors::EncryptedDataBagSecretNotFound, "Encrypted data bag secret provided but not found at '#{encrypted_data_bag_secret_path}'"
213
- end
214
-
215
- def finalize
216
- log.info { "Ridley stopping..." }
217
- end
218
-
219
- private
220
-
221
- attr_reader :conn
222
-
223
- def evaluate(&block)
224
- @self_before_instance_eval = eval("self", block.binding)
225
- instance_eval(&block)
226
- end
227
-
228
- def method_missing(method, *args, &block)
229
- if block_given?
230
- @self_before_instance_eval ||= eval("self", block.binding)
231
- end
232
-
233
- if @self_before_instance_eval.nil?
234
- super
235
- end
236
-
237
- @self_before_instance_eval.send(method, *args, &block)
238
- end
239
83
  end
240
84
  end
@@ -4,6 +4,8 @@ module Ridley
4
4
  module Middleware
5
5
  # @author Jamie Winsor <jamie@vialstudios.com>
6
6
  class ChefAuth < Faraday::Middleware
7
+ include Ridley::Logging
8
+
7
9
  attr_reader :client_name
8
10
  attr_reader :client_key
9
11
 
@@ -26,7 +28,8 @@ module Ridley
26
28
  env[:request_headers] = default_headers.merge(env[:request_headers]).merge(authentication_headers)
27
29
  env[:request_headers] = env[:request_headers].merge('Content-Length' => env[:body].bytesize.to_s) if env[:body]
28
30
 
29
- Ridley.log.debug(env)
31
+ log.debug { "Performing Authenticated Chef Request: "}
32
+ log.debug { env }
30
33
 
31
34
  @app.call(env)
32
35
  end
@@ -52,94 +52,88 @@ module Ridley
52
52
  attribute(:json_class, default: klass)
53
53
  end
54
54
 
55
- # @param [Ridley::Connection] connection
55
+ # @param [Ridley::Client] client
56
56
  #
57
57
  # @return [Array<Object>]
58
- def all(connection)
59
- connection.get(self.resource_path).body.collect do |identity, location|
60
- new(connection, self.chef_id => identity)
58
+ def all(client)
59
+ client.connection.get(self.resource_path).body.collect do |identity, location|
60
+ new(client, self.chef_id => identity)
61
61
  end
62
62
  end
63
63
 
64
- # @param [Ridley::Connection] connection
64
+ # @param [Ridley::Client] client
65
65
  # @param [String, #chef_id] object
66
66
  #
67
67
  # @return [nil, Object]
68
- def find(connection, object)
69
- find!(connection, object)
68
+ def find(client, object)
69
+ find!(client, object)
70
70
  rescue Errors::HTTPNotFound
71
71
  nil
72
72
  end
73
73
 
74
- # @param [Ridley::Connection] connection
74
+ # @param [Ridley::Client] client
75
75
  # @param [String, #chef_id] object
76
76
  #
77
77
  # @raise [Errors::HTTPNotFound]
78
78
  # if a resource with the given chef_id is not found
79
79
  #
80
80
  # @return [Object]
81
- def find!(connection, object)
81
+ def find!(client, object)
82
82
  chef_id = object.respond_to?(:chef_id) ? object.chef_id : object
83
- new(connection, connection.get("#{self.resource_path}/#{chef_id}").body)
83
+ new(client, client.connection.get("#{self.resource_path}/#{chef_id}").body)
84
84
  end
85
85
 
86
- # @param [Ridley::Connection] connection
86
+ # @param [Ridley::Client] client
87
87
  # @param [#to_hash] object
88
88
  #
89
89
  # @return [Object]
90
- def create(connection, object)
91
- resource = new(connection, object.to_hash)
92
- new_attributes = connection.post(self.resource_path, resource.to_json).body
90
+ def create(client, object)
91
+ resource = new(client, object.to_hash)
92
+ new_attributes = client.connection.post(self.resource_path, resource.to_json).body
93
93
  resource.attributes = resource.attributes.deep_merge(new_attributes)
94
94
  resource
95
95
  end
96
96
 
97
- # @param [Ridley::Connection] connection
97
+ # @param [Ridley::Client] client
98
98
  # @param [String, #chef_id] object
99
99
  #
100
100
  # @return [Object]
101
- def delete(connection, object)
101
+ def delete(client, object)
102
102
  chef_id = object.respond_to?(:chef_id) ? object.chef_id : object
103
- new(connection, connection.delete("#{self.resource_path}/#{chef_id}").body)
103
+ new(client, client.connection.delete("#{self.resource_path}/#{chef_id}").body)
104
104
  end
105
105
 
106
- # @param [Ridley::Connection] connection
106
+ # @param [Ridley::Client] client
107
107
  #
108
108
  # @return [Array<Object>]
109
- def delete_all(connection)
109
+ def delete_all(client)
110
110
  mutex = Mutex.new
111
111
  deleted = []
112
- resources = all(connection)
113
-
114
- connection.thread_count.times.collect do
115
- Thread.new(connection, resources, deleted) do |connection, resources, deleted|
116
- while resource = mutex.synchronize { resources.pop }
117
- result = delete(connection, resource)
118
- mutex.synchronize { deleted << result }
119
- end
120
- end
121
- end.each(&:join)
122
-
123
- deleted
112
+
113
+ all(client).collect do |resource|
114
+ Celluloid::Future.new {
115
+ delete(client, resource)
116
+ }
117
+ end.map(&:value)
124
118
  end
125
119
 
126
- # @param [Ridley::Connection] connection
120
+ # @param [Ridley::Client] client
127
121
  # @param [#to_hash] object
128
122
  #
129
123
  # @return [Object]
130
- def update(connection, object)
131
- resource = new(connection, object.to_hash)
132
- new(connection, connection.put("#{self.resource_path}/#{resource.chef_id}", resource.to_json).body)
124
+ def update(client, object)
125
+ resource = new(client, object.to_hash)
126
+ new(client, client.connection.put("#{self.resource_path}/#{resource.chef_id}", resource.to_json).body)
133
127
  end
134
128
  end
135
129
 
136
130
  include Chozo::VariaModel
137
131
  include Comparable
138
132
 
139
- # @param [Ridley::Connection] connection
133
+ # @param [Ridley::Client] client
140
134
  # @param [Hash] new_attrs
141
- def initialize(connection, new_attrs = {})
142
- @connection = connection
135
+ def initialize(client, new_attrs = {})
136
+ @client = client
143
137
  mass_assign(new_attrs)
144
138
  end
145
139
 
@@ -153,7 +147,7 @@ module Ridley
153
147
  def save
154
148
  raise Errors::InvalidResource.new(self.errors) unless valid?
155
149
 
156
- mass_assign(self.class.create(connection, self).attributes)
150
+ mass_assign(self.class.create(client, self).attributes)
157
151
  true
158
152
  rescue Errors::HTTPConflict
159
153
  self.update
@@ -170,7 +164,7 @@ module Ridley
170
164
  def update
171
165
  raise Errors::InvalidResource.new(self.errors) unless valid?
172
166
 
173
- mass_assign(self.class.update(connection, self).attributes)
167
+ mass_assign(self.class.update(client, self).attributes)
174
168
  true
175
169
  end
176
170
 
@@ -178,7 +172,7 @@ module Ridley
178
172
  #
179
173
  # @return [Object]
180
174
  def reload
181
- mass_assign(self.class.find(connection, self).attributes)
175
+ mass_assign(self.class.find(client, self).attributes)
182
176
  self
183
177
  end
184
178
 
@@ -215,6 +209,6 @@ module Ridley
215
209
 
216
210
  private
217
211
 
218
- attr_reader :connection
212
+ attr_reader :client
219
213
  end
220
214
  end
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/resources/*.rb"].sort.each do |path|
2
+ require "ridley/resources/#{File.basename(path, '.rb')}"
3
+ end
@@ -1,21 +1,21 @@
1
1
  module Ridley
2
2
  # @author Jamie Winsor <jamie@vialstudios.com>
3
- class Client < Ridley::Resource
3
+ class ClientResource < Ridley::Resource
4
4
  class << self
5
5
  # Retrieves a client from the remote connection matching the given chef_id
6
6
  # and regenerates it's private key. An instance of the updated object will
7
7
  # be returned and have a value set for the 'private_key' accessor.
8
8
  #
9
- # @param [Ridley::Connection] connection
10
- # @param [String, #chef_id] client
9
+ # @param [Ridley::Client] client
10
+ # @param [String, #chef_id] chef_client
11
11
  #
12
12
  # @raise [Errors::HTTPNotFound]
13
13
  # if a client with the given chef_id is not found
14
14
  # @raise [Errors::HTTPError]
15
15
  #
16
- # @return [Ridley::Client]
17
- def regenerate_key(connection, client)
18
- obj = find!(connection, client)
16
+ # @return [Ridley::ClientResource]
17
+ def regenerate_key(client, chef_client)
18
+ obj = find!(client, chef_client)
19
19
  obj.regenerate_key
20
20
  obj
21
21
  end
@@ -66,24 +66,11 @@ module Ridley
66
66
  # Override to_json to reflect to massage the returned attributes based on the type
67
67
  # of connection. Only OHC/OPC requires the json_class attribute is not present.
68
68
  def to_json
69
- if connection.hosted?
69
+ if client.connection.hosted?
70
70
  attributes.except(:json_class).to_json
71
71
  else
72
72
  super
73
73
  end
74
74
  end
75
75
  end
76
-
77
- module DSL
78
- # Coerces instance functions into class functions on Ridley::Client. This coercion
79
- # sends an instance of the including class along to the class function.
80
- #
81
- # @see Ridley::ChainLink
82
- #
83
- # @return [Ridley::ChainLink]
84
- # a context object to delegate instance functions to class functions on Ridley::Client
85
- def client
86
- ChainLink.new(self, Ridley::Client)
87
- end
88
- end
89
76
  end