ridley 0.11.0.rc1 → 0.11.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.
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