ridley 0.7.0.beta → 0.7.0.rc1

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