cheffish 1.5.0 → 1.6.0

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +10 -0
  3. data/LICENSE +201 -201
  4. data/README.md +120 -120
  5. data/Rakefile +23 -23
  6. data/cheffish.gemspec +26 -0
  7. data/lib/chef/provider/chef_acl.rb +446 -439
  8. data/lib/chef/provider/chef_client.rb +53 -53
  9. data/lib/chef/provider/chef_container.rb +55 -55
  10. data/lib/chef/provider/chef_data_bag.rb +55 -55
  11. data/lib/chef/provider/chef_data_bag_item.rb +278 -278
  12. data/lib/chef/provider/chef_environment.rb +83 -83
  13. data/lib/chef/provider/chef_group.rb +83 -83
  14. data/lib/chef/provider/chef_mirror.rb +169 -169
  15. data/lib/chef/provider/chef_node.rb +87 -87
  16. data/lib/chef/provider/chef_organization.rb +155 -155
  17. data/lib/chef/provider/chef_resolved_cookbooks.rb +46 -46
  18. data/lib/chef/provider/chef_role.rb +84 -84
  19. data/lib/chef/provider/chef_user.rb +59 -59
  20. data/lib/chef/provider/private_key.rb +225 -225
  21. data/lib/chef/provider/public_key.rb +88 -88
  22. data/lib/chef/resource/chef_acl.rb +69 -69
  23. data/lib/chef/resource/chef_client.rb +48 -48
  24. data/lib/chef/resource/chef_container.rb +22 -22
  25. data/lib/chef/resource/chef_data_bag.rb +22 -22
  26. data/lib/chef/resource/chef_data_bag_item.rb +121 -121
  27. data/lib/chef/resource/chef_environment.rb +77 -77
  28. data/lib/chef/resource/chef_group.rb +53 -53
  29. data/lib/chef/resource/chef_mirror.rb +52 -52
  30. data/lib/chef/resource/chef_node.rb +22 -22
  31. data/lib/chef/resource/chef_organization.rb +69 -69
  32. data/lib/chef/resource/chef_resolved_cookbooks.rb +35 -35
  33. data/lib/chef/resource/chef_role.rb +110 -110
  34. data/lib/chef/resource/chef_user.rb +56 -56
  35. data/lib/chef/resource/private_key.rb +48 -48
  36. data/lib/chef/resource/public_key.rb +25 -25
  37. data/lib/cheffish.rb +235 -235
  38. data/lib/cheffish/actor_provider_base.rb +131 -131
  39. data/lib/cheffish/basic_chef_client.rb +184 -184
  40. data/lib/cheffish/chef_provider_base.rb +246 -246
  41. data/lib/cheffish/chef_run.rb +162 -162
  42. data/lib/cheffish/chef_run_data.rb +19 -19
  43. data/lib/cheffish/chef_run_listener.rb +30 -30
  44. data/lib/cheffish/key_formatter.rb +113 -113
  45. data/lib/cheffish/merged_config.rb +98 -94
  46. data/lib/cheffish/recipe_dsl.rb +157 -157
  47. data/lib/cheffish/rspec.rb +8 -8
  48. data/lib/cheffish/rspec/chef_run_support.rb +83 -83
  49. data/lib/cheffish/rspec/matchers.rb +4 -4
  50. data/lib/cheffish/rspec/matchers/be_idempotent.rb +16 -16
  51. data/lib/cheffish/rspec/matchers/emit_no_warnings_or_errors.rb +15 -15
  52. data/lib/cheffish/rspec/matchers/have_updated.rb +37 -37
  53. data/lib/cheffish/rspec/matchers/partially_match.rb +63 -63
  54. data/lib/cheffish/rspec/recipe_run_wrapper.rb +78 -78
  55. data/lib/cheffish/rspec/repository_support.rb +108 -108
  56. data/lib/cheffish/server_api.rb +52 -52
  57. data/lib/cheffish/version.rb +3 -3
  58. data/lib/cheffish/with_pattern.rb +21 -21
  59. data/spec/functional/fingerprint_spec.rb +64 -64
  60. data/spec/functional/merged_config_spec.rb +19 -19
  61. data/spec/functional/server_api_spec.rb +13 -13
  62. data/spec/integration/chef_acl_spec.rb +892 -879
  63. data/spec/integration/chef_client_spec.rb +105 -105
  64. data/spec/integration/chef_container_spec.rb +33 -33
  65. data/spec/integration/chef_group_spec.rb +309 -309
  66. data/spec/integration/chef_mirror_spec.rb +491 -491
  67. data/spec/integration/chef_node_spec.rb +786 -786
  68. data/spec/integration/chef_organization_spec.rb +226 -226
  69. data/spec/integration/chef_role_spec.rb +78 -78
  70. data/spec/integration/chef_user_spec.rb +85 -85
  71. data/spec/integration/private_key_spec.rb +399 -399
  72. data/spec/integration/recipe_dsl_spec.rb +28 -28
  73. data/spec/integration/rspec/converge_spec.rb +183 -183
  74. data/spec/support/key_support.rb +29 -29
  75. data/spec/support/spec_support.rb +15 -15
  76. data/spec/unit/get_private_key_spec.rb +131 -131
  77. data/spec/unit/recipe_run_wrapper_spec.rb +37 -37
  78. metadata +7 -5
@@ -1,131 +1,131 @@
1
- require 'cheffish/key_formatter'
2
- require 'cheffish/chef_provider_base'
3
-
4
- class Cheffish::ActorProviderBase < Cheffish::ChefProviderBase
5
-
6
- def create_actor
7
- if new_resource.before
8
- new_resource.before.call(new_resource)
9
- end
10
-
11
- # Create or update the client/user
12
- current_public_key = new_json['public_key']
13
- differences = json_differences(current_json, new_json)
14
- if current_resource_exists?
15
- # Update the actor if it's different
16
- if differences.size > 0
17
- description = [ "update #{actor_type} #{new_resource.name} at #{actor_path}" ] + differences
18
- converge_by description do
19
- result = rest.put("#{actor_path}/#{new_resource.name}", normalize_for_put(new_json))
20
- current_public_key, current_public_key_format = Cheffish::KeyFormatter.decode(result['public_key']) if result['public_key']
21
- end
22
- end
23
- else
24
- # Create the actor if it's missing
25
- if !new_public_key
26
- raise "You must specify a public key to create a #{actor_type}! Use the private_key resource to create a key, and pass it in with source_key_path."
27
- end
28
- description = [ "create #{actor_type} #{new_resource.name} at #{actor_path}" ] + differences
29
- converge_by description do
30
- result = rest.post("#{actor_path}", normalize_for_post(new_json))
31
- current_public_key, current_public_key_format = Cheffish::KeyFormatter.decode(result['public_key']) if result['public_key']
32
- end
33
- end
34
-
35
- # Write out the public key
36
- if new_resource.output_key_path
37
- # TODO use inline_resource
38
- key_content = Cheffish::KeyFormatter.encode(current_public_key, { :format => new_resource.output_key_format })
39
- if !current_resource.output_key_path
40
- action = 'create'
41
- elsif key_content != IO.read(current_resource.output_key_path)
42
- action = 'overwrite'
43
- else
44
- action = nil
45
- end
46
- if action
47
- converge_by "#{action} public key #{new_resource.output_key_path}" do
48
- IO.write(new_resource.output_key_path, key_content)
49
- end
50
- end
51
- # TODO permissions?
52
- end
53
-
54
- if new_resource.after
55
- new_resource.after.call(self, new_json, server_private_key, server_public_key)
56
- end
57
- end
58
-
59
- def delete_actor
60
- if current_resource_exists?
61
- converge_by "delete #{actor_type} #{new_resource.name} at #{actor_path}" do
62
- rest.delete("#{actor_path}/#{new_resource.name}")
63
- Chef::Log.info("#{new_resource} deleted #{actor_type} #{new_resource.name} at #{rest.url}")
64
- end
65
- end
66
- if current_resource.output_key_path
67
- converge_by "delete public key #{current_resource.output_key_path}" do
68
- ::File.unlink(current_resource.output_key_path)
69
- end
70
- end
71
- end
72
-
73
- def new_public_key
74
- @new_public_key ||= begin
75
- if new_resource.source_key
76
- if new_resource.source_key.is_a?(String)
77
- key, key_format = Cheffish::KeyFormatter.decode(new_resource.source_key)
78
-
79
- if key.private?
80
- key.public_key
81
- else
82
- key
83
- end
84
- elsif new_resource.source_key.private?
85
- new_resource.source_key.public_key
86
- else
87
- new_resource.source_key
88
- end
89
- elsif new_resource.source_key_path
90
- source_key_path = new_resource.source_key_path
91
- if Pathname.new(source_key_path).relative?
92
- source_key_str, source_key_path = Cheffish.get_private_key_with_path(source_key_path, run_context.config)
93
- else
94
- source_key_str = IO.read(source_key_path)
95
- end
96
- source_key, source_key_format = Cheffish::KeyFormatter.decode(source_key_str, new_resource.source_key_pass_phrase, source_key_path)
97
- if source_key.private?
98
- source_key.public_key
99
- else
100
- source_key
101
- end
102
- else
103
- nil
104
- end
105
- end
106
- end
107
-
108
- def augment_new_json(json)
109
- if new_public_key
110
- json['public_key'] = new_public_key.to_pem
111
- end
112
- json
113
- end
114
-
115
- def load_current_resource
116
- begin
117
- json = rest.get("#{actor_path}/#{new_resource.name}")
118
- @current_resource = json_to_resource(json)
119
- rescue Net::HTTPServerException => e
120
- if e.response.code == "404"
121
- @current_resource = not_found_resource
122
- else
123
- raise
124
- end
125
- end
126
-
127
- if new_resource.output_key_path && ::File.exist?(new_resource.output_key_path)
128
- current_resource.output_key_path = new_resource.output_key_path
129
- end
130
- end
131
- end
1
+ require 'cheffish/key_formatter'
2
+ require 'cheffish/chef_provider_base'
3
+
4
+ class Cheffish::ActorProviderBase < Cheffish::ChefProviderBase
5
+
6
+ def create_actor
7
+ if new_resource.before
8
+ new_resource.before.call(new_resource)
9
+ end
10
+
11
+ # Create or update the client/user
12
+ current_public_key = new_json['public_key']
13
+ differences = json_differences(current_json, new_json)
14
+ if current_resource_exists?
15
+ # Update the actor if it's different
16
+ if differences.size > 0
17
+ description = [ "update #{actor_type} #{new_resource.name} at #{actor_path}" ] + differences
18
+ converge_by description do
19
+ result = rest.put("#{actor_path}/#{new_resource.name}", normalize_for_put(new_json))
20
+ current_public_key, current_public_key_format = Cheffish::KeyFormatter.decode(result['public_key']) if result['public_key']
21
+ end
22
+ end
23
+ else
24
+ # Create the actor if it's missing
25
+ if !new_public_key
26
+ raise "You must specify a public key to create a #{actor_type}! Use the private_key resource to create a key, and pass it in with source_key_path."
27
+ end
28
+ description = [ "create #{actor_type} #{new_resource.name} at #{actor_path}" ] + differences
29
+ converge_by description do
30
+ result = rest.post("#{actor_path}", normalize_for_post(new_json))
31
+ current_public_key, current_public_key_format = Cheffish::KeyFormatter.decode(result['public_key']) if result['public_key']
32
+ end
33
+ end
34
+
35
+ # Write out the public key
36
+ if new_resource.output_key_path
37
+ # TODO use inline_resource
38
+ key_content = Cheffish::KeyFormatter.encode(current_public_key, { :format => new_resource.output_key_format })
39
+ if !current_resource.output_key_path
40
+ action = 'create'
41
+ elsif key_content != IO.read(current_resource.output_key_path)
42
+ action = 'overwrite'
43
+ else
44
+ action = nil
45
+ end
46
+ if action
47
+ converge_by "#{action} public key #{new_resource.output_key_path}" do
48
+ IO.write(new_resource.output_key_path, key_content)
49
+ end
50
+ end
51
+ # TODO permissions?
52
+ end
53
+
54
+ if new_resource.after
55
+ new_resource.after.call(self, new_json, server_private_key, server_public_key)
56
+ end
57
+ end
58
+
59
+ def delete_actor
60
+ if current_resource_exists?
61
+ converge_by "delete #{actor_type} #{new_resource.name} at #{actor_path}" do
62
+ rest.delete("#{actor_path}/#{new_resource.name}")
63
+ Chef::Log.info("#{new_resource} deleted #{actor_type} #{new_resource.name} at #{rest.url}")
64
+ end
65
+ end
66
+ if current_resource.output_key_path
67
+ converge_by "delete public key #{current_resource.output_key_path}" do
68
+ ::File.unlink(current_resource.output_key_path)
69
+ end
70
+ end
71
+ end
72
+
73
+ def new_public_key
74
+ @new_public_key ||= begin
75
+ if new_resource.source_key
76
+ if new_resource.source_key.is_a?(String)
77
+ key, key_format = Cheffish::KeyFormatter.decode(new_resource.source_key)
78
+
79
+ if key.private?
80
+ key.public_key
81
+ else
82
+ key
83
+ end
84
+ elsif new_resource.source_key.private?
85
+ new_resource.source_key.public_key
86
+ else
87
+ new_resource.source_key
88
+ end
89
+ elsif new_resource.source_key_path
90
+ source_key_path = new_resource.source_key_path
91
+ if Pathname.new(source_key_path).relative?
92
+ source_key_str, source_key_path = Cheffish.get_private_key_with_path(source_key_path, run_context.config)
93
+ else
94
+ source_key_str = IO.read(source_key_path)
95
+ end
96
+ source_key, source_key_format = Cheffish::KeyFormatter.decode(source_key_str, new_resource.source_key_pass_phrase, source_key_path)
97
+ if source_key.private?
98
+ source_key.public_key
99
+ else
100
+ source_key
101
+ end
102
+ else
103
+ nil
104
+ end
105
+ end
106
+ end
107
+
108
+ def augment_new_json(json)
109
+ if new_public_key
110
+ json['public_key'] = new_public_key.to_pem
111
+ end
112
+ json
113
+ end
114
+
115
+ def load_current_resource
116
+ begin
117
+ json = rest.get("#{actor_path}/#{new_resource.name}")
118
+ @current_resource = json_to_resource(json)
119
+ rescue Net::HTTPServerException => e
120
+ if e.response.code == "404"
121
+ @current_resource = not_found_resource
122
+ else
123
+ raise
124
+ end
125
+ end
126
+
127
+ if new_resource.output_key_path && ::File.exist?(new_resource.output_key_path)
128
+ current_resource.output_key_path = new_resource.output_key_path
129
+ end
130
+ end
131
+ end
@@ -1,184 +1,184 @@
1
- require 'cheffish/version'
2
- require 'chef/dsl/recipe'
3
- require 'chef/event_dispatch/base'
4
- require 'chef/event_dispatch/dispatcher'
5
- require 'chef/node'
6
- require 'chef/run_context'
7
- require 'chef/runner'
8
- require 'forwardable'
9
- require 'chef/providers'
10
- require 'chef/resources'
11
-
12
- module Cheffish
13
- class BasicChefClient
14
- include Chef::DSL::Recipe
15
-
16
- def initialize(node = nil, events = nil, **chef_config)
17
- if !node
18
- node = Chef::Node.new
19
- node.name 'basic_chef_client'
20
- node.automatic[:platform] = 'basic_chef_client'
21
- node.automatic[:platform_version] = Cheffish::VERSION
22
- end
23
-
24
- # Decide on the config we want for this chef client
25
- @chef_config = chef_config
26
-
27
- with_chef_config do
28
- @cookbook_name = 'basic_chef_client'
29
- @event_catcher = BasicChefClientEvents.new
30
- dispatcher = Chef::EventDispatch::Dispatcher.new(@event_catcher)
31
- case events
32
- when nil
33
- when Array
34
- events.each { |e| dispatcher.register(e) } if events
35
- else
36
- dispatcher.register(events)
37
- end
38
- @run_context = Chef::RunContext.new(node, {}, dispatcher)
39
- @updated = []
40
- @cookbook_name = 'basic_chef_client'
41
- end
42
- end
43
-
44
- extend Forwardable
45
-
46
- # Stuff recipes need
47
- attr_reader :chef_config
48
- attr_reader :run_context
49
- attr_accessor :cookbook_name
50
- attr_accessor :recipe_name
51
- def_delegators :@run_context, :resource_collection, :immediate_notifications, :delayed_notifications
52
-
53
- def add_resource(resource)
54
- with_chef_config do
55
- resource.run_context = run_context
56
- run_context.resource_collection.insert(resource)
57
- end
58
- end
59
-
60
- def load_block(&block)
61
- with_chef_config do
62
- @recipe_name = 'block'
63
- instance_eval(&block)
64
- end
65
- end
66
-
67
- def converge
68
- with_chef_config do
69
- Chef::Runner.new(self).converge
70
- end
71
- end
72
-
73
- def updates
74
- @event_catcher.updates
75
- end
76
-
77
- def updated?
78
- @event_catcher.updates.size > 0
79
- end
80
-
81
- # Builds a resource sans context, which can be later used in a new client's
82
- # add_resource() method.
83
- def self.build_resource(type, name, created_at=nil, &resource_attrs_block)
84
- created_at ||= caller[0]
85
- result = BasicChefClient.new.tap do |client|
86
- client.with_chef_config do
87
- client.build_resource(type, name, created_at, &resource_attrs_block)
88
- end
89
- end
90
- result
91
- end
92
-
93
- def self.inline_resource(provider, provider_action, *resources, &block)
94
- events = ProviderEventForwarder.new(provider, provider_action)
95
- client = BasicChefClient.new(provider.node, events)
96
- client.with_chef_config do
97
- resources.each do |resource|
98
- client.add_resource(resource)
99
- end
100
- end
101
- client.load_block(&block) if block
102
- client.converge
103
- client.updated?
104
- end
105
-
106
- def self.converge_block(node = nil, events = nil, &block)
107
- client = BasicChefClient.new(node, events)
108
- client.load_block(&block)
109
- client.converge
110
- client.updated?
111
- end
112
-
113
- def with_chef_config(&block)
114
- old_chef_config = Chef::Config.save
115
- if chef_config[:log_location]
116
- old_loggers = Chef::Log.loggers
117
- Chef::Log.init(chef_config[:log_location])
118
- end
119
- if chef_config[:log_level]
120
- old_level = Chef::Log.level
121
- Chef::Log.level(chef_config[:log_level])
122
- end
123
- # if chef_config[:stdout]
124
- # old_stdout = $stdout
125
- # $stdout = chef_config[:stdout]
126
- # end
127
- # if chef_config[:stderr]
128
- # old_stderr = $stderr
129
- # $stderr = chef_config[:stderr]
130
- # end
131
- begin
132
- deep_merge_config(chef_config, Chef::Config)
133
- block.call
134
- ensure
135
- # $stdout = old_stdout if chef_config[:stdout]
136
- # $stderr = old_stderr if chef_config[:stderr]
137
- if old_loggers
138
- Chef::Log.logger = old_loggers.shift
139
- old_loggers.each { |l| Chef::Log.loggers.push(l) }
140
- elsif chef_config[:log_level]
141
- Chef::Log.level = old_level
142
- end
143
- Chef::Config.restore(old_chef_config)
144
- end
145
- end
146
-
147
- def deep_merge_config(src, dest)
148
- src.each do |name, value|
149
- if value.is_a?(Hash) && dest[name].is_a?(Hash)
150
- deep_merge_config(value, dest[name])
151
- else
152
- dest[name] = value
153
- end
154
- end
155
- end
156
-
157
- class BasicChefClientEvents < Chef::EventDispatch::Base
158
- def initialize
159
- @updates = []
160
- end
161
-
162
- attr_reader :updates
163
-
164
- # Called after a resource has been completely converged.
165
- def resource_updated(resource, action)
166
- updates << [ resource, action ]
167
- end
168
- end
169
-
170
- class ProviderEventForwarder < Chef::EventDispatch::Base
171
- def initialize(provider, provider_action)
172
- @provider = provider
173
- @provider_action = provider_action
174
- end
175
-
176
- attr_reader :provider
177
- attr_reader :provider_action
178
-
179
- def resource_update_applied(resource, action, update)
180
- provider.run_context.events.resource_update_applied(provider.new_resource, provider_action, update)
181
- end
182
- end
183
- end
184
- end
1
+ require 'cheffish/version'
2
+ require 'chef/dsl/recipe'
3
+ require 'chef/event_dispatch/base'
4
+ require 'chef/event_dispatch/dispatcher'
5
+ require 'chef/node'
6
+ require 'chef/run_context'
7
+ require 'chef/runner'
8
+ require 'forwardable'
9
+ require 'chef/providers'
10
+ require 'chef/resources'
11
+
12
+ module Cheffish
13
+ class BasicChefClient
14
+ include Chef::DSL::Recipe
15
+
16
+ def initialize(node = nil, events = nil, **chef_config)
17
+ if !node
18
+ node = Chef::Node.new
19
+ node.name 'basic_chef_client'
20
+ node.automatic[:platform] = 'basic_chef_client'
21
+ node.automatic[:platform_version] = Cheffish::VERSION
22
+ end
23
+
24
+ # Decide on the config we want for this chef client
25
+ @chef_config = chef_config
26
+
27
+ with_chef_config do
28
+ @cookbook_name = 'basic_chef_client'
29
+ @event_catcher = BasicChefClientEvents.new
30
+ dispatcher = Chef::EventDispatch::Dispatcher.new(@event_catcher)
31
+ case events
32
+ when nil
33
+ when Array
34
+ events.each { |e| dispatcher.register(e) } if events
35
+ else
36
+ dispatcher.register(events)
37
+ end
38
+ @run_context = Chef::RunContext.new(node, {}, dispatcher)
39
+ @updated = []
40
+ @cookbook_name = 'basic_chef_client'
41
+ end
42
+ end
43
+
44
+ extend Forwardable
45
+
46
+ # Stuff recipes need
47
+ attr_reader :chef_config
48
+ attr_reader :run_context
49
+ attr_accessor :cookbook_name
50
+ attr_accessor :recipe_name
51
+ def_delegators :@run_context, :resource_collection, :immediate_notifications, :delayed_notifications
52
+
53
+ def add_resource(resource)
54
+ with_chef_config do
55
+ resource.run_context = run_context
56
+ run_context.resource_collection.insert(resource)
57
+ end
58
+ end
59
+
60
+ def load_block(&block)
61
+ with_chef_config do
62
+ @recipe_name = 'block'
63
+ instance_eval(&block)
64
+ end
65
+ end
66
+
67
+ def converge
68
+ with_chef_config do
69
+ Chef::Runner.new(self).converge
70
+ end
71
+ end
72
+
73
+ def updates
74
+ @event_catcher.updates
75
+ end
76
+
77
+ def updated?
78
+ @event_catcher.updates.size > 0
79
+ end
80
+
81
+ # Builds a resource sans context, which can be later used in a new client's
82
+ # add_resource() method.
83
+ def self.build_resource(type, name, created_at=nil, &resource_attrs_block)
84
+ created_at ||= caller[0]
85
+ result = BasicChefClient.new.tap do |client|
86
+ client.with_chef_config do
87
+ client.build_resource(type, name, created_at, &resource_attrs_block)
88
+ end
89
+ end
90
+ result
91
+ end
92
+
93
+ def self.inline_resource(provider, provider_action, *resources, &block)
94
+ events = ProviderEventForwarder.new(provider, provider_action)
95
+ client = BasicChefClient.new(provider.node, events)
96
+ client.with_chef_config do
97
+ resources.each do |resource|
98
+ client.add_resource(resource)
99
+ end
100
+ end
101
+ client.load_block(&block) if block
102
+ client.converge
103
+ client.updated?
104
+ end
105
+
106
+ def self.converge_block(node = nil, events = nil, &block)
107
+ client = BasicChefClient.new(node, events)
108
+ client.load_block(&block)
109
+ client.converge
110
+ client.updated?
111
+ end
112
+
113
+ def with_chef_config(&block)
114
+ old_chef_config = Chef::Config.save
115
+ if chef_config[:log_location]
116
+ old_loggers = Chef::Log.loggers
117
+ Chef::Log.init(chef_config[:log_location])
118
+ end
119
+ if chef_config[:log_level]
120
+ old_level = Chef::Log.level
121
+ Chef::Log.level(chef_config[:log_level])
122
+ end
123
+ # if chef_config[:stdout]
124
+ # old_stdout = $stdout
125
+ # $stdout = chef_config[:stdout]
126
+ # end
127
+ # if chef_config[:stderr]
128
+ # old_stderr = $stderr
129
+ # $stderr = chef_config[:stderr]
130
+ # end
131
+ begin
132
+ deep_merge_config(chef_config, Chef::Config)
133
+ block.call
134
+ ensure
135
+ # $stdout = old_stdout if chef_config[:stdout]
136
+ # $stderr = old_stderr if chef_config[:stderr]
137
+ if old_loggers
138
+ Chef::Log.logger = old_loggers.shift
139
+ old_loggers.each { |l| Chef::Log.loggers.push(l) }
140
+ elsif chef_config[:log_level]
141
+ Chef::Log.level = old_level
142
+ end
143
+ Chef::Config.restore(old_chef_config)
144
+ end
145
+ end
146
+
147
+ def deep_merge_config(src, dest)
148
+ src.each do |name, value|
149
+ if value.is_a?(Hash) && dest[name].is_a?(Hash)
150
+ deep_merge_config(value, dest[name])
151
+ else
152
+ dest[name] = value
153
+ end
154
+ end
155
+ end
156
+
157
+ class BasicChefClientEvents < Chef::EventDispatch::Base
158
+ def initialize
159
+ @updates = []
160
+ end
161
+
162
+ attr_reader :updates
163
+
164
+ # Called after a resource has been completely converged.
165
+ def resource_updated(resource, action)
166
+ updates << [ resource, action ]
167
+ end
168
+ end
169
+
170
+ class ProviderEventForwarder < Chef::EventDispatch::Base
171
+ def initialize(provider, provider_action)
172
+ @provider = provider
173
+ @provider_action = provider_action
174
+ end
175
+
176
+ attr_reader :provider
177
+ attr_reader :provider_action
178
+
179
+ def resource_update_applied(resource, action, update)
180
+ provider.run_context.events.resource_update_applied(provider.new_resource, provider_action, update)
181
+ end
182
+ end
183
+ end
184
+ end