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.
- checksums.yaml +4 -4
- data/Gemfile +10 -0
- data/LICENSE +201 -201
- data/README.md +120 -120
- data/Rakefile +23 -23
- data/cheffish.gemspec +26 -0
- data/lib/chef/provider/chef_acl.rb +446 -439
- data/lib/chef/provider/chef_client.rb +53 -53
- data/lib/chef/provider/chef_container.rb +55 -55
- data/lib/chef/provider/chef_data_bag.rb +55 -55
- data/lib/chef/provider/chef_data_bag_item.rb +278 -278
- data/lib/chef/provider/chef_environment.rb +83 -83
- data/lib/chef/provider/chef_group.rb +83 -83
- data/lib/chef/provider/chef_mirror.rb +169 -169
- data/lib/chef/provider/chef_node.rb +87 -87
- data/lib/chef/provider/chef_organization.rb +155 -155
- data/lib/chef/provider/chef_resolved_cookbooks.rb +46 -46
- data/lib/chef/provider/chef_role.rb +84 -84
- data/lib/chef/provider/chef_user.rb +59 -59
- data/lib/chef/provider/private_key.rb +225 -225
- data/lib/chef/provider/public_key.rb +88 -88
- data/lib/chef/resource/chef_acl.rb +69 -69
- data/lib/chef/resource/chef_client.rb +48 -48
- data/lib/chef/resource/chef_container.rb +22 -22
- data/lib/chef/resource/chef_data_bag.rb +22 -22
- data/lib/chef/resource/chef_data_bag_item.rb +121 -121
- data/lib/chef/resource/chef_environment.rb +77 -77
- data/lib/chef/resource/chef_group.rb +53 -53
- data/lib/chef/resource/chef_mirror.rb +52 -52
- data/lib/chef/resource/chef_node.rb +22 -22
- data/lib/chef/resource/chef_organization.rb +69 -69
- data/lib/chef/resource/chef_resolved_cookbooks.rb +35 -35
- data/lib/chef/resource/chef_role.rb +110 -110
- data/lib/chef/resource/chef_user.rb +56 -56
- data/lib/chef/resource/private_key.rb +48 -48
- data/lib/chef/resource/public_key.rb +25 -25
- data/lib/cheffish.rb +235 -235
- data/lib/cheffish/actor_provider_base.rb +131 -131
- data/lib/cheffish/basic_chef_client.rb +184 -184
- data/lib/cheffish/chef_provider_base.rb +246 -246
- data/lib/cheffish/chef_run.rb +162 -162
- data/lib/cheffish/chef_run_data.rb +19 -19
- data/lib/cheffish/chef_run_listener.rb +30 -30
- data/lib/cheffish/key_formatter.rb +113 -113
- data/lib/cheffish/merged_config.rb +98 -94
- data/lib/cheffish/recipe_dsl.rb +157 -157
- data/lib/cheffish/rspec.rb +8 -8
- data/lib/cheffish/rspec/chef_run_support.rb +83 -83
- data/lib/cheffish/rspec/matchers.rb +4 -4
- data/lib/cheffish/rspec/matchers/be_idempotent.rb +16 -16
- data/lib/cheffish/rspec/matchers/emit_no_warnings_or_errors.rb +15 -15
- data/lib/cheffish/rspec/matchers/have_updated.rb +37 -37
- data/lib/cheffish/rspec/matchers/partially_match.rb +63 -63
- data/lib/cheffish/rspec/recipe_run_wrapper.rb +78 -78
- data/lib/cheffish/rspec/repository_support.rb +108 -108
- data/lib/cheffish/server_api.rb +52 -52
- data/lib/cheffish/version.rb +3 -3
- data/lib/cheffish/with_pattern.rb +21 -21
- data/spec/functional/fingerprint_spec.rb +64 -64
- data/spec/functional/merged_config_spec.rb +19 -19
- data/spec/functional/server_api_spec.rb +13 -13
- data/spec/integration/chef_acl_spec.rb +892 -879
- data/spec/integration/chef_client_spec.rb +105 -105
- data/spec/integration/chef_container_spec.rb +33 -33
- data/spec/integration/chef_group_spec.rb +309 -309
- data/spec/integration/chef_mirror_spec.rb +491 -491
- data/spec/integration/chef_node_spec.rb +786 -786
- data/spec/integration/chef_organization_spec.rb +226 -226
- data/spec/integration/chef_role_spec.rb +78 -78
- data/spec/integration/chef_user_spec.rb +85 -85
- data/spec/integration/private_key_spec.rb +399 -399
- data/spec/integration/recipe_dsl_spec.rb +28 -28
- data/spec/integration/rspec/converge_spec.rb +183 -183
- data/spec/support/key_support.rb +29 -29
- data/spec/support/spec_support.rb +15 -15
- data/spec/unit/get_private_key_spec.rb +131 -131
- data/spec/unit/recipe_run_wrapper_spec.rb +37 -37
- 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
|