ridley 0.11.0.rc1 → 0.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.travis.yml +3 -2
  2. data/Gemfile +1 -0
  3. data/Guardfile +2 -2
  4. data/Thorfile +2 -2
  5. data/lib/ridley/chef.rb +1 -0
  6. data/lib/ridley/chef/chefignore.rb +76 -0
  7. data/lib/ridley/chef/cookbook.rb +17 -7
  8. data/lib/ridley/chef/cookbook/metadata.rb +8 -0
  9. data/lib/ridley/chef_objects/cookbook_object.rb +21 -37
  10. data/lib/ridley/chef_objects/data_bag_item_obect.rb +9 -0
  11. data/lib/ridley/client.rb +2 -2
  12. data/lib/ridley/connection.rb +12 -6
  13. data/lib/ridley/errors.rb +14 -0
  14. data/lib/ridley/host_connector.rb +10 -3
  15. data/lib/ridley/resource.rb +25 -5
  16. data/lib/ridley/resources/cookbook_resource.rb +19 -9
  17. data/lib/ridley/resources/data_bag_item_resource.rb +8 -4
  18. data/lib/ridley/resources/search_resource.rb +5 -5
  19. data/lib/ridley/sandbox_uploader.rb +6 -0
  20. data/lib/ridley/version.rb +1 -1
  21. data/ridley.gemspec +1 -1
  22. data/spec/acceptance/client_resource_spec.rb +44 -62
  23. data/spec/acceptance/cookbook_resource_spec.rb +27 -0
  24. data/spec/acceptance/data_bag_item_resource_spec.rb +36 -54
  25. data/spec/acceptance/data_bag_resource_spec.rb +9 -21
  26. data/spec/acceptance/environment_resource_spec.rb +34 -63
  27. data/spec/acceptance/node_resource_spec.rb +27 -47
  28. data/spec/acceptance/role_resource_spec.rb +27 -67
  29. data/spec/acceptance/sandbox_resource_spec.rb +3 -13
  30. data/spec/acceptance/search_resource_spec.rb +5 -15
  31. data/spec/fixtures/chefignore +8 -0
  32. data/spec/spec_helper.rb +15 -1
  33. data/spec/support/chef_server.rb +77 -0
  34. data/spec/unit/ridley/chef/chefignore_spec.rb +40 -0
  35. data/spec/unit/ridley/chef/cookbook_spec.rb +30 -2
  36. data/spec/unit/ridley/chef_objects/cookbook_object_spec.rb +3 -3
  37. data/spec/unit/ridley/connection_spec.rb +1 -2
  38. data/spec/unit/ridley/resources/cookbook_resource_spec.rb +85 -48
  39. data/spec/unit/ridley/resources/data_bag_resource_spec.rb +1 -1
  40. data/spec/unit/ridley/resources/search_resource_spec.rb +39 -2
  41. data/spec/unit/ridley/sandbox_uploader_spec.rb +25 -0
  42. metadata +19 -7
data/.travis.yml CHANGED
@@ -1,6 +1,7 @@
1
- script: "bundle exec thor spec:unit"
1
+ script: "bundle exec thor spec:all"
2
2
  language: ruby
3
3
  rvm:
4
4
  - 1.9.2
5
5
  - 1.9.3
6
- - jruby-19mode
6
+ - 2.0.0
7
+ # - jruby-19mode # temporarily disable until ChefZero works with JRuby
data/Gemfile CHANGED
@@ -45,4 +45,5 @@ group :test do
45
45
  gem 'fuubar'
46
46
  gem 'json_spec'
47
47
  gem 'webmock'
48
+ gem 'chef-zero'
48
49
  end
data/Guardfile CHANGED
@@ -12,10 +12,10 @@ guard 'yard', stdout: '/dev/null', stderr: '/dev/null' do
12
12
  watch(%r{ext/.+\.c})
13
13
  end
14
14
 
15
- guard 'rspec', cli: "--color --drb --format Fuubar --tag ~type:acceptance", all_on_start: false, all_after_pass: false do
15
+ guard 'rspec', cli: "--color --drb --format Fuubar", all_on_start: false, all_after_pass: false do
16
16
  watch(%r{^spec/unit/.+_spec\.rb$})
17
17
  watch(%r{^spec/acceptance/.+_spec\.rb$})
18
-
18
+
19
19
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
20
20
  watch('spec/spec_helper.rb') { "spec" }
21
21
  watch(%r{^spec/support/.+\.rb$}) { "spec" }
data/Thorfile CHANGED
@@ -9,7 +9,7 @@ require 'ridley'
9
9
  class Default < Thor
10
10
  unless jruby?
11
11
  require 'thor/rake_compat'
12
-
12
+
13
13
  include Thor::RakeCompat
14
14
  Bundler::GemHelper.install_tasks
15
15
 
@@ -40,7 +40,7 @@ class Default < Thor
40
40
 
41
41
  desc "unit", "run only unit tests"
42
42
  def unit
43
- exec "rspec --color --format=documentation spec --tag ~type:acceptance"
43
+ exec "rspec --color --format=documentation spec --tag ~type:acceptance"
44
44
  end
45
45
 
46
46
  desc "acceptance", "run only acceptance tests"
data/lib/ridley/chef.rb CHANGED
@@ -5,6 +5,7 @@ module Ridley
5
5
  # site, and Chef Cookbooks
6
6
  module Chef
7
7
  autoload :Cookbook, 'ridley/chef/cookbook'
8
+ autoload :Chefignore, 'ridley/chef/chefignore'
8
9
  autoload :Digester, 'ridley/chef/digester'
9
10
  end
10
11
  end
@@ -0,0 +1,76 @@
1
+ module Ridley::Chef
2
+ # Borrowed and modified from:
3
+ # {https://raw.github.com/opscode/chef/62f9b0e3be8e22eef092163c331b7d3f8d350f94/lib/chef/cookbook/chefignore.rb}
4
+ #
5
+ # Copyright:: Copyright (c) 2011 Opscode, Inc.
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ class Chefignore
19
+ class << self
20
+ # Traverse a path in relative context to find a Chefignore file
21
+ #
22
+ # @param [String] path
23
+ # path to traverse
24
+ #
25
+ # @return [String, nil]
26
+ def find_relative_to(path)
27
+ [
28
+ File.join(path, FILENAME),
29
+ File.join(path, 'cookbooks', FILENAME)
30
+ ].find { |f| File.exists?(f) }
31
+ end
32
+ end
33
+
34
+ FILENAME = "chefignore".freeze
35
+ COMMENTS_AND_WHITESPACE = /^\s*(?:#.*)?$/
36
+
37
+ attr_reader :ignores
38
+
39
+ def initialize(ignore_file_or_repo)
40
+ @ignore_file = find_ignore_file(ignore_file_or_repo)
41
+ @ignores = parse_ignore_file
42
+ end
43
+
44
+ def remove_ignores_from(file_list)
45
+ Array(file_list).inject([]) do |unignored, file|
46
+ ignored?(file) ? unignored : unignored << file
47
+ end
48
+ end
49
+
50
+ def ignored?(file_name)
51
+ @ignores.any? { |glob| File.fnmatch?(glob, file_name) }
52
+ end
53
+
54
+ private
55
+
56
+ def parse_ignore_file
57
+ ignore_globs = []
58
+
59
+ if File.exist?(@ignore_file) && File.readable?(@ignore_file)
60
+ File.foreach(@ignore_file) do |line|
61
+ ignore_globs << line.strip unless line =~ COMMENTS_AND_WHITESPACE
62
+ end
63
+ end
64
+
65
+ ignore_globs
66
+ end
67
+
68
+ def find_ignore_file(path)
69
+ if File.basename(path) =~ /#{FILENAME}/
70
+ path
71
+ else
72
+ File.join(path, FILENAME)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -29,20 +29,21 @@ module Ridley::Chef
29
29
  # explicitly supply the name of the cookbook we are loading. This is useful if
30
30
  # you are dealing with a cookbook that does not have well-formed metadata
31
31
  #
32
- # @raise [IOError] if the path does not contain a metadata.rb file
32
+ # @raise [IOError] if the path does not contain a metadata.rb or metadata.json file
33
33
  #
34
34
  # @return [Ridley::Chef::Cookbook]
35
35
  def from_path(path, options = {})
36
36
  path = Pathname.new(path)
37
- metadata = Cookbook::Metadata.from_file(path.join('metadata.rb'))
38
-
39
- name = if options[:name].present?
40
- options[:name]
37
+ metadata = if path.join('metadata.rb').exist?
38
+ Cookbook::Metadata.from_file(path.join('metadata.rb'))
39
+ elsif path.join('metadata.json').exist?
40
+ Cookbook::Metadata.from_json(File.read(path.join('metadata.json')))
41
41
  else
42
- metadata.name.empty? ? File.basename(path) : metadata.name
42
+ raise IOError, "no metadata.rb or metadata.json found at #{path}"
43
43
  end
44
44
 
45
- new(name, path, metadata)
45
+ metadata.name(options[:name].presence || metadata.name.presence || File.basename(path))
46
+ new(metadata.name, path, metadata)
46
47
  end
47
48
  end
48
49
 
@@ -96,6 +97,7 @@ module Ridley::Chef
96
97
  root_files: Array.new
97
98
  )
98
99
  @frozen = false
100
+ @chefignore = Ridley::Chef::Chefignore.new(@path)
99
101
 
100
102
  load_files
101
103
  end
@@ -207,7 +209,12 @@ module Ridley::Chef
207
209
 
208
210
  private
209
211
 
212
+ # @return [Array]
210
213
  attr_reader :files
214
+ # @return [Ridley::Chef::Chefignore]
215
+ attr_reader :chefignore
216
+
217
+ def_delegator :chefignore, :ignored?
211
218
 
212
219
  def load_files
213
220
  load_shallow(:recipes, 'recipes', '*.rb')
@@ -225,6 +232,7 @@ module Ridley::Chef
225
232
  [].tap do |files|
226
233
  Dir.glob(path.join('*'), File::FNM_DOTMATCH).each do |file|
227
234
  next if File.directory?(file)
235
+ next if ignored?(file)
228
236
  @files << file
229
237
  @manifest[:root_files] << file_metadata(:root_files, file)
230
238
  end
@@ -236,6 +244,7 @@ module Ridley::Chef
236
244
  file_spec = path.join(category_dir, '**', glob)
237
245
  Dir.glob(file_spec, File::FNM_DOTMATCH).each do |file|
238
246
  next if File.directory?(file)
247
+ next if ignored?(file)
239
248
  @files << file
240
249
  @manifest[category] << file_metadata(category, file)
241
250
  end
@@ -245,6 +254,7 @@ module Ridley::Chef
245
254
  def load_shallow(category, *path_glob)
246
255
  [].tap do |files|
247
256
  Dir[path.join(*path_glob)].each do |file|
257
+ next if ignored?(file)
248
258
  @files << file
249
259
  @manifest[category] << file_metadata(category, file)
250
260
  end
@@ -26,6 +26,10 @@ module Ridley::Chef
26
26
  def from_hash(hash)
27
27
  new.from_hash(hash)
28
28
  end
29
+
30
+ def from_json(json)
31
+ new.from_json(json)
32
+ end
29
33
  end
30
34
 
31
35
  NAME = 'name'.freeze
@@ -458,6 +462,10 @@ module Ridley::Chef
458
462
  self
459
463
  end
460
464
 
465
+ def from_json(json)
466
+ from_hash MultiJson.decode(json)
467
+ end
468
+
461
469
  private
462
470
 
463
471
  # Verify that the given array is an array of strings
@@ -118,7 +118,27 @@ module Ridley
118
118
  #
119
119
  # @return [nil]
120
120
  def download_file(filetype, path, destination)
121
- download_fun(filetype).call(path, destination)
121
+ files = case filetype.to_sym
122
+ when :attribute, :attributes; attributes
123
+ when :definition, :definitions; definitions
124
+ when :file, :files; files
125
+ when :library, :libraries; libraries
126
+ when :provider, :providers; providers
127
+ when :recipe, :recipes; recipes
128
+ when :resource, :resources; resources
129
+ when :root_file, :root_files; root_files
130
+ when :template, :templates; templates
131
+ else
132
+ raise Errors::UnknownCookbookFileType.new(filetype)
133
+ end
134
+
135
+ file = files.find { |f| f[:path] == path }
136
+ return nil if file.nil?
137
+
138
+ destination = File.expand_path(destination)
139
+ log.debug { "downloading '#{filetype}' file: #{file} to: '#{destination}'" }
140
+
141
+ resource.connection.stream(file[:url], destination)
122
142
  end
123
143
 
124
144
  # A hash containing keys for all of the different cookbook filetypes with values
@@ -150,41 +170,5 @@ module Ridley
150
170
  def to_s
151
171
  "#{name}: #{manifest}"
152
172
  end
153
-
154
- private
155
-
156
- # Return a lambda for downloading a file from the cookbook of the given type
157
- #
158
- # @param [#to_sym] filetype
159
- #
160
- # @return [lambda]
161
- # a lambda which takes to parameters: target and path. Target is the URL to download from
162
- # and path is the location on disk to steam the contents of the remote URL to.
163
- def download_fun(filetype)
164
- collection = case filetype.to_sym
165
- when :attribute, :attributes; method(:attributes)
166
- when :definition, :definitions; method(:definitions)
167
- when :file, :files; method(:files)
168
- when :library, :libraries; method(:libraries)
169
- when :provider, :providers; method(:providers)
170
- when :recipe, :recipes; method(:recipes)
171
- when :resource, :resources; method(:resources)
172
- when :root_file, :root_files; method(:root_files)
173
- when :template, :templates; method(:templates)
174
- else
175
- raise Errors::UnknownCookbookFileType.new(filetype)
176
- end
177
-
178
- ->(target, destination) {
179
- files = collection.call # JW: always chaining .call.find results in a nil value. WHY?
180
- file = files.find { |f| f[:path] == target }
181
- return nil if file.nil?
182
-
183
- destination = File.expand_path(destination)
184
- log.debug { "downloading '#{filetype}' file: #{file} to: '#{destination}'" }
185
-
186
- connection.stream(file[:url], destination)
187
- }
188
- end
189
173
  end
190
174
  end
@@ -48,6 +48,15 @@ module Ridley
48
48
  mass_assign(decrypted_hash)
49
49
  end
50
50
 
51
+ # Decrypts an individual value stored inside the data bag item.
52
+ #
53
+ # @example
54
+ # data_bag_item.decrypt_value("Xk0E8lV9r4BhZzcg4wal0X4w9ZexN3azxMjZ9r1MCZc=")
55
+ # => {test: {database: {username: "test"}}}
56
+ #
57
+ # @param [String] an encrypted String value
58
+ #
59
+ # @return [Hash] a decrypted attribute value
51
60
  def decrypt_value(value)
52
61
  if encrypted_data_bag_secret.nil?
53
62
  raise Errors::EncryptedDataBagSecretNotSet
data/lib/ridley/client.rb CHANGED
@@ -234,7 +234,7 @@ module Ridley
234
234
  #
235
235
  # @return [Hash]
236
236
  def search(index, query = nil, options = {})
237
- @resources_registry[:search_resource].run(index, query, options)
237
+ @resources_registry[:search_resource].run(index, query, @resources_registry, options)
238
238
  end
239
239
 
240
240
  # Return the array of all possible search indexes for the including connection
@@ -257,7 +257,7 @@ module Ridley
257
257
  def encrypted_data_bag_secret
258
258
  return nil if encrypted_data_bag_secret_path.nil?
259
259
 
260
- IO.read(encrypted_data_bag_secret_path).chomp
260
+ ::IO.read(encrypted_data_bag_secret_path).chomp
261
261
  rescue Errno::ENOENT => e
262
262
  raise Errors::EncryptedDataBagSecretNotFound, "Encrypted data bag secret provided but not found at '#{encrypted_data_bag_secret_path}'"
263
263
  end
@@ -14,8 +14,11 @@ module Ridley
14
14
  :proxy
15
15
  ]
16
16
 
17
+ # @return [String]
17
18
  attr_reader :organization
19
+ # @return [String]
18
20
  attr_reader :client_key
21
+ # @return [String]
19
22
  attr_reader :client_name
20
23
  # @return [Integer]
21
24
  # how many retries to attempt on HTTP requests
@@ -95,7 +98,7 @@ module Ridley
95
98
  # Override Faraday::Connection#run_request to catch exceptions from {Ridley::Middleware} that
96
99
  # we expect. Caught exceptions are re-raised with Celluloid#abort so we don't crash the connection.
97
100
  def run_request(*args)
98
- super
101
+ defer { super }
99
102
  rescue Errors::HTTPError, Faraday::Error => ex
100
103
  abort(ex)
101
104
  end
@@ -129,13 +132,16 @@ module Ridley
129
132
  local = Tempfile.new('ridley-stream')
130
133
  local.binmode
131
134
 
132
- retryable(tries: retries, on: OpenURI::HTTPError, sleep: retry_interval) do
133
- open(target, 'rb', headers) do |remote|
134
- local.write(remote.read)
135
+ defer {
136
+ retryable(tries: retries, on: OpenURI::HTTPError, sleep: retry_interval) do
137
+ open(target, 'rb', headers) do |remote|
138
+ local.write(remote.read)
139
+ end
135
140
  end
136
- end
137
141
 
138
- local.flush
142
+ local.flush
143
+ }
144
+
139
145
  FileUtils.mv(local.path, destination)
140
146
  rescue OpenURI::HTTPError => ex
141
147
  abort(ex)
data/lib/ridley/errors.rb CHANGED
@@ -115,17 +115,31 @@ module Ridley
115
115
  class HTTP5XXError < HTTPError; end
116
116
 
117
117
  # 3XX
118
+ class HTTPMultipleChoices < HTTP3XXError; register_error(300); end
118
119
  class HTTPMovedPermanently < HTTP3XXError; register_error(301); end
119
120
  class HTTPFound < HTTP3XXError; register_error(302); end
121
+ class HTTPSeeOther < HTTP3XXError; register_error(303); end
122
+ class HTTPNotModified < HTTP3XXError; register_error(304); end
123
+ class HTTPUseProxy < HTTP3XXError; register_error(305); end
124
+ class HTTPTemporaryRedirect < HTTP3XXError; register_error(307); end
120
125
 
121
126
  # 4XX
122
127
  class HTTPBadRequest < HTTP4XXError; register_error(400); end
123
128
  class HTTPUnauthorized < HTTP4XXError; register_error(401); end
129
+ class HTTPPaymentRequired < HTTP4XXError; register_error(402); end
124
130
  class HTTPForbidden < HTTP4XXError; register_error(403); end
125
131
  class HTTPNotFound < HTTP4XXError; register_error(404); end
126
132
  class HTTPMethodNotAllowed < HTTP4XXError; register_error(405); end
133
+ class HTTPNotAcceptable < HTTP4XXError; register_error(406); end
134
+ class HTTPProxyAuthenticationRequired < HTTP4XXError; register_error(407); end
127
135
  class HTTPRequestTimeout < HTTP4XXError; register_error(408); end
128
136
  class HTTPConflict < HTTP4XXError; register_error(409); end
137
+ class HTTPGone < HTTP4XXError; register_error(410); end
138
+ class HTTPLengthRequired < HTTP4XXError; register_error(411); end
139
+ class HTTPPreconditionFailed < HTTP4XXError; register_error(412); end
140
+ class HTTPRequestEntityTooLarge < HTTP4XXError; register_error(413); end
141
+ class HTTPRequestURITooLong < HTTP4XXError; register_error(414); end
142
+ class HTTPUnsupportedMediaType < HTTP4XXError; register_error(415); end
129
143
 
130
144
  # 5XX
131
145
  class HTTPInternalServerError < HTTP5XXError; register_error(500); end
@@ -42,6 +42,7 @@ module Ridley
42
42
  # the host to attempt to connect to
43
43
  # @option options [Hash] :ssh
44
44
  # * :port (Fixnum) the ssh port to connect on the node the bootstrap will be performed on (22)
45
+ # * :timeout (Float) [5.0] timeout value for testing SSH connection
45
46
  # @option options [Hash] :winrm
46
47
  # * :port (Fixnum) the winrm port to connect on the node the bootstrap will be performed on (5985)
47
48
  # @param block [Proc]
@@ -50,7 +51,9 @@ module Ridley
50
51
  # @return [Ridley::HostConnector] a class under Ridley::HostConnector
51
52
  def best_connector_for(host, options = {}, &block)
52
53
  ssh_port, winrm_port = parse_port_options(options)
53
- if connector_port_open?(host, ssh_port)
54
+ timeout = options[:ssh] && options[:ssh][:timeout]
55
+
56
+ if connector_port_open?(host, ssh_port, timeout)
54
57
  host_connector = Ridley::HostConnector::SSH
55
58
  elsif connector_port_open?(host, winrm_port)
56
59
  host_connector = Ridley::HostConnector::WinRM
@@ -72,10 +75,14 @@ module Ridley
72
75
  # the host to attempt to connect to
73
76
  # @param port [Fixnum]
74
77
  # the port to attempt to connect on
78
+ # @param timeout [Float]
79
+ # the number of seconds to wait (default: 3)
75
80
  #
76
81
  # @return [Boolean]
77
- def connector_port_open?(host, port)
78
- Timeout::timeout(3) do
82
+ def connector_port_open?(host, port, timeout = nil)
83
+ timeout ||= 3
84
+
85
+ Timeout::timeout(timeout) do
79
86
  socket = TCPSocket.new(host, port)
80
87
  socket.close
81
88
  end