poise 2.0.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 +10 -0
- data/.kitchen.travis.yml +9 -0
- data/.kitchen.yml +18 -0
- data/.rubocop.yml +2 -0
- data/.travis.yml +25 -0
- data/.yardopts +5 -0
- data/Berksfile +26 -0
- data/Berksfile.lock +10 -0
- data/CHANGELOG.md +58 -0
- data/Gemfile +32 -0
- data/LICENSE +202 -0
- data/README.md +198 -0
- data/Rakefile +17 -0
- data/lib/poise.rb +71 -0
- data/lib/poise/error.rb +24 -0
- data/lib/poise/helpers.rb +33 -0
- data/lib/poise/helpers/chefspec_matchers.rb +92 -0
- data/lib/poise/helpers/defined_in.rb +111 -0
- data/lib/poise/helpers/fused.rb +127 -0
- data/lib/poise/helpers/include_recipe.rb +62 -0
- data/lib/poise/helpers/inversion.rb +374 -0
- data/lib/poise/helpers/inversion/options_provider.rb +41 -0
- data/lib/poise/helpers/inversion/options_resource.rb +101 -0
- data/lib/poise/helpers/lazy_default.rb +62 -0
- data/lib/poise/helpers/lwrp_polyfill.rb +96 -0
- data/lib/poise/helpers/notifying_block.rb +78 -0
- data/lib/poise/helpers/option_collector.rb +117 -0
- data/lib/poise/helpers/resource_name.rb +99 -0
- data/lib/poise/helpers/subcontext_block.rb +58 -0
- data/lib/poise/helpers/subresources.rb +29 -0
- data/lib/poise/helpers/subresources/child.rb +217 -0
- data/lib/poise/helpers/subresources/container.rb +165 -0
- data/lib/poise/helpers/subresources/default_containers.rb +73 -0
- data/lib/poise/helpers/template_content.rb +165 -0
- data/lib/poise/provider.rb +55 -0
- data/lib/poise/resource.rb +75 -0
- data/lib/poise/subcontext.rb +27 -0
- data/lib/poise/subcontext/resource_collection.rb +56 -0
- data/lib/poise/subcontext/runner.rb +50 -0
- data/lib/poise/utils.rb +60 -0
- data/lib/poise/utils/resource_provider_mixin.rb +53 -0
- data/lib/poise/version.rb +20 -0
- data/poise.gemspec +38 -0
- data/test/cookbooks/poise_test/attributes/default.rb +17 -0
- data/test/cookbooks/poise_test/libraries/app.rb +43 -0
- data/test/cookbooks/poise_test/libraries/app_config.rb +46 -0
- data/test/cookbooks/poise_test/libraries/inversion.rb +67 -0
- data/test/cookbooks/poise_test/metadata.rb +18 -0
- data/test/cookbooks/poise_test/recipes/default.rb +25 -0
- data/test/cookbooks/poise_test/recipes/inversion.rb +42 -0
- data/test/gemfiles/chef-12.0.gemfile +18 -0
- data/test/gemfiles/chef-12.1.gemfile +18 -0
- data/test/gemfiles/chef-12.2.gemfile +18 -0
- data/test/gemfiles/chef-12.gemfile +18 -0
- data/test/gemfiles/master.gemfile +20 -0
- data/test/integration/default/serverspec/default_spec.rb +35 -0
- data/test/integration/default/serverspec/inversion_spec.rb +43 -0
- data/test/spec/helpers/chefspec_matchers_spec.rb +45 -0
- data/test/spec/helpers/defined_in_spec.rb +62 -0
- data/test/spec/helpers/fused_spec.rb +92 -0
- data/test/spec/helpers/include_recipe_spec.rb +34 -0
- data/test/spec/helpers/inversion/options_resource_spec.rb +212 -0
- data/test/spec/helpers/inversion_spec.rb +273 -0
- data/test/spec/helpers/lazy_default_spec.rb +74 -0
- data/test/spec/helpers/lwrp_polyfill_spec.rb +128 -0
- data/test/spec/helpers/notifying_block_spec.rb +87 -0
- data/test/spec/helpers/option_collector_spec.rb +82 -0
- data/test/spec/helpers/resource_name_spec.rb +39 -0
- data/test/spec/helpers/subcontext_block_spec.rb +94 -0
- data/test/spec/helpers/subresources/child_spec.rb +339 -0
- data/test/spec/helpers/subresources/container_spec.rb +175 -0
- data/test/spec/helpers/template_context_spec.rb +147 -0
- data/test/spec/poise_spec.rb +185 -0
- data/test/spec/provider_spec.rb +28 -0
- data/test/spec/resource_spec.rb +85 -0
- data/test/spec/spec_helper.rb +18 -0
- data/test/spec/utils/resource_provider_mixin_spec.rb +52 -0
- data/test/spec/utils_spec.rb +110 -0
- metadata +190 -0
@@ -0,0 +1,99 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2013-2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require 'chef/mixin/convert_to_class_name'
|
18
|
+
|
19
|
+
|
20
|
+
module Poise
|
21
|
+
module Helpers
|
22
|
+
# A resource mixin to automatically set @resource_name.
|
23
|
+
#
|
24
|
+
# @since 1.0.0
|
25
|
+
# @example
|
26
|
+
# class MyResource < Chef::Resource
|
27
|
+
# include Poise::Helpers::ResourceName
|
28
|
+
# provides(:my_resource)
|
29
|
+
# end
|
30
|
+
module ResourceName
|
31
|
+
def initialize(*args)
|
32
|
+
super
|
33
|
+
# If provides() was explicitly set, unconditionally set @resource_name.
|
34
|
+
# This helps when subclassing core Chef resources which set it
|
35
|
+
# themselves in #initialize.
|
36
|
+
if self.class.resource_name(false)
|
37
|
+
@resource_name = self.class.resource_name
|
38
|
+
else
|
39
|
+
@resource_name ||= self.class.resource_name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @!classmethods
|
44
|
+
module ClassMethods
|
45
|
+
# Set the DSL name for the the resource class.
|
46
|
+
#
|
47
|
+
# @param name [Symbol] Name of the resource.
|
48
|
+
# @return [void]
|
49
|
+
# @example
|
50
|
+
# class MyResource < Chef::Resource
|
51
|
+
# include Poise::Resource::ResourceName
|
52
|
+
# provides(:my_resource)
|
53
|
+
# end
|
54
|
+
def provides(name)
|
55
|
+
# Patch self.constantize so this can cope with anonymous classes.
|
56
|
+
# This does require that the anonymous class define self.name though.
|
57
|
+
if self.name && respond_to?(:constantize)
|
58
|
+
old_constantize = instance_method(:constantize)
|
59
|
+
define_singleton_method(:constantize) do |const_name|
|
60
|
+
( const_name == self.name ) ? self : old_constantize.bind(self).call(const_name)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
# Store the name for later.
|
64
|
+
@provides_name = name
|
65
|
+
# Call the original if present. The defined? is for old Chef.
|
66
|
+
super if defined?(super)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Retreive the DSL name for the resource class. If not set explicitly
|
70
|
+
# via {provides} this will try to auto-detect based on the class name.
|
71
|
+
#
|
72
|
+
# @param auto [Boolean] Try to auto-detect based on class name.
|
73
|
+
# @return [Symbol]
|
74
|
+
def resource_name(auto=true)
|
75
|
+
return @provides_name if @provides_name
|
76
|
+
@provides_name || if name && name.start_with?('Chef::Resource')
|
77
|
+
Chef::Mixin::ConvertToClassName.convert_to_snake_case(name, 'Chef::Resource').to_sym
|
78
|
+
elsif name
|
79
|
+
Chef::Mixin::ConvertToClassName.convert_to_snake_case(name.split('::').last).to_sym
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Used by Resource#to_text to find the human name for the resource.
|
84
|
+
#
|
85
|
+
# @api private
|
86
|
+
def dsl_name
|
87
|
+
resource_name.to_s
|
88
|
+
end
|
89
|
+
|
90
|
+
def included(klass)
|
91
|
+
super
|
92
|
+
klass.extend(ClassMethods)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
extend ClassMethods
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2013-2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require 'poise/subcontext/resource_collection'
|
18
|
+
|
19
|
+
|
20
|
+
module Poise
|
21
|
+
module Helpers
|
22
|
+
# A provider mixin to help with creating subcontexts. Mostly for internal
|
23
|
+
# use within Poise.
|
24
|
+
#
|
25
|
+
# @since 1.0.0
|
26
|
+
module SubcontextBlock
|
27
|
+
private
|
28
|
+
|
29
|
+
def subcontext_block(parent_context=nil, &block)
|
30
|
+
# Setup a sub-run-context.
|
31
|
+
parent_context ||= @run_context
|
32
|
+
sub_run_context = parent_context.dup
|
33
|
+
sub_run_context.resource_collection = Poise::Subcontext::ResourceCollection.new(parent_context.resource_collection)
|
34
|
+
|
35
|
+
# Declare sub-resources within the sub-run-context. Since they
|
36
|
+
# are declared here, they do not pollute the parent run-context.
|
37
|
+
begin
|
38
|
+
outer_run_context = @run_context
|
39
|
+
@run_context = sub_run_context
|
40
|
+
instance_eval(&block)
|
41
|
+
ensure
|
42
|
+
@run_context = outer_run_context
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return the inner context to do other things with
|
46
|
+
sub_run_context
|
47
|
+
end
|
48
|
+
|
49
|
+
def global_resource_collection
|
50
|
+
collection = @run_context.resource_collection
|
51
|
+
while collection.respond_to?(:parent) && collection.parent
|
52
|
+
collection = collection.parent
|
53
|
+
end
|
54
|
+
collection
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
|
18
|
+
module Poise
|
19
|
+
module Helpers
|
20
|
+
# Mixins and helpers for managing subresources, resources with a
|
21
|
+
# parent/child relationship.
|
22
|
+
#
|
23
|
+
# @since 2.0.0
|
24
|
+
module Subresources
|
25
|
+
autoload :Child, 'poise/helpers/subresources/child'
|
26
|
+
autoload :Container, 'poise/helpers/subresources/container'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,217 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2013-2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require 'chef/resource'
|
18
|
+
|
19
|
+
require 'poise/error'
|
20
|
+
require 'poise/helpers/subresources/default_containers'
|
21
|
+
|
22
|
+
|
23
|
+
module Poise
|
24
|
+
module Helpers
|
25
|
+
module Subresources
|
26
|
+
# A resource mixin for child subresources.
|
27
|
+
#
|
28
|
+
# @since 1.0.0
|
29
|
+
module Child
|
30
|
+
# Little class used to fix up the display of subresources in #to_text.
|
31
|
+
# Without this you get the full parent resource shown for @parent et al.
|
32
|
+
# @api private
|
33
|
+
class ParentRef
|
34
|
+
attr_accessor :resource
|
35
|
+
|
36
|
+
def initialize(resource)
|
37
|
+
@resource = resource
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_text
|
41
|
+
if @resource.nil?
|
42
|
+
'nil'
|
43
|
+
else
|
44
|
+
@resource.to_s
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @overload parent()
|
50
|
+
# Get the parent resource for this child. This may be nil if the
|
51
|
+
# resource is set to parent_optional = true.
|
52
|
+
# @return [Chef::Resource, nil]
|
53
|
+
# @overload parent(val)
|
54
|
+
# Set the parent resource. The parent can be set as resource
|
55
|
+
# object, a string (either a bare resource name or a type[name]
|
56
|
+
# string), or a type:name hash.
|
57
|
+
# @param val [String, Hash, Chef::Resource] Parent resource to set.
|
58
|
+
# @return [Chef::Resource, nil]
|
59
|
+
def parent(val=nil)
|
60
|
+
_parent(:parent, self.class.parent_type, self.class.parent_optional, self.class.parent_auto, val)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Register ourself with parents in case this is not a nested resource.
|
64
|
+
#
|
65
|
+
# @api private
|
66
|
+
def after_created
|
67
|
+
super
|
68
|
+
self.class.parent_attributes.each_key do |name|
|
69
|
+
parent = self.send(name)
|
70
|
+
parent.register_subresource(self) if parent
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# Generic form of the parent getter/setter.
|
77
|
+
#
|
78
|
+
# @since 2.0.0
|
79
|
+
# @see #parent
|
80
|
+
def _parent(name, parent_type, parent_optional, parent_auto, val=nil)
|
81
|
+
# Allow using a DSL symbol as the parent type.
|
82
|
+
if parent_type.is_a?(Symbol)
|
83
|
+
parent_type = Chef::Resource.resource_for_node(parent_type, node)
|
84
|
+
end
|
85
|
+
# Grab the ivar for local use.
|
86
|
+
parent_ref = instance_variable_get(:"@#{name}")
|
87
|
+
if val
|
88
|
+
if val.is_a?(String) && !val.include?('[')
|
89
|
+
raise Poise::Error.new('Cannot use a string parent without defining a parent type') if parent_type == Chef::Resource
|
90
|
+
val = "#{parent_type.resource_name}[#{val}]"
|
91
|
+
end
|
92
|
+
if val.is_a?(String) || val.is_a?(Hash)
|
93
|
+
parent = @run_context.resource_collection.find(val)
|
94
|
+
else
|
95
|
+
parent = val
|
96
|
+
end
|
97
|
+
if !parent.is_a?(parent_type)
|
98
|
+
raise Poise::Error.new("Parent resource is not an instance of #{parent_type.name}: #{val.inspect}")
|
99
|
+
end
|
100
|
+
parent_ref = ParentRef.new(parent)
|
101
|
+
elsif !parent_ref
|
102
|
+
if parent_auto
|
103
|
+
# Automatic sibling lookup for sequential composition.
|
104
|
+
# Find the last instance of the parent class as the default parent.
|
105
|
+
# This is super flaky and should only be a last resort.
|
106
|
+
parent = Poise::Helpers::Subresources::DefaultContainers.find(parent_type, run_context)
|
107
|
+
end
|
108
|
+
# Can't find a valid parent, if it wasn't optional raise an error.
|
109
|
+
raise Poise::Error.new("No parent found for #{self}") unless parent || parent_optional
|
110
|
+
parent_ref = ParentRef.new(parent)
|
111
|
+
else
|
112
|
+
parent = parent_ref.resource
|
113
|
+
end
|
114
|
+
# Store the ivar back.
|
115
|
+
instance_variable_set(:"@#{name}", parent_ref)
|
116
|
+
# Return the actual resource.
|
117
|
+
parent
|
118
|
+
end
|
119
|
+
|
120
|
+
module ClassMethods
|
121
|
+
# @overload parent_type()
|
122
|
+
# Get the class of the default parent link on this resource.
|
123
|
+
# @return [Class, Symbol]
|
124
|
+
# @overload parent_type(type)
|
125
|
+
# Set the class of the default parent link on this resource.
|
126
|
+
# @param type [Class, Symbol] Class to set.
|
127
|
+
# @return [Class, Symbol]
|
128
|
+
def parent_type(type=nil)
|
129
|
+
if type
|
130
|
+
raise Poise::Error.new("Parent type must be a class, symbol, or true, got #{type.inspect}") unless type.is_a?(Class) || type.is_a?(Symbol) || type == true
|
131
|
+
@parent_type = type
|
132
|
+
end
|
133
|
+
@parent_type || (superclass.respond_to?(:parent_type) ? superclass.parent_type : Chef::Resource)
|
134
|
+
end
|
135
|
+
|
136
|
+
# @overload parent_optional()
|
137
|
+
# Get the optional mode for the default parent link on this resource.
|
138
|
+
# @return [Boolean]
|
139
|
+
# @overload parent_optional(val)
|
140
|
+
# Set the optional mode for the default parent link on this resource.
|
141
|
+
# @param val [Boolean] Mode to set.
|
142
|
+
# @return [Boolean]
|
143
|
+
def parent_optional(val=nil)
|
144
|
+
unless val.nil?
|
145
|
+
@parent_optional = val
|
146
|
+
end
|
147
|
+
if @parent_optional.nil?
|
148
|
+
superclass.respond_to?(:parent_optional) ? superclass.parent_optional : false
|
149
|
+
else
|
150
|
+
@parent_optional
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# @overload parent_auto()
|
155
|
+
# Get the auto-detect mode for the default parent link on this resource.
|
156
|
+
# @return [Boolean]
|
157
|
+
# @overload parent_auto(val)
|
158
|
+
# Set the auto-detect mode for the default parent link on this resource.
|
159
|
+
# @param val [Boolean] Mode to set.
|
160
|
+
# @return [Boolean]
|
161
|
+
def parent_auto(val=nil)
|
162
|
+
unless val.nil?
|
163
|
+
@parent_auto = val
|
164
|
+
end
|
165
|
+
if @parent_auto.nil?
|
166
|
+
superclass.respond_to?(:parent_auto) ? superclass.parent_auto : true
|
167
|
+
else
|
168
|
+
@parent_auto
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Create a new kind of parent link.
|
173
|
+
#
|
174
|
+
# @since 2.0.0
|
175
|
+
# @param name [Symbol] Name of the relationship. This becomes a method
|
176
|
+
# name on the resource instance.
|
177
|
+
# @param type [Class] Class of the parent.
|
178
|
+
# @param optional [Boolean] If the parent is optional.
|
179
|
+
# @param auto [Boolean] If the parent is auto-detected.
|
180
|
+
# @return [void]
|
181
|
+
def parent_attribute(name, type: Chef::Resource, optional: false, auto: true)
|
182
|
+
name = :"parent_#{name}"
|
183
|
+
(@parent_attributes ||= {})[name] = type
|
184
|
+
define_method(name) do |val=nil|
|
185
|
+
_parent(name, type, optional, auto, val)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Return the name of all parent relationships on this class.
|
190
|
+
#
|
191
|
+
# @since 2.0.0
|
192
|
+
# @return [Hash<Symbol, Class>]
|
193
|
+
def parent_attributes
|
194
|
+
{}.tap do |attrs|
|
195
|
+
# Grab superclass's attributes if possible.
|
196
|
+
attrs.update(superclass.parent_attributes) if superclass.respond_to?(:parent_attributes)
|
197
|
+
# Local default parent.
|
198
|
+
attrs[:parent] = parent_type
|
199
|
+
# Extra locally defined parents.
|
200
|
+
attrs.update(@parent_attributes) if @parent_attributes
|
201
|
+
# Remove anything with the type set to true.
|
202
|
+
attrs.reject! {|name, type| type == true }
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# @api private
|
207
|
+
def included(klass)
|
208
|
+
super
|
209
|
+
klass.extend(ClassMethods)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
extend ClassMethods
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2013-2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require 'chef/dsl/recipe'
|
18
|
+
|
19
|
+
require 'poise/helpers/subcontext_block'
|
20
|
+
require 'poise/helpers/subresources/default_containers'
|
21
|
+
|
22
|
+
|
23
|
+
module Poise
|
24
|
+
module Helpers
|
25
|
+
module Subresources
|
26
|
+
# A resource mixin for subresource containers.
|
27
|
+
#
|
28
|
+
# @since 1.0.0
|
29
|
+
module Container
|
30
|
+
# A resource collection that has much more condensed text output. This
|
31
|
+
# is used to show the value of @subresources during Chef's error formatting.
|
32
|
+
# @api private
|
33
|
+
class NoPrintingResourceCollection < Chef::ResourceCollection
|
34
|
+
def to_text
|
35
|
+
"[#{all_resources.map(&:to_s).join(', ')}]"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
include SubcontextBlock
|
40
|
+
include Chef::DSL::Recipe
|
41
|
+
|
42
|
+
attr_reader :subresources
|
43
|
+
|
44
|
+
def initialize(*args)
|
45
|
+
super
|
46
|
+
@subresources = NoPrintingResourceCollection.new
|
47
|
+
end
|
48
|
+
|
49
|
+
def after_created
|
50
|
+
super
|
51
|
+
# Register
|
52
|
+
Poise::Helpers::Subresources::DefaultContainers.register!(self, run_context)
|
53
|
+
unless @subresources.empty?
|
54
|
+
Chef::Log.debug("[#{self}] Adding subresources to collection:")
|
55
|
+
# Because after_create is run before adding the container to the resource collection
|
56
|
+
# we need to jump through some hoops to get it swapped into place.
|
57
|
+
self_ = self
|
58
|
+
order_fixer = Chef::Resource::RubyBlock.new('subresource_order_fixer', @run_context)
|
59
|
+
order_fixer.block do
|
60
|
+
collection = self_.run_context.resource_collection
|
61
|
+
# Delete the current container resource from its current position.
|
62
|
+
collection.all_resources.delete(self_)
|
63
|
+
# Replace the order fixer with the container so it runs before all
|
64
|
+
# subresources.
|
65
|
+
collection.all_resources[collection.iterator.position] = self_
|
66
|
+
# Hack for Chef 11 to reset the resources_by_name position too.
|
67
|
+
# @todo Remove this when I drop support for Chef 11.
|
68
|
+
if resources_by_name = collection.instance_variable_get(:@resources_by_name)
|
69
|
+
resources_by_name[self_.to_s] = collection.iterator.position
|
70
|
+
end
|
71
|
+
# Step back so we re-run the "current" resource, which is now the
|
72
|
+
# container.
|
73
|
+
collection.iterator.skip_back
|
74
|
+
end
|
75
|
+
@run_context.resource_collection.insert(order_fixer)
|
76
|
+
@subresources.each do |r|
|
77
|
+
Chef::Log.debug(" * #{r}")
|
78
|
+
@run_context.resource_collection.insert(r)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def declare_resource(type, name, created_at=nil, &block)
|
84
|
+
Chef::Log.debug("[#{self}] Creating subresource from #{type}(#{name})")
|
85
|
+
self_ = self
|
86
|
+
# Used to break block context, non-local return from subcontext_block.
|
87
|
+
resource = []
|
88
|
+
# Grab the caller so we can make the subresource look like it comes from
|
89
|
+
# correct place.
|
90
|
+
created_at ||= caller[0]
|
91
|
+
# Run this inside a subcontext to avoid adding to the current resource collection.
|
92
|
+
# It will end up added later, indirected via @subresources to ensure ordering.
|
93
|
+
subcontext_block do
|
94
|
+
namespace = if self.class.container_namespace == true
|
95
|
+
# If the value is true, use the name of the container resource.
|
96
|
+
self.name
|
97
|
+
elsif self.class.container_namespace.is_a?(Proc)
|
98
|
+
instance_eval(&self.class.container_namespace)
|
99
|
+
else
|
100
|
+
self.class.container_namespace
|
101
|
+
end
|
102
|
+
sub_name = if name && !name.empty?
|
103
|
+
if namespace
|
104
|
+
"#{namespace}::#{name}"
|
105
|
+
else
|
106
|
+
name
|
107
|
+
end
|
108
|
+
else
|
109
|
+
# If you pass in nil or '', you just get the namespace or parent name.
|
110
|
+
namespace || self.name
|
111
|
+
end
|
112
|
+
resource << super(type, sub_name, created_at) do
|
113
|
+
# Apply the correct parent before anything else so it is available
|
114
|
+
# in after_created for the subresource.
|
115
|
+
parent(self_) if respond_to?(:parent)
|
116
|
+
# Run the resource block.
|
117
|
+
instance_exec(&block) if block
|
118
|
+
end
|
119
|
+
end
|
120
|
+
# Try and add to subresources. For normal subresources this is handled
|
121
|
+
# in the after_created.
|
122
|
+
register_subresource(resource.first) if resource.first
|
123
|
+
# Return whatever we have
|
124
|
+
resource.first
|
125
|
+
end
|
126
|
+
|
127
|
+
def register_subresource(resource)
|
128
|
+
subresources.lookup(resource)
|
129
|
+
rescue Chef::Exceptions::ResourceNotFound
|
130
|
+
Chef::Log.debug("[#{self}] Adding #{resource} to subresources")
|
131
|
+
subresources.insert(resource)
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
# Thanks Array.flatten, big help you are. Specifically the
|
137
|
+
# method_missing in the recipe DSL will make a flatten on an array of
|
138
|
+
# resources fail, so make this safe.
|
139
|
+
def to_ary
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
|
143
|
+
# @!classmethods
|
144
|
+
module ClassMethods
|
145
|
+
def container_namespace(val=nil)
|
146
|
+
@container_namespace = val unless val.nil?
|
147
|
+
if @container_namespace.nil?
|
148
|
+
# Not set here, look at the superclass of true by default for backwards compat.
|
149
|
+
superclass.respond_to?(:container_namespace) ? superclass.container_namespace : true
|
150
|
+
else
|
151
|
+
@container_namespace
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def included(klass)
|
156
|
+
super
|
157
|
+
klass.extend(ClassMethods)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
extend ClassMethods
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|