microwave 0.1004.6 → 0.1006.1

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