poise 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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