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.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.kitchen.travis.yml +9 -0
  4. data/.kitchen.yml +18 -0
  5. data/.rubocop.yml +2 -0
  6. data/.travis.yml +25 -0
  7. data/.yardopts +5 -0
  8. data/Berksfile +26 -0
  9. data/Berksfile.lock +10 -0
  10. data/CHANGELOG.md +58 -0
  11. data/Gemfile +32 -0
  12. data/LICENSE +202 -0
  13. data/README.md +198 -0
  14. data/Rakefile +17 -0
  15. data/lib/poise.rb +71 -0
  16. data/lib/poise/error.rb +24 -0
  17. data/lib/poise/helpers.rb +33 -0
  18. data/lib/poise/helpers/chefspec_matchers.rb +92 -0
  19. data/lib/poise/helpers/defined_in.rb +111 -0
  20. data/lib/poise/helpers/fused.rb +127 -0
  21. data/lib/poise/helpers/include_recipe.rb +62 -0
  22. data/lib/poise/helpers/inversion.rb +374 -0
  23. data/lib/poise/helpers/inversion/options_provider.rb +41 -0
  24. data/lib/poise/helpers/inversion/options_resource.rb +101 -0
  25. data/lib/poise/helpers/lazy_default.rb +62 -0
  26. data/lib/poise/helpers/lwrp_polyfill.rb +96 -0
  27. data/lib/poise/helpers/notifying_block.rb +78 -0
  28. data/lib/poise/helpers/option_collector.rb +117 -0
  29. data/lib/poise/helpers/resource_name.rb +99 -0
  30. data/lib/poise/helpers/subcontext_block.rb +58 -0
  31. data/lib/poise/helpers/subresources.rb +29 -0
  32. data/lib/poise/helpers/subresources/child.rb +217 -0
  33. data/lib/poise/helpers/subresources/container.rb +165 -0
  34. data/lib/poise/helpers/subresources/default_containers.rb +73 -0
  35. data/lib/poise/helpers/template_content.rb +165 -0
  36. data/lib/poise/provider.rb +55 -0
  37. data/lib/poise/resource.rb +75 -0
  38. data/lib/poise/subcontext.rb +27 -0
  39. data/lib/poise/subcontext/resource_collection.rb +56 -0
  40. data/lib/poise/subcontext/runner.rb +50 -0
  41. data/lib/poise/utils.rb +60 -0
  42. data/lib/poise/utils/resource_provider_mixin.rb +53 -0
  43. data/lib/poise/version.rb +20 -0
  44. data/poise.gemspec +38 -0
  45. data/test/cookbooks/poise_test/attributes/default.rb +17 -0
  46. data/test/cookbooks/poise_test/libraries/app.rb +43 -0
  47. data/test/cookbooks/poise_test/libraries/app_config.rb +46 -0
  48. data/test/cookbooks/poise_test/libraries/inversion.rb +67 -0
  49. data/test/cookbooks/poise_test/metadata.rb +18 -0
  50. data/test/cookbooks/poise_test/recipes/default.rb +25 -0
  51. data/test/cookbooks/poise_test/recipes/inversion.rb +42 -0
  52. data/test/gemfiles/chef-12.0.gemfile +18 -0
  53. data/test/gemfiles/chef-12.1.gemfile +18 -0
  54. data/test/gemfiles/chef-12.2.gemfile +18 -0
  55. data/test/gemfiles/chef-12.gemfile +18 -0
  56. data/test/gemfiles/master.gemfile +20 -0
  57. data/test/integration/default/serverspec/default_spec.rb +35 -0
  58. data/test/integration/default/serverspec/inversion_spec.rb +43 -0
  59. data/test/spec/helpers/chefspec_matchers_spec.rb +45 -0
  60. data/test/spec/helpers/defined_in_spec.rb +62 -0
  61. data/test/spec/helpers/fused_spec.rb +92 -0
  62. data/test/spec/helpers/include_recipe_spec.rb +34 -0
  63. data/test/spec/helpers/inversion/options_resource_spec.rb +212 -0
  64. data/test/spec/helpers/inversion_spec.rb +273 -0
  65. data/test/spec/helpers/lazy_default_spec.rb +74 -0
  66. data/test/spec/helpers/lwrp_polyfill_spec.rb +128 -0
  67. data/test/spec/helpers/notifying_block_spec.rb +87 -0
  68. data/test/spec/helpers/option_collector_spec.rb +82 -0
  69. data/test/spec/helpers/resource_name_spec.rb +39 -0
  70. data/test/spec/helpers/subcontext_block_spec.rb +94 -0
  71. data/test/spec/helpers/subresources/child_spec.rb +339 -0
  72. data/test/spec/helpers/subresources/container_spec.rb +175 -0
  73. data/test/spec/helpers/template_context_spec.rb +147 -0
  74. data/test/spec/poise_spec.rb +185 -0
  75. data/test/spec/provider_spec.rb +28 -0
  76. data/test/spec/resource_spec.rb +85 -0
  77. data/test/spec/spec_helper.rb +18 -0
  78. data/test/spec/utils/resource_provider_mixin_spec.rb +52 -0
  79. data/test/spec/utils_spec.rb +110 -0
  80. metadata +190 -0
@@ -0,0 +1,73 @@
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
+ module Subresources
21
+ # Helpers to track default container resources. This is used to find a
22
+ # default parent for a child with no parent set. It flat out violates
23
+ # encapsulation to allow for the use of default parents to act as
24
+ # system-level defaults even when created in a nested scope.
25
+ #
26
+ # @api private
27
+ # @since 2.0.0
28
+ module DefaultContainers
29
+ # Mutex to sync access to the containers array.
30
+ #
31
+ # @see .containers
32
+ CONTAINER_MUTEX = Mutex.new
33
+
34
+ # Add a resource to the array of default containers.
35
+ #
36
+ # @param resource [Chef::Resource] Resource to add.
37
+ # @param run_context [Chef::RunContext] Context of the current run.
38
+ # @return [void]
39
+ def self.register!(resource, run_context)
40
+ CONTAINER_MUTEX.synchronize do
41
+ containers(run_context) << resource
42
+ end
43
+ end
44
+
45
+ # Find a default container for a resource class.
46
+ #
47
+ # @param klass [Class] Resource class to search for.
48
+ # @param run_context [Chef::RunContext] Context of the current run.
49
+ # @return [Chef::Resource]
50
+ def self.find(klass, run_context)
51
+ CONTAINER_MUTEX.synchronize do
52
+ containers(run_context).reverse_each do |resource|
53
+ return resource if resource.is_a?(klass)
54
+ end
55
+ # Nothing found.
56
+ nil
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ # Get the array of all default container resources.
63
+ #
64
+ # @note MUST BE CALLED FROM A LOCKED CONTEXT!
65
+ # @param run_context [Chef::RunContext] Context of the current run.
66
+ # @return [Array<Chef::Resource>]
67
+ def self.containers(run_context)
68
+ run_context.node.run_state[:poise_default_containers] ||= []
69
+ end
70
+ end
71
+ end
72
+ end
73
+ 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/provider/template_finder'
18
+ require 'chef/mixin/template'
19
+
20
+ require 'poise/helpers/lazy_default'
21
+ require 'poise/helpers/lwrp_polyfill'
22
+ require 'poise/helpers/option_collector'
23
+ require 'poise/utils'
24
+
25
+
26
+ module Poise
27
+ module Helpers
28
+ # A resource mixin to add a new kind of attribute, template content. TODO
29
+ #
30
+ # @since 1.0.0
31
+ module TemplateContent
32
+ include LazyDefault
33
+ include LWRPPolyfill
34
+ include OptionCollector
35
+
36
+ # @!classmethods
37
+ module ClassMethods
38
+ def attribute(name, options={})
39
+ if options.delete(:template)
40
+ name_prefix = name.empty? ? '' : "#{name}_"
41
+
42
+ # If you are reading this, I'm so sorry
43
+ # This is used for computing the default cookbook below
44
+ parent_filename = caller.first.reverse.split(':', 4).last.reverse
45
+
46
+ # If our parent class also declared a template_content attribute on the same name, inherit its options
47
+ if superclass.respond_to?("_#{name_prefix}_template_content_options")
48
+ options = superclass.send("_#{name_prefix}_template_content_options").merge(options)
49
+ end
50
+
51
+ # Template source path if using a template
52
+ attribute("#{name_prefix}source", kind_of: String)
53
+ define_method("_#{name_prefix}source") do
54
+ send("#{name_prefix}source") || maybe_eval(options[:default_source])
55
+ end
56
+
57
+ # Template cookbook name if using a template
58
+ attribute("#{name_prefix}cookbook", kind_of: [String, Symbol], default: lazy do
59
+ if send("#{name_prefix}source")
60
+ cookbook_name
61
+ elsif options[:default_cookbook]
62
+ maybe_eval(options[:default_cookbook])
63
+ else
64
+ Poise::Utils.find_cookbook_name(run_context, parent_filename)
65
+ end
66
+ end)
67
+
68
+ # Template variables if using a template
69
+ attribute("#{name_prefix}options", option_collector: true)
70
+
71
+ # The big one, get/set content, but if you are getting and no
72
+ # explicit content was given, try to render the template
73
+ define_method("#{name_prefix}content") do |arg=nil, no_compute=false|
74
+ ret = set_or_return("#{name_prefix}content", arg, kind_of: String)
75
+ if !ret && !arg && !no_compute
76
+ ret = send("_#{name_prefix}content")
77
+ # Cache the results for next time
78
+ set_or_return("#{name_prefix}content", ret, {}) if ret
79
+ end
80
+ ret
81
+ end
82
+
83
+ # Validate that arguments work
84
+ define_method("_#{name_prefix}validate") do
85
+ if options[:required] && !send("_#{name_prefix}source") && !send("#{name_prefix}content", nil, true)
86
+ raise Chef::Exceptions::ValidationFailed, "#{self}: One of #{name_prefix}source or #{name_prefix}content is required"
87
+ end
88
+ if send("#{name_prefix}source") && send("#{name_prefix}content", nil, true)
89
+ raise Chef::Exceptions::ValidationFailed, "#{self}: Only one of #{name_prefix}source or #{name_prefix}content can be specified"
90
+ end
91
+ end
92
+
93
+ # Monkey patch #after_create to run best-effort validation. Arguments
94
+ # could be changed after creation, but this gives nicer errors for
95
+ # most cases.
96
+ unless options[:no_validate_on_create]
97
+ old_after_created = instance_method(:after_created)
98
+ define_method(:after_created) do
99
+ old_after_created.bind(self).call
100
+ send("_#{name_prefix}validate")
101
+ end
102
+ end
103
+
104
+ # Compile the needed content
105
+ define_method("_#{name_prefix}content") do
106
+ # Run validation again
107
+ send("_#{name_prefix}validate")
108
+ # Get all the relevant parameters
109
+ content = send("#{name_prefix}content", nil, true)
110
+ source = send("_#{name_prefix}source")
111
+ if content
112
+ content # I don't think it can ever hit this branch
113
+ elsif source
114
+ cookbook = send("#{name_prefix}cookbook")
115
+ template_options = send("#{name_prefix}options")
116
+ send("_#{name_prefix}render_template", source, cookbook, template_options)
117
+ else
118
+ maybe_eval(options[:default])
119
+ end
120
+ end
121
+
122
+ # Actually render a template
123
+ define_method("_#{name_prefix}render_template") do |source, cookbook, template_options|
124
+ all_template_options = {}
125
+ all_template_options.update(maybe_eval(options[:default_options])) if options[:default_options]
126
+ all_template_options.update(template_options)
127
+ all_template_options[:new_resource] = self
128
+ finder = Chef::Provider::TemplateFinder.new(run_context, cookbook, node)
129
+ context = Chef::Mixin::Template::TemplateContext.new(all_template_options)
130
+ context[:node] = node
131
+ context[:template_finder] = finder
132
+ context.render_template(finder.find(source))
133
+ end
134
+
135
+ # Used to check if a parent class already defined a template_content thing here
136
+ define_singleton_method("_#{name_prefix}_template_content_options") do
137
+ options
138
+ end
139
+ else
140
+ super if defined?(super)
141
+ end
142
+ end
143
+
144
+ def included(klass)
145
+ super
146
+ klass.extend(ClassMethods)
147
+ end
148
+ end
149
+
150
+ extend ClassMethods
151
+
152
+ private
153
+
154
+ # Evaluate lazy blocks if needed
155
+ def maybe_eval(val)
156
+ if val.is_a?(Chef::DelayedEvaluator)
157
+ instance_eval(&val)
158
+ else
159
+ val
160
+ end
161
+ end
162
+
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,55 @@
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/helpers'
18
+
19
+
20
+ module Poise
21
+ # Master provider mixin for Poise-based providers.
22
+ #
23
+ # @since 1.0.0
24
+ # @example Default helpers.
25
+ # class MyProvider < Chef::Provider
26
+ # include Poise::Provider
27
+ # end
28
+ # @example With optional helpers.
29
+ # class MyProvider < Chef::Provider
30
+ # include Poise::Provider
31
+ # poise_inversion(MyResource)
32
+ # end
33
+ module Provider
34
+ include Poise::Helpers::DefinedIn
35
+ include Poise::Helpers::IncludeRecipe
36
+ include Poise::Helpers::LWRPPolyfill
37
+ include Poise::Helpers::NotifyingBlock
38
+
39
+ # @!classmethods
40
+ module ClassMethods
41
+ def poise_inversion(resource, attribute=nil)
42
+ include Poise::Helpers::Inversion
43
+ inversion_resource(resource)
44
+ inversion_attribute(attribute) if attribute
45
+ end
46
+
47
+ def included(klass)
48
+ super
49
+ klass.extend(ClassMethods)
50
+ end
51
+ end
52
+
53
+ extend ClassMethods
54
+ end
55
+ end
@@ -0,0 +1,75 @@
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/helpers'
18
+
19
+
20
+ module Poise
21
+ # Master resource mixin for Poise-based resources.
22
+ #
23
+ # @since 1.0.0
24
+ # @example Default helpers.
25
+ # class MyResource < Chef::Resource
26
+ # include Poise::Resource
27
+ # end
28
+ # @example With optional helpers.
29
+ # class MyResource < Chef::Resource
30
+ # include Poise::Resource
31
+ # poise_subresource(MyParent)
32
+ # poise_fused
33
+ # end
34
+ module Resource
35
+ include Poise::Helpers::ChefspecMatchers
36
+ include Poise::Helpers::DefinedIn
37
+ include Poise::Helpers::LazyDefault
38
+ include Poise::Helpers::LWRPPolyfill
39
+ include Poise::Helpers::OptionCollector
40
+ include Poise::Helpers::ResourceName
41
+ include Poise::Helpers::TemplateContent
42
+
43
+ # @!classmethods
44
+ module ClassMethods
45
+ def poise_subresource_container(namespace=nil)
46
+ include Poise::Helpers::Subresources::Container
47
+ # false is a valid value.
48
+ container_namespace(namespace) unless namespace.nil?
49
+ end
50
+
51
+ def poise_subresource(parent_type=nil, parent_optional=nil, parent_auto=nil)
52
+ include Poise::Helpers::Subresources::Child
53
+ parent_type(parent_type) if parent_type
54
+ parent_optional(parent_optional) unless parent_optional.nil?
55
+ parent_auto(parent_auto) unless parent_auto.nil?
56
+ end
57
+
58
+ def poise_fused
59
+ include Poise::Helpers::Fused
60
+ end
61
+
62
+ def poise_inversion(options_resource=nil)
63
+ include Poise::Helpers::Inversion
64
+ inversion_options_resource(true) unless options_resource == false
65
+ end
66
+
67
+ def included(klass)
68
+ super
69
+ klass.extend(ClassMethods)
70
+ end
71
+ end
72
+
73
+ extend ClassMethods
74
+ end
75
+ end
@@ -0,0 +1,27 @@
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
+ # Helpers and whatnot for dealing with subcontexts.
20
+ #
21
+ # @api private
22
+ # @since 2.0.0
23
+ module Subcontext
24
+ autoload :ResourceCollection, 'poise/subcontext/resource_collection'
25
+ autoload :Runner, 'poise/subcontext/runner'
26
+ end
27
+ end
@@ -0,0 +1,56 @@
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_collection'
18
+
19
+
20
+ module Poise
21
+ module Subcontext
22
+ # A subclass of the normal Chef ResourceCollection that creates a partially
23
+ # isolated set of resources. Notifications and other resources lookups can
24
+ # propagate out to parent contexts but not back in. This is used to allow
25
+ # black-box resources that are still aware of things in upper contexts.
26
+ #
27
+ # @api private
28
+ # @since 1.0.0
29
+ class ResourceCollection < Chef::ResourceCollection
30
+ attr_accessor :parent
31
+
32
+ def initialize(parent)
33
+ @parent = parent
34
+ super()
35
+ end
36
+
37
+ def lookup(resource)
38
+ super
39
+ rescue Chef::Exceptions::ResourceNotFound
40
+ @parent.lookup(resource)
41
+ end
42
+
43
+ # Iterate and expand all nested contexts
44
+ def recursive_each(&block)
45
+ if @parent
46
+ if @parent.respond_to?(:recursive_each)
47
+ @parent.recursive_each(&block)
48
+ else
49
+ @parent.each(&block)
50
+ end
51
+ end
52
+ each(&block)
53
+ end
54
+ end
55
+ end
56
+ end