chef_sous_vide 0.1.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 +7 -0
- data/.gitignore +14 -0
- data/.travis.yml +7 -0
- data/Berksfile +11 -0
- data/Berksfile.lock +45 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +297 -0
- data/LICENSE.txt +21 -0
- data/README.md +127 -0
- data/Rakefile +13 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/kitchen.yml +98 -0
- data/lib/sous_vide.rb +10 -0
- data/lib/sous_vide/event_methods.rb +176 -0
- data/lib/sous_vide/handler.rb +186 -0
- data/lib/sous_vide/outputs/json_file.rb +43 -0
- data/lib/sous_vide/outputs/json_http.rb +66 -0
- data/lib/sous_vide/outputs/logger.rb +48 -0
- data/lib/sous_vide/outputs/multi.rb +21 -0
- data/lib/sous_vide/tracked_resource.rb +75 -0
- data/lib/sous_vide/version.rb +3 -0
- data/sous_vide.gemspec +35 -0
- metadata +194 -0
data/Rakefile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
require "cucumber/rake/task"
|
|
3
|
+
|
|
4
|
+
Cucumber::Rake::Task.new
|
|
5
|
+
|
|
6
|
+
task :default => [ "cucumber" ]
|
|
7
|
+
|
|
8
|
+
begin
|
|
9
|
+
require "kitchen/rake_tasks"
|
|
10
|
+
Kitchen::RakeTasks.new
|
|
11
|
+
rescue LoadError
|
|
12
|
+
puts ">>>>> Kitchen gem not loaded, omitting tasks" unless ENV["CI"]
|
|
13
|
+
end
|
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "sous_vide"
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require "irb"
|
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/kitchen.yml
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
<% if (ARGV[0] == "converge") %>
|
|
3
|
+
transport:
|
|
4
|
+
name: rsync
|
|
5
|
+
<% end %>
|
|
6
|
+
|
|
7
|
+
driver:
|
|
8
|
+
name: docker
|
|
9
|
+
use_sudo: false
|
|
10
|
+
provision_command:
|
|
11
|
+
# prevent APT from deleting the APT folder
|
|
12
|
+
- rm /etc/apt/apt.conf.d/docker-clean
|
|
13
|
+
- apt-get install -y -q apt-transport-https rsync
|
|
14
|
+
# allow test-kitchen to use root user (connects via ssh)
|
|
15
|
+
- sed -i 's/prohibit-password/yes/' /etc/ssh/sshd_config
|
|
16
|
+
# disable systemd since it doesn't work in docker
|
|
17
|
+
- ln -sf /bin/true /bin/systemctl
|
|
18
|
+
# install chef-client as part of the image, save time in the runtime
|
|
19
|
+
- curl -LO https://omnitruck.chef.io/install.sh && bash ./install.sh -v 12.17.44 && rm install.sh
|
|
20
|
+
- ln /opt/chef/bin/chef-client /bin/chef-client
|
|
21
|
+
username: root
|
|
22
|
+
password: root
|
|
23
|
+
|
|
24
|
+
verifier:
|
|
25
|
+
sudo: false
|
|
26
|
+
|
|
27
|
+
provisioner:
|
|
28
|
+
install_strategy: 'skip'
|
|
29
|
+
|
|
30
|
+
platforms:
|
|
31
|
+
- name: ubuntu-16.04
|
|
32
|
+
- name: ubuntu-18.04
|
|
33
|
+
|
|
34
|
+
suites:
|
|
35
|
+
- name: default
|
|
36
|
+
includes:
|
|
37
|
+
- ubuntu-16.04
|
|
38
|
+
driver:
|
|
39
|
+
hostname: elasticsearch
|
|
40
|
+
instance_name: elasticsearch
|
|
41
|
+
forward:
|
|
42
|
+
- "5601:5601" # kibana
|
|
43
|
+
- "9200:9200" # es
|
|
44
|
+
run_list:
|
|
45
|
+
- sous_vide::install
|
|
46
|
+
- sous_vide::default
|
|
47
|
+
attributes:
|
|
48
|
+
kitchen:
|
|
49
|
+
roles:
|
|
50
|
+
- elasticsearch
|
|
51
|
+
java:
|
|
52
|
+
jdk_version: 8
|
|
53
|
+
elasticsearch:
|
|
54
|
+
install:
|
|
55
|
+
version: "6.6.2"
|
|
56
|
+
- name: tomcat
|
|
57
|
+
includes:
|
|
58
|
+
- ubuntu-18.04
|
|
59
|
+
- ubuntu-16.04
|
|
60
|
+
driver:
|
|
61
|
+
links:
|
|
62
|
+
- elasticsearch
|
|
63
|
+
run_list:
|
|
64
|
+
- sous_vide::install
|
|
65
|
+
- sous_vide::tomcat
|
|
66
|
+
attributes:
|
|
67
|
+
kitchen:
|
|
68
|
+
roles:
|
|
69
|
+
- tomcat-example
|
|
70
|
+
java:
|
|
71
|
+
jdk_version: 8
|
|
72
|
+
- name: nginx
|
|
73
|
+
includes:
|
|
74
|
+
- ubuntu-16.04
|
|
75
|
+
run_list:
|
|
76
|
+
- sous_vide::install
|
|
77
|
+
- sous_vide::nginx
|
|
78
|
+
attributes:
|
|
79
|
+
kitchen:
|
|
80
|
+
roles:
|
|
81
|
+
- nginx-example
|
|
82
|
+
nginx:
|
|
83
|
+
install_method: "source"
|
|
84
|
+
modules:
|
|
85
|
+
- nginx::headers_more_module
|
|
86
|
+
- nginx::http_auth_request_module
|
|
87
|
+
- nginx::http_echo_module
|
|
88
|
+
- nginx::http_geoip_module
|
|
89
|
+
- nginx::http_gzip_static_module
|
|
90
|
+
- nginx::http_realip_module
|
|
91
|
+
- nginx::http_v2_module
|
|
92
|
+
- nginx::http_ssl_module
|
|
93
|
+
- nginx::http_stub_status_module
|
|
94
|
+
- nginx::naxsi_module
|
|
95
|
+
- nginx::ngx_devel_module
|
|
96
|
+
- nginx::ngx_lua_module
|
|
97
|
+
- nginx::openssl_source
|
|
98
|
+
- nginx::upload_progress_module
|
data/lib/sous_vide.rb
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
module SousVide
|
|
2
|
+
# This module implements Chef event methods. It's based on Chef::EventDispatch::Base.
|
|
3
|
+
#
|
|
4
|
+
# Nested resources are explicitly ignored and the code flow will be as follows:
|
|
5
|
+
#
|
|
6
|
+
# 1. resource_action_start
|
|
7
|
+
# 2. resource_* events (possibly multiple)
|
|
8
|
+
# 3. resource_action_complete
|
|
9
|
+
#
|
|
10
|
+
# The code is intentionally procedural and explicit. If :resource_action_start assigned
|
|
11
|
+
# @processing_now only then other events will work. :resource_action_completed unassigns
|
|
12
|
+
# @processing_now so all events will be ignored until :resource_action_start is called
|
|
13
|
+
# again.
|
|
14
|
+
module EventMethods
|
|
15
|
+
# This hook will always fire whenever chef is about to converge a resource, including why_run
|
|
16
|
+
# mode, notifications or skipped resources.
|
|
17
|
+
def resource_action_start(new_resource, action, notification_type, notifying_resource)
|
|
18
|
+
if nested?(new_resource) # ignore nested resources
|
|
19
|
+
new_r_name = "#{new_resource.resource_name}[#{new_resource.name}]##{action}"
|
|
20
|
+
debug("Received :resource_action_start on #{new_r_name}.",
|
|
21
|
+
"It's a nested resource and will be ignored.")
|
|
22
|
+
return false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# This is a delayed notification. From now on we are in 'delayed' run phase.
|
|
26
|
+
if notification_type == :delayed && @run_phase != "delayed"
|
|
27
|
+
debug("Changed run phase to 'delayed'.")
|
|
28
|
+
@run_phase = "delayed"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
@processing_now = create(chef_resource: new_resource, action: action)
|
|
32
|
+
debug("Received :resource_action_start on #{@processing_now}.")
|
|
33
|
+
|
|
34
|
+
@execution_order += 1
|
|
35
|
+
@processing_now.execution_order = @execution_order
|
|
36
|
+
@processing_now.execution_phase = @run_phase
|
|
37
|
+
@processing_now.started_at = Time.now.strftime("%F %T")
|
|
38
|
+
|
|
39
|
+
@processing_now.chef_resource_handle = new_resource
|
|
40
|
+
|
|
41
|
+
@processing_now.before_notifications = new_resource.before_notifications.size
|
|
42
|
+
@processing_now.immediate_notifications = new_resource.immediate_notifications.size
|
|
43
|
+
@processing_now.delayed_notifications = new_resource.delayed_notifications.size
|
|
44
|
+
|
|
45
|
+
# When notifying resource is present notification_type will also be present. It is nil for
|
|
46
|
+
# delayed notifications.
|
|
47
|
+
if notifying_resource
|
|
48
|
+
_name = "#{notifying_resource.resource_name}[#{notifying_resource.name}]"
|
|
49
|
+
debug("Notified from #{_name} (:#{notification_type})")
|
|
50
|
+
@processing_now.notifying_resource = _name
|
|
51
|
+
end
|
|
52
|
+
@processing_now.notification_type = notification_type
|
|
53
|
+
true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def resource_updated(new_resource, _action)
|
|
57
|
+
return false if @processing_now.nil? || nested?(new_resource) # ignore nested resources
|
|
58
|
+
|
|
59
|
+
debug("Received :resource_updated on #{@processing_now}")
|
|
60
|
+
@processing_now.status = "updated"
|
|
61
|
+
true
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Resource is skipped when a guard instruction stops the converge process or when
|
|
65
|
+
# `action :nothing` is used (it's a guard too).
|
|
66
|
+
def resource_skipped(new_resource, _action, conditional)
|
|
67
|
+
return false if @processing_now.nil? || nested?(new_resource) # ignore nested resources
|
|
68
|
+
|
|
69
|
+
debug("Received :resource_skipped on #{@processing_now}", "(#{conditional.to_text})")
|
|
70
|
+
@processing_now.guard_description = conditional.to_text
|
|
71
|
+
@processing_now.status = "skipped"
|
|
72
|
+
true
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def resource_up_to_date(new_resource, _action)
|
|
76
|
+
return false if @processing_now.nil? || nested?(new_resource) # ignore nested resources
|
|
77
|
+
|
|
78
|
+
debug("Received :resource_up_to_date on #{@processing_now}")
|
|
79
|
+
@processing_now.status = "up-to-date"
|
|
80
|
+
true
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def resource_completed(new_resource)
|
|
84
|
+
return false if @processing_now.nil? || nested?(new_resource) # ignore nested resources
|
|
85
|
+
|
|
86
|
+
debug("Received :resource_completed on #{@processing_now}")
|
|
87
|
+
@processing_now.duration_ms = (new_resource.elapsed_time.to_f * 1000).to_i
|
|
88
|
+
|
|
89
|
+
# If a resource has notifications Chef will converge it in a forced_why_run mode to
|
|
90
|
+
# determine if any update will happen and if the notifications should be called.
|
|
91
|
+
#
|
|
92
|
+
# When this event was fired in why_run mode we override it's status to `why-run`.
|
|
93
|
+
#
|
|
94
|
+
# This is also how Chef::Runner#focred_why_run is implemented so it should be reliable.
|
|
95
|
+
#
|
|
96
|
+
# TODO: what happens when :why_run for notification fails?
|
|
97
|
+
if ::Chef::Config[:why_run]
|
|
98
|
+
debug("Resource #{@processing_now.name}##{@processing_now.action} marked why-run",
|
|
99
|
+
"because Chef::Config[:why_run] is true.")
|
|
100
|
+
@processing_now.status = "why-run"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# This resource was not notified by another and is a subject of normal ordered converge
|
|
104
|
+
# process. @resource_collection_cursor is pointing to next resource according to the
|
|
105
|
+
# expanded resource collection.
|
|
106
|
+
#
|
|
107
|
+
# When chef-client fails we will take remaining entries and add to the report as
|
|
108
|
+
# 'unprocessed'. It works because resources are ordered and we can keep track where we are.
|
|
109
|
+
#
|
|
110
|
+
# why-run mode and notifications are not in natural order and must not move the cursor.
|
|
111
|
+
#
|
|
112
|
+
# Having it pointing ahead is relevant because current resource has just been converged
|
|
113
|
+
# (technically 'failed') and it is not 'unprocessed'.
|
|
114
|
+
if !@processing_now.notifying_resource && # not notified
|
|
115
|
+
!::Chef::Config[:why_run] && # not why-run
|
|
116
|
+
@run_phase == "converge" # only converge phase
|
|
117
|
+
|
|
118
|
+
@resource_collection_cursor += 1
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
@processing_now.completed_at = Time.now.strftime("%F %T")
|
|
122
|
+
@processed << @processing_now
|
|
123
|
+
@processing_now = nil
|
|
124
|
+
true
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def resource_failed(new_resource, _action, exception)
|
|
128
|
+
return false if @processing_now.nil? || nested?(new_resource) # ignore nested resources
|
|
129
|
+
|
|
130
|
+
debug("Received :resource_failed on #{@processing_now}")
|
|
131
|
+
@processing_now.status = "failed"
|
|
132
|
+
@processing_now.error_source = new_resource.to_text
|
|
133
|
+
@processing_now.error_output = exception.message
|
|
134
|
+
true
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Resources with retries can succeed on subsequent attempts or ignore_failure option may be
|
|
138
|
+
# set and it's the only place we can capture intermittent errors.
|
|
139
|
+
#
|
|
140
|
+
# This event can fire multiple times, but we capture only the most recent error.
|
|
141
|
+
def resource_failed_retriable(new_resource, _action, _remaining_retries, exception)
|
|
142
|
+
return false if @processing_now.nil? || nested?(new_resource) # ignore nested resources
|
|
143
|
+
|
|
144
|
+
debug("Received :resource_failed_retriable on #{@processing_now}")
|
|
145
|
+
@processing_now.retries += 1
|
|
146
|
+
@processing_now.error_source = new_resource.to_text
|
|
147
|
+
@processing_now.error_output = exception.message
|
|
148
|
+
true
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def converge_start
|
|
152
|
+
debug("Received :converge_start")
|
|
153
|
+
debug("Changed run phase to 'converge'.")
|
|
154
|
+
@run_phase = "converge"
|
|
155
|
+
@run_name ||= [@run_started_at, @chef_node_role, @chef_node_ipv4, @run_id].join(" ")
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def converge_complete
|
|
159
|
+
debug("Received :converge_completed")
|
|
160
|
+
@run_success = true
|
|
161
|
+
@run_completed_at = Time.now.strftime("%F %T")
|
|
162
|
+
send_to_output!
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def converge_failed(_exception)
|
|
166
|
+
debug("Received :converge_failed")
|
|
167
|
+
@run_success = false
|
|
168
|
+
@run_completed_at = Time.now.strftime("%F %T")
|
|
169
|
+
@run_phase = "post-converge"
|
|
170
|
+
debug("Changed run phase to 'post-converge'.")
|
|
171
|
+
|
|
172
|
+
consume_unprocessed_resources!
|
|
173
|
+
send_to_output!
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
require "sous_vide/event_methods"
|
|
2
|
+
require "sous_vide/outputs/json_file"
|
|
3
|
+
require "sous_vide/outputs/json_http"
|
|
4
|
+
require "sous_vide/outputs/logger"
|
|
5
|
+
require "sous_vide/outputs/multi"
|
|
6
|
+
|
|
7
|
+
require "securerandom"
|
|
8
|
+
require "singleton"
|
|
9
|
+
|
|
10
|
+
module SousVide
|
|
11
|
+
# == SousVide::Handler
|
|
12
|
+
#
|
|
13
|
+
# The Handler receives event data from chef-client and keeps track of the converge process. It's
|
|
14
|
+
# essentially a stream parser hooked into Chef::EventDispatch.
|
|
15
|
+
#
|
|
16
|
+
# Event methods are all in SousVide::EventMethods module. This file contains logic that does not
|
|
17
|
+
# deal with events directly.
|
|
18
|
+
class Handler
|
|
19
|
+
include Singleton
|
|
20
|
+
include EventMethods
|
|
21
|
+
|
|
22
|
+
attr_accessor :chef_run_context,
|
|
23
|
+
:logger,
|
|
24
|
+
:sous_output,
|
|
25
|
+
:processing_now,
|
|
26
|
+
:processed,
|
|
27
|
+
:run_phase,
|
|
28
|
+
:run_id,
|
|
29
|
+
:run_name
|
|
30
|
+
|
|
31
|
+
# Enables the handler. Call it anywhere in the recipe, ideally as early as possible.
|
|
32
|
+
#
|
|
33
|
+
# SousVide::Handler.register(node.run_context)
|
|
34
|
+
#
|
|
35
|
+
# All converge-time resources will be included in the report regardless at what point
|
|
36
|
+
# registration happens.
|
|
37
|
+
#
|
|
38
|
+
# Compile-time resources defined before registration will not be included.
|
|
39
|
+
# TODO: see client.rb start_handlers as an option.
|
|
40
|
+
#
|
|
41
|
+
# `chef_handler` resource does not support subscribing to :events so we have give up DSL and
|
|
42
|
+
# use Chef API.
|
|
43
|
+
#
|
|
44
|
+
# The `Chef.event_handler` DSL could be used but dealing with returns and exceptions in procs
|
|
45
|
+
# is a pain.
|
|
46
|
+
def self.register(run_context)
|
|
47
|
+
::Chef::Log.info "Registering SousVide"
|
|
48
|
+
|
|
49
|
+
instance.chef_run_context = run_context
|
|
50
|
+
instance.post_initialize
|
|
51
|
+
|
|
52
|
+
run_context.events.register(instance)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def initialize
|
|
56
|
+
@execution_order = 0
|
|
57
|
+
@resource_collection_cursor = 0
|
|
58
|
+
|
|
59
|
+
@processed = []
|
|
60
|
+
@processing_now = nil
|
|
61
|
+
|
|
62
|
+
@run_started_at = Time.now.strftime("%F %T")
|
|
63
|
+
|
|
64
|
+
# Not related to RunStatus#run_id, it's our internal run id.
|
|
65
|
+
@run_id = SecureRandom.uuid.split("-").first # => 596e9d00
|
|
66
|
+
@run_phase = "compile"
|
|
67
|
+
|
|
68
|
+
# Default to Chef logger, but can be changed to anything that responds to #call
|
|
69
|
+
@logger = ::Chef::Log
|
|
70
|
+
@sous_output = Outputs::Logger.new(logger: @logger)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# This is called in #register, as soon as @chef_run_context is available.
|
|
74
|
+
def post_initialize
|
|
75
|
+
@chef_node_ipv4 = @chef_run_context.node["ipaddress"] || "<no ip>"
|
|
76
|
+
@chef_node_role = @chef_run_context.node["roles"].first || "<no role>"
|
|
77
|
+
@chef_node_instance_id = @chef_run_context.node.name
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def run_data
|
|
82
|
+
{
|
|
83
|
+
chef_run_id: @run_id,
|
|
84
|
+
chef_run_name: @run_name,
|
|
85
|
+
chef_run_started_at: @run_started_at,
|
|
86
|
+
chef_run_completed_at: @run_completed_at,
|
|
87
|
+
chef_run_success: @run_success
|
|
88
|
+
}
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def node_data
|
|
92
|
+
{
|
|
93
|
+
chef_node_ipv4: @chef_node_ipv4,
|
|
94
|
+
chef_node_instance_id: @chef_node_instance_id,
|
|
95
|
+
chef_node_role: @chef_node_role
|
|
96
|
+
}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def create(chef_resource:, action:)
|
|
100
|
+
tracked = TrackedResource.new(action: action,
|
|
101
|
+
name: chef_resource.name,
|
|
102
|
+
type: chef_resource.resource_name)
|
|
103
|
+
|
|
104
|
+
tracked.cookbook_name = chef_resource.cookbook_name || "<Dynamically Defined Resource>"
|
|
105
|
+
tracked.cookbook_recipe = chef_resource.recipe_name || "<Dynamically Defined Resource>"
|
|
106
|
+
tracked.source_line = chef_resource.source_line || "<Dynamically Defined Resource>"
|
|
107
|
+
tracked.chef_resource_handle = chef_resource
|
|
108
|
+
tracked
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# We will ignore nested resources. Once a top level resource triggered :resource_action
|
|
112
|
+
# started any events not related to it will be ignored.
|
|
113
|
+
def nested?(chef_resource)
|
|
114
|
+
@processing_now &&
|
|
115
|
+
@processing_now.chef_resource_handle != chef_resource
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# When chef-client fails we haven't seen all resources and need to backfill the handler.
|
|
119
|
+
def consume_unprocessed_resources!
|
|
120
|
+
all_known_resources = expand_chef_resources!
|
|
121
|
+
|
|
122
|
+
# No unprocessed resources left. Failure likely occured on last resource or in a delayed
|
|
123
|
+
# notification.
|
|
124
|
+
# TODO: check delayed notification failure
|
|
125
|
+
return if @resource_collection_cursor >= all_known_resources.size
|
|
126
|
+
|
|
127
|
+
unprocessed = all_known_resources[@resource_collection_cursor..-1]
|
|
128
|
+
|
|
129
|
+
# We will pass unprocessed resources via :resource_action_start and :resource_completed so
|
|
130
|
+
# they will end up in @processed array, but with status set to 'unprocessed' and execution
|
|
131
|
+
# phase 'post-converge'.
|
|
132
|
+
#
|
|
133
|
+
# TODO: consider placing these resources before delayed notification, currently they are
|
|
134
|
+
# always at the very end. It _maybe_ makes sense.
|
|
135
|
+
unprocessed.each do |tracked|
|
|
136
|
+
resource_action_start(
|
|
137
|
+
tracked.chef_resource_handle, # new_resource
|
|
138
|
+
tracked.action, # action
|
|
139
|
+
nil, # notification_type
|
|
140
|
+
nil # notifying_resource
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
resource_completed(
|
|
144
|
+
tracked.chef_resource_handle # new_resource
|
|
145
|
+
)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Resources with multiple actions must be expanded, ie. given resource:
|
|
150
|
+
#
|
|
151
|
+
# service 'nginx' do
|
|
152
|
+
# action [:enable, :start]
|
|
153
|
+
# end
|
|
154
|
+
#
|
|
155
|
+
# After expansion we should have 2 resources:
|
|
156
|
+
#
|
|
157
|
+
# * service[nginx] with action :enable
|
|
158
|
+
# * service[nginx] with action :start
|
|
159
|
+
#
|
|
160
|
+
# We keep track of the progress and on failure we will pick up unprocessed resources from here
|
|
161
|
+
# to feed the handler in post-converge stage.
|
|
162
|
+
#
|
|
163
|
+
# On a successful chef-client run @processed will contain all resources and this method won't
|
|
164
|
+
# be called.
|
|
165
|
+
def expand_chef_resources!
|
|
166
|
+
chef_run_context.resource_collection.flat_map do |chef_resource|
|
|
167
|
+
Array(chef_resource.action).map do |action|
|
|
168
|
+
create(chef_resource: chef_resource, action: action)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def send_to_output!
|
|
174
|
+
@sous_output.call(run_data: run_data, node_data: node_data, resources_data: @processed)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def debug(*args)
|
|
178
|
+
message = args.compact.join(" ")
|
|
179
|
+
logger.debug(message)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def logger
|
|
183
|
+
@logger ||= ::Chef::Log
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|