microwave 0.1004.6 → 0.1006.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/lib/chef/checksum.rb CHANGED
@@ -16,6 +16,7 @@
16
16
  # limitations under the License.
17
17
 
18
18
  require 'chef/log'
19
+ require 'chef/checksum/storage'
19
20
  require 'uuidtools'
20
21
 
21
22
  class Chef
@@ -25,6 +26,14 @@ class Chef
25
26
  class Checksum
26
27
  attr_accessor :checksum, :create_time
27
28
 
29
+ attr_reader :storage
30
+
31
+ # When a Checksum commits a sandboxed file to its final home in the checksum
32
+ # repo, this attribute will have the original on-disk path where the file
33
+ # was stored; it will be used if the commit is reverted to restore the sandbox
34
+ # to the pre-commit state.
35
+ attr_reader :original_committed_file_location
36
+
28
37
  # Creates a new Chef::Checksum object.
29
38
  # === Arguments
30
39
  # checksum::: the MD5 content hash of the file
@@ -35,6 +44,7 @@ class Chef
35
44
  @create_time = Time.now.iso8601
36
45
  @checksum = checksum
37
46
  @original_committed_file_location = nil
47
+ @storage = Storage::Filesystem.new(Chef::Config.checksum_path, checksum)
38
48
  end
39
49
 
40
50
  def to_json(*a)
@@ -55,28 +65,38 @@ class Chef
55
65
  checksum
56
66
  end
57
67
 
68
+ # Moves the given +sandbox_file+ into the checksum repo using the path
69
+ # given by +file_location+ and saves the Checksum to the database
70
+ def commit_sandbox_file(sandbox_file)
71
+ @original_committed_file_location = sandbox_file
72
+ Chef::Log.info("Commiting sandbox file: move #{sandbox_file} to #{@storage}")
73
+ @storage.commit(sandbox_file)
74
+ end
58
75
 
59
- ##
60
- # On-Disk Checksum File Repo (Chef Server API)
61
- ##
76
+ # Moves the checksum file back to its pre-commit location and deletes
77
+ # the checksum object from the database, effectively undoing +commit_sandbox_file+.
78
+ # Raises Chef::Exceptions::IllegalChecksumRevert if the original file location
79
+ # is unknown, which is will be the case if commit_sandbox_file was not
80
+ # previously called
81
+ def revert_sandbox_file_commit
82
+ unless original_committed_file_location
83
+ raise Chef::Exceptions::IllegalChecksumRevert, "Checksum #{self.inspect} cannot be reverted because the original sandbox file location is not known"
84
+ end
62
85
 
63
- def file_location
64
- File.join(checksum_repo_directory, checksum)
86
+ Chef::Log.warn("Reverting sandbox file commit: moving #{@storage} back to #{original_committed_file_location}")
87
+ @storage.revert(original_committed_file_location)
65
88
  end
66
89
 
67
- def checksum_repo_directory
68
- File.join(Chef::Config.checksum_path, checksum[0..1])
90
+ # Removes the on-disk file backing this checksum object, then removes it
91
+ # from the database
92
+ def purge
93
+ purge_file
69
94
  end
70
95
 
71
96
  private
72
97
 
73
- # Deletes the file backing this checksum from the on-disk repo.
74
- # Purging the checksums is how users can get back to a valid state if
75
- # they've deleted files, so we silently swallow Errno::ENOENT here.
76
98
  def purge_file
77
- FileUtils.rm(file_location)
78
- rescue Errno::ENOENT
79
- true
99
+ @storage.purge
80
100
  end
81
101
 
82
102
  end
@@ -0,0 +1,18 @@
1
+ #
2
+ # Author:: Andrea Campi (<andrea.campi@zephirworks.com>)
3
+ # Copyright:: Copyright (c) 2011 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require 'chef/checksum/storage/filesystem'
@@ -0,0 +1,56 @@
1
+ #
2
+ # Author:: Tim Hinderliter (<tim@opscode.com>)
3
+ # Copyright:: Copyright (c) 2011 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ class Chef
19
+ class Checksum
20
+ class Storage
21
+ class Filesystem
22
+ def initialize(base_dir, checksum)
23
+ @base_dir = base_dir
24
+ @checksum = checksum
25
+ end
26
+
27
+ def file_location
28
+ File.join(checksum_repo_directory, @checksum)
29
+ end
30
+ alias :to_s :file_location
31
+
32
+ def checksum_repo_directory
33
+ File.join(Chef::Config.checksum_path, @checksum[0..1])
34
+ end
35
+
36
+ def commit(sandbox_file)
37
+ FileUtils.mkdir_p(checksum_repo_directory)
38
+ File.rename(sandbox_file, file_location)
39
+ end
40
+
41
+ def revert(original_committed_file_location)
42
+ File.rename(file_location, original_committed_file_location)
43
+ end
44
+
45
+ # Deletes the file backing this checksum from the on-disk repo.
46
+ # Purging the checksums is how users can get back to a valid state if
47
+ # they've deleted files, so we silently swallow Errno::ENOENT here.
48
+ def purge
49
+ FileUtils.rm(file_location)
50
+ rescue Errno::ENOENT
51
+ true
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
data/lib/chef/config.rb CHANGED
@@ -64,7 +64,8 @@ class Chef
64
64
  interval nil
65
65
  log_level :info
66
66
  log_location STDOUT
67
- verbose_logging nil
67
+ # toggle info level log items that can create a lot of output
68
+ verbose_logging true
68
69
  node_name nil
69
70
  node_path "#{ENV['HOME']}/nodes"
70
71
 
@@ -84,6 +85,9 @@ class Chef
84
85
  # Exception Handlers
85
86
  exception_handlers []
86
87
 
88
+ # Start handlers
89
+ start_handlers []
90
+
87
91
  # Checksum Cache
88
92
  # Uses Moneta on the back-end
89
93
  cache_type "BasicFile"
@@ -109,7 +109,7 @@ class Chef
109
109
 
110
110
  def validate_template(erb_file)
111
111
  Chef::Log.debug("Testing template #{erb_file} for syntax errors...")
112
- result = shell_out("sh -c 'erubis -x #{erb_file} | ruby -c'")
112
+ result = shell_out("erubis -x #{erb_file} | ruby -c")
113
113
  result.error!
114
114
  true
115
115
  rescue Chef::Exceptions::ShellCommandFailed
@@ -377,7 +377,24 @@ class Chef
377
377
  if found_pref
378
378
  @manifest_records_by_path[found_pref]
379
379
  else
380
- raise Chef::Exceptions::FileNotFound, "cookbook #{name} does not contain file #{segment}/#{filename}"
380
+ if segment == :files || segment == :templates
381
+ error_message = "Cookbook '#{name}' (#{version}) does not contain a file at any of these locations:\n"
382
+ error_locations = [
383
+ " #{segment}/#{node[:platform]}-#{node[:platform_version]}/#{filename}",
384
+ " #{segment}/#{node[:platform]}/#{filename}",
385
+ " #{segment}/default/#{filename}",
386
+ ]
387
+ error_message << error_locations.join("\n")
388
+ existing_files = segment_filenames(segment)
389
+ # Show the files that the cookbook does have. If the user made a typo,
390
+ # hopefully they'll see it here.
391
+ unless existing_files.empty?
392
+ error_message << "\n\nThis cookbook _does_ contain: ['#{existing_files.join("','")}']"
393
+ end
394
+ raise Chef::Exceptions::FileNotFound, error_message
395
+ else
396
+ raise Chef::Exceptions::FileNotFound, "cookbook #{name} does not contain file #{segment}/#{filename}"
397
+ end
381
398
  end
382
399
  end
383
400
 
@@ -422,7 +439,7 @@ class Chef
422
439
 
423
440
  best_pref = preferences.find { |pref| !filenames_by_pref[pref].empty? }
424
441
 
425
- raise Chef::Exceptions::FileNotFound, "cookbook #{name} has no directory #{segment}/#{dirname}" unless best_pref
442
+ raise Chef::Exceptions::FileNotFound, "cookbook #{name} has no directory #{segment}/default/#{dirname}" unless best_pref
426
443
 
427
444
  filenames_by_pref[best_pref]
428
445
 
@@ -457,7 +474,7 @@ class Chef
457
474
 
458
475
  best_pref = preferences.find { |pref| !records_by_pref[pref].empty? }
459
476
 
460
- raise Chef::Exceptions::FileNotFound, "cookbook #{name} has no directory #{segment}/#{dirname}" unless best_pref
477
+ raise Chef::Exceptions::FileNotFound, "cookbook #{name} (#{version}) has no directory #{segment}/default/#{dirname}" unless best_pref
461
478
 
462
479
  records_by_pref[best_pref]
463
480
  end
@@ -483,13 +500,24 @@ class Chef
483
500
 
484
501
  fqdn = node[:fqdn]
485
502
 
503
+ # Break version into components, eg: "5.7.1" => [ "5.7.1", "5.7", "5" ]
504
+ search_versions = []
505
+ parts = version.to_s.split('.')
506
+
507
+ parts.size.times do
508
+ search_versions << parts.join('.')
509
+ parts.pop
510
+ end
511
+
486
512
  # Most specific to least specific places to find the path
487
- [
488
- File.join(segment.to_s, "host-#{fqdn}", path),
489
- File.join(segment.to_s, "#{platform}-#{version}", path),
490
- File.join(segment.to_s, platform.to_s, path),
491
- File.join(segment.to_s, "default", path)
492
- ]
513
+ search_path = [ File.join(segment.to_s, "host-#{fqdn}", path) ]
514
+ search_versions.each do |v|
515
+ search_path << File.join(segment.to_s, "#{platform}-#{v}", path)
516
+ end
517
+ search_path << File.join(segment.to_s, platform.to_s, path)
518
+ search_path << File.join(segment.to_s, "default", path)
519
+
520
+ search_path
493
521
  else
494
522
  [File.join(segment, path)]
495
523
  end
@@ -107,7 +107,7 @@ class Chef
107
107
  def set_group
108
108
  if (gid = target_gid) && (gid != stat.gid)
109
109
  File.chown(nil, gid, file)
110
- Chef::Log.info("#{log_string} owner changed to #{gid}")
110
+ Chef::Log.info("#{log_string} group changed to #{gid}")
111
111
  modified
112
112
  end
113
113
  end
data/lib/chef/handler.rb CHANGED
@@ -57,6 +57,27 @@ class Chef
57
57
  #
58
58
  class Handler
59
59
 
60
+ # The list of currently configured start handlers
61
+ def self.start_handlers
62
+ Array(Chef::Config[:start_handlers])
63
+ end
64
+
65
+ # Run the start handlers. This will usually be called by a notification
66
+ # from Chef::Client
67
+ def self.run_start_handlers(run_status)
68
+ Chef::Log.info("Running start handlers")
69
+ start_handlers.each do |handler|
70
+ handler.run_report_safely(run_status)
71
+ end
72
+ Chef::Log.info("Start handlers complete.")
73
+ end
74
+
75
+ # Wire up a notification to run the start handlers when the chef run
76
+ # starts.
77
+ Chef::Client.when_run_starts do |run_status|
78
+ run_start_handlers(run_status)
79
+ end
80
+
60
81
  # The list of currently configured report handlers
61
82
  def self.report_handlers
62
83
  Array(Chef::Config[:report_handlers])
@@ -38,64 +38,6 @@ class Chef
38
38
  extend ::Chef::Mixin::Command::Unix
39
39
  end
40
40
 
41
- # If command is a block, returns true if the block returns true, false if it returns false.
42
- # ("Only run this resource if the block is true")
43
- #
44
- # If the command is not a block, executes the command. If it returns any status other than
45
- # 0, it returns false (clearly, a 0 status code is true)
46
- #
47
- # === Parameters
48
- # command<Block>, <String>:: A block to check, or a string to execute
49
- #
50
- # === Returns
51
- # true:: Returns true if the block is true, or if the command returns 0
52
- # false:: Returns false if the block is false, or if the command returns a non-zero exit code.
53
- def only_if(command, args = {})
54
- if command.kind_of?(Proc)
55
- chdir_or_tmpdir(args[:cwd]) do
56
- res = command.call
57
- unless res
58
- return false
59
- end
60
- end
61
- else
62
- status = run_command({:command => command, :ignore_failure => true}.merge(args))
63
- if status.exitstatus != 0
64
- return false
65
- end
66
- end
67
- true
68
- end
69
-
70
- # If command is a block, returns false if the block returns true, true if it returns false.
71
- # ("Do not run this resource if the block is true")
72
- #
73
- # If the command is not a block, executes the command. If it returns a 0 exitstatus, returns false.
74
- # ("Do not run this resource if the command returns 0")
75
- #
76
- # === Parameters
77
- # command<Block>, <String>:: A block to check, or a string to execute
78
- #
79
- # === Returns
80
- # true:: Returns true if the block is false, or if the command returns a non-zero exit status.
81
- # false:: Returns false if the block is true, or if the command returns a 0 exit status.
82
- def not_if(command, args = {})
83
- if command.kind_of?(Proc)
84
- chdir_or_tmpdir(args[:cwd]) do
85
- res = command.call
86
- if res
87
- return false
88
- end
89
- end
90
- else
91
- status = run_command({:command => command, :ignore_failure => true}.merge(args))
92
- if status.exitstatus == 0
93
- return false
94
- end
95
- end
96
- true
97
- end
98
-
99
41
  # === Parameters
100
42
  # args<Hash>: A number of required and optional arguments
101
43
  # command<String>, <Array>: A complete command with options to execute or a command and options as an Array
@@ -35,7 +35,7 @@ class Chef
35
35
 
36
36
  #XXX :user, :group, :environment support?
37
37
 
38
- Open4.popen4(cmd) do |stdin,stdout,stderr,cid|
38
+ Open3.popen3(cmd) do |stdin,stdout,stderr,cid|
39
39
  if b
40
40
  if args[:waitlast]
41
41
  b[cid, stdin, stdout, stderr]
@@ -151,7 +151,11 @@ class Chef
151
151
  end
152
152
 
153
153
  def action_create_if_missing
154
- action_create
154
+ if ::File.exists?(@new_resource.path)
155
+ Chef::Log.debug("File #{@new_resource.path} exists, taking no action.")
156
+ else
157
+ action_create
158
+ end
155
159
  end
156
160
 
157
161
  def action_delete
@@ -43,7 +43,6 @@ class Chef
43
43
  files_to_purge.delete(::File.join(@new_resource.path, cookbook_file_relative_path))
44
44
  end
45
45
  purge_unmanaged_files(files_to_purge)
46
- Chef::Log.info("#{@new_resource} created")
47
46
  end
48
47
 
49
48
  def action_create_if_missing
data/lib/chef/resource.rb CHANGED
@@ -21,7 +21,7 @@ require 'chef/mixin/params_validate'
21
21
  require 'chef/mixin/check_helper'
22
22
  require 'chef/mixin/language'
23
23
  require 'chef/mixin/convert_to_class_name'
24
- require 'chef/mixin/command'
24
+ require 'chef/resource/conditional'
25
25
  require 'chef/resource_collection'
26
26
  require 'chef/node'
27
27
 
@@ -70,7 +70,7 @@ F
70
70
 
71
71
  end
72
72
 
73
- FORBIDDEN_IVARS = [:@run_context, :@node]
73
+ FORBIDDEN_IVARS = [:@run_context, :@node, :@not_if, :@only_if]
74
74
  HIDDEN_IVARS = [:@allowed_actions, :@resource_name, :@source_line, :@run_context, :@name, :@node]
75
75
 
76
76
  include Chef::Mixin::CheckHelper
@@ -116,10 +116,8 @@ F
116
116
  @ignore_failure = false
117
117
  @retries = 0
118
118
  @retry_delay = 2
119
- @not_if = nil
120
- @not_if_args = {}
121
- @only_if = nil
122
- @only_if_args = {}
119
+ @not_if = []
120
+ @only_if = []
123
121
  @immediate_notifications = Array.new
124
122
  @delayed_notifications = Array.new
125
123
  @source_line = nil
@@ -371,24 +369,44 @@ F
371
369
  instance_vars
372
370
  end
373
371
 
374
- def only_if(arg=nil, args = {}, &blk)
375
- if Kernel.block_given?
376
- @only_if = blk
377
- @only_if_args = args
378
- else
379
- @only_if = arg if arg
380
- @only_if_args = args if arg
372
+ # If command is a block, returns true if the block returns true, false if it returns false.
373
+ # ("Only run this resource if the block is true")
374
+ #
375
+ # If the command is not a block, executes the command. If it returns any status other than
376
+ # 0, it returns false (clearly, a 0 status code is true)
377
+ #
378
+ # === Parameters
379
+ # command<String>:: A a string to execute.
380
+ # opts<Hash>:: Options control the execution of the command
381
+ # block<Proc>:: A ruby block to run. Ignored if a command is given.
382
+ #
383
+ # === Evaluation
384
+ # * evaluates to true if the block is true, or if the command returns 0
385
+ # * evaluates to false if the block is false, or if the command returns a non-zero exit code.
386
+ def only_if(command=nil, opts={}, &block)
387
+ if command || block_given?
388
+ @only_if << Conditional.only_if(command, opts, &block)
381
389
  end
382
390
  @only_if
383
391
  end
384
392
 
385
- def not_if(arg=nil, args = {}, &blk)
386
- if Kernel.block_given?
387
- @not_if = blk
388
- @not_if_args = args
389
- else
390
- @not_if = arg if arg
391
- @not_if_args = args if arg
393
+ # If command is a block, returns false if the block returns true, true if it returns false.
394
+ # ("Do not run this resource if the block is true")
395
+ #
396
+ # If the command is not a block, executes the command. If it returns a 0 exitstatus, returns false.
397
+ # ("Do not run this resource if the command returns 0")
398
+ #
399
+ # === Parameters
400
+ # command<String>:: A a string to execute.
401
+ # opts<Hash>:: Options control the execution of the command
402
+ # block<Proc>:: A ruby block to run. Ignored if a command is given.
403
+ #
404
+ # === Evaluation
405
+ # * evaluates to true if the block is false, or if the command returns a non-zero exit status.
406
+ # * evaluates to false if the block is true, or if the command returns a 0 exit status.
407
+ def not_if(command=nil, opts={}, &block)
408
+ if command || block_given?
409
+ @not_if << Conditional.not_if(command, opts, &block)
392
410
  end
393
411
  @not_if
394
412
  end
@@ -405,32 +423,17 @@ F
405
423
  end
406
424
 
407
425
  def run_action(action)
408
- Chef::Log.debug("Processing #{self} action #{action} (#{defined_at})")
426
+ if Chef::Config[:verbose_logging] || Chef::Log.level == :debug
427
+ # This can be noisy
428
+ Chef::Log.info("Processing #{self} action #{action} (#{defined_at})")
429
+ end
409
430
 
410
431
  # ensure that we don't leave @updated_by_last_action set to true
411
432
  # on accident
412
433
  updated_by_last_action(false)
413
434
 
414
435
  begin
415
- # Check if this resource has an only_if block -- if it does,
416
- # evaluate the only_if block and skip the resource if
417
- # appropriate.
418
- if only_if
419
- unless Chef::Mixin::Command.only_if(only_if, only_if_args)
420
- Chef::Log.debug("Skipping #{self} due to only_if")
421
- return
422
- end
423
- end
424
-
425
- # Check if this resource has a not_if block -- if it does,
426
- # evaluate the not_if block and skip the resource if
427
- # appropriate.
428
- if not_if
429
- unless Chef::Mixin::Command.not_if(not_if, not_if_args)
430
- Chef::Log.debug("Skipping #{self} due to not_if")
431
- return
432
- end
433
- end
436
+ return if should_skip?
434
437
 
435
438
  provider = Chef::Platform.provider_for_resource(self)
436
439
  provider.load_current_resource
@@ -440,13 +443,35 @@ F
440
443
  Chef::Log.error("#{self} (#{defined_at}) had an error: #{e.message}")
441
444
  else
442
445
  Chef::Log.error("#{self} (#{defined_at}) has had an error")
443
- new_exception = e.exception("#{self} (#{defined_at}) had an error: #{e.message}")
446
+ new_exception = e.exception("#{self} (#{defined_at}) had an error: #{e.class.name}: #{e.message}")
444
447
  new_exception.set_backtrace(e.backtrace)
445
448
  raise new_exception
446
449
  end
447
450
  end
448
451
  end
449
452
 
453
+ # Evaluates not_if and only_if conditionals. Returns a falsey value if any
454
+ # of the conditionals indicate that this resource should be skipped, i.e.,
455
+ # if an only_if evaluates to false or a not_if evaluates to true.
456
+ #
457
+ # If this resource should be skipped, returns the first conditional that
458
+ # "fails" its check. Subsequent conditionals are not evaluated, so in
459
+ # general it's not a good idea to rely on side effects from not_if or
460
+ # only_if commands/blocks being evaluated.
461
+ def should_skip?
462
+ conditionals = only_if + not_if
463
+ return false if conditionals.empty?
464
+
465
+ conditionals.find do |conditional|
466
+ if conditional.continue?
467
+ false
468
+ else
469
+ Chef::Log.debug("Skipping #{self} due to #{conditional.description}")
470
+ true
471
+ end
472
+ end
473
+ end
474
+
450
475
  def updated_by_last_action(true_or_false)
451
476
  @updated ||= true_or_false
452
477
  @updated_by_last_action = true_or_false