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,17 @@
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_boiler/rakefile'
@@ -0,0 +1,71 @@
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'
18
+ require 'chef/resource'
19
+
20
+ require 'poise/utils/resource_provider_mixin'
21
+
22
+
23
+ module Poise
24
+ include Poise::Utils::ResourceProviderMixin
25
+ autoload :Helpers, 'poise/helpers'
26
+ autoload :Provider, 'poise/provider'
27
+ autoload :Resource, 'poise/resource'
28
+ autoload :Subcontext, 'poise/subcontext'
29
+ autoload :Utils, 'poise/utils'
30
+ autoload :VERSION, 'poise/version'
31
+ end
32
+
33
+ # Callable form to allow passing in options:
34
+ # include Poise(ParentResource)
35
+ # include Poise(parent: ParentResource)
36
+ # include Poise(container: true)
37
+ def Poise(options={})
38
+ # Allow passing a class as a shortcut
39
+ if options.is_a?(Class)
40
+ options = {parent: options}
41
+ end
42
+
43
+ # Create a new anonymous module
44
+ mod = Module.new
45
+
46
+ # Fake the name.
47
+ mod.define_singleton_method(:name) do
48
+ super() || 'Poise'
49
+ end
50
+
51
+ mod.define_singleton_method(:included) do |klass|
52
+ super(klass)
53
+ # Pull in the main helper to cover most of the needed logic.
54
+ klass.class_exec { include Poise }
55
+ # Set the defined_in values as needed.
56
+ klass.poise_defined!(caller)
57
+ # Resource-specific options.
58
+ if klass < Chef::Resource
59
+ klass.poise_subresource(options[:parent], options[:parent_optional], options[:parent_auto]) if options[:parent]
60
+ klass.poise_subresource_container(options[:container_namespace]) if options[:container]
61
+ klass.poise_fused if options[:fused]
62
+ klass.poise_inversion(options[:inversion_options_resource]) if options[:inversion]
63
+ end
64
+ # Provider-specific options.
65
+ if klass < Chef::Provider
66
+ klass.poise_inversion(options[:inversion], options[:inversion_attribute]) if options[:inversion]
67
+ end
68
+ end
69
+
70
+ mod
71
+ end
@@ -0,0 +1,24 @@
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
+ # Base exception class for Poise errors.
20
+ #
21
+ # @since 2.0.0
22
+ class Error < Exception
23
+ end
24
+ end
@@ -0,0 +1,33 @@
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
+ autoload :ChefspecMatchers, 'poise/helpers/chefspec_matchers'
21
+ autoload :DefinedIn, 'poise/helpers/defined_in'
22
+ autoload :Fused, 'poise/helpers/fused'
23
+ autoload :IncludeRecipe, 'poise/helpers/include_recipe'
24
+ autoload :Inversion, 'poise/helpers/inversion'
25
+ autoload :LazyDefault, 'poise/helpers/lazy_default'
26
+ autoload :LWRPPolyfill, 'poise/helpers/lwrp_polyfill'
27
+ autoload :NotifyingBlock, 'poise/helpers/notifying_block'
28
+ autoload :OptionCollector, 'poise/helpers/option_collector'
29
+ autoload :ResourceName, 'poise/helpers/resource_name'
30
+ autoload :Subresources, 'poise/helpers/subresources'
31
+ autoload :TemplateContent, 'poise/helpers/template_content'
32
+ end
33
+ end
@@ -0,0 +1,92 @@
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
+ begin
18
+ require 'chefspec'
19
+ require 'rspec/expectations'
20
+ rescue LoadError
21
+ # Don't panic! We will no-op later on if these aren't available.
22
+ end
23
+
24
+ require 'poise/helpers/lwrp_polyfill'
25
+ require 'poise/helpers/resource_name'
26
+
27
+
28
+ module Poise
29
+ module Helpers
30
+ # A resource mixin to register ChefSpec matchers for a resource
31
+ # automatically.
32
+ #
33
+ # If you are using the provides() form for naming resources, ensure that is
34
+ # set before declaring actions.
35
+ #
36
+ # @since 2.0.0
37
+ # @example Define a class
38
+ # class Chef::Resource::MyResource < Chef::Resource
39
+ # include Poise::Helpers::ChefspecMatchers
40
+ # actions(:run)
41
+ # end
42
+ # @example Use a matcher
43
+ # expect(chef_run).to run_my_resource('...')
44
+ module ChefspecMatchers
45
+ include Poise::Helpers::LWRPPolyfill::Resource
46
+ include Poise::Helpers::ResourceName
47
+
48
+ # Create a matcher for a given resource type and action. This is
49
+ # idempotent so if a matcher already exists, it will not be recreated.
50
+ #
51
+ # @api private
52
+ def self.create_matcher(resource, action)
53
+ # Check that we have everything we need.
54
+ return unless defined?(ChefSpec) && defined?(RSpec::Matchers) && resource
55
+ method = :"#{action}_#{resource}"
56
+ return if RSpec::Matchers.method_defined?(method)
57
+ RSpec::Matchers.send(:define_method, method) do |resource_name|
58
+ ChefSpec::Matchers::ResourceMatcher.new(resource, action, resource_name)
59
+ end
60
+ end
61
+
62
+ # @!classmethods
63
+ module ClassMethods
64
+ # Create a resource-level matcher for this resource.
65
+ #
66
+ # @see Resource::ResourceName.provides
67
+ def provides(name)
68
+ ChefSpec.define_matcher(name) if defined?(ChefSpec)
69
+ super
70
+ end
71
+
72
+ # Create matchers for all declared actions.
73
+ #
74
+ # @see Resource::LWRPPolyfill.actions
75
+ def actions(*names)
76
+ super.tap do |actions|
77
+ actions.each do |action|
78
+ ChefspecMatchers.create_matcher(resource_name, action)
79
+ end
80
+ end
81
+ end
82
+
83
+ def included(klass)
84
+ super
85
+ klass.extend ClassMethods
86
+ end
87
+ end
88
+
89
+ extend ClassMethods
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,111 @@
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
+ require 'poise/error'
18
+ require 'poise/utils'
19
+
20
+
21
+ module Poise
22
+ module Helpers
23
+ # A mixin to track where a resource or provider was defined. This can
24
+ # provide either the filename of the class or the cookbook it was defined in.
25
+ #
26
+ # @since 2.0.0
27
+ # @example
28
+ # class MyProvider < Chef::provider
29
+ # include Poise::Helpers::DefinedIn
30
+ #
31
+ # def action_create
32
+ # template '...' do
33
+ # # ...
34
+ # cookbook new_resource.poise_defined_in
35
+ # end
36
+ # end
37
+ # end
38
+ module DefinedIn
39
+ # Wrapper for {.poise_defined_in_cookbook} to pass the run context for you.
40
+ #
41
+ # @see .poise_defined_in_cookbook
42
+ # @param file [String, nil] Optional file path to check instead of the path
43
+ # this class was defined in.
44
+ # @return [String]
45
+ def poise_defined_in_cookbook(file=nil)
46
+ self.class.poise_defined_in_cookbook(run_context, file)
47
+ end
48
+
49
+ # @!classmethods
50
+ module ClassMethods
51
+ # The file this class or module was defined in, or nil if it isn't found.
52
+ #
53
+ # @return [String]
54
+ def poise_defined_in
55
+ raise Poise::Error.new("Unable to determine location of #{self.name}") unless @poise_defined_in
56
+ @poise_defined_in
57
+ end
58
+
59
+ # The cookbook this class or module was defined in. Can pass a file to
60
+ # check that instead.
61
+ #
62
+ # @param run_context [Chef::RunContext] Run context to check cookbooks in.
63
+ # @param file [String, nil] Optional file path to check instead of the
64
+ # path this class was defined in.
65
+ # @return [String]
66
+ def poise_defined_in_cookbook(run_context, file=nil)
67
+ file ||= poise_defined_in
68
+ Chef::Log.debug("[#{self.name}] Checking cookbook name for #{file}")
69
+ Poise::Utils.find_cookbook_name(run_context, file).tap do |cookbook|
70
+ Chef::Log.debug("[#{self.name}] found cookbook #{cookbook.inspect}")
71
+ end
72
+ end
73
+
74
+ # Record that the class/module was defined. Called automatically by Ruby
75
+ # for all normal cases.
76
+ #
77
+ # @param caller_array [Array<String>] A strack trace returned by #caller.
78
+ # @return [void]
79
+ def poise_defined!(caller_array)
80
+ # Only try to set this once.
81
+ return if @poise_defined_in
82
+ # Path to ignore, assumes Halite transformation which I'm not thrilled
83
+ # about.
84
+ poise_libraries = File.expand_path('../..', __FILE__)
85
+ # Parse out just the filenames.
86
+ caller_array = caller_array.map {|line| line.split(/:/, 2).first }
87
+ # Find the first non-poise line.
88
+ caller_path = caller_array.find do |line|
89
+ !line.start_with?(poise_libraries)
90
+ end
91
+ Chef::Log.debug("[#{self.name}] Recording poise_defined_in as #{caller_path}")
92
+ @poise_defined_in = caller_path
93
+ end
94
+
95
+ # @api private
96
+ def inherited(klass)
97
+ super
98
+ klass.poise_defined!(caller)
99
+ end
100
+
101
+ def included(klass)
102
+ super
103
+ klass.extend(ClassMethods)
104
+ klass.poise_defined!(caller)
105
+ end
106
+ end
107
+
108
+ extend ClassMethods
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,127 @@
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'
18
+
19
+
20
+ module Poise
21
+ module Helpers
22
+ # Resource mixin to create "fused" resources where the resource and provider
23
+ # are implemented in the same class.
24
+ #
25
+ # @since 2.0.0
26
+ # @example
27
+ # class Chef::Resource::MyResource < Chef::Resource
28
+ # include Poise(fused: true)
29
+ # attribute(:path, kind_of: String)
30
+ # attribute(:message, kind_of: String)
31
+ # action(:run) do
32
+ # file new_resource.path do
33
+ # content new_resource.message
34
+ # end
35
+ # end
36
+ # end
37
+ module Fused
38
+ # Hack is_a? so that the DSL will consider this a Provider for the
39
+ # purposes of attaching enclosing_provider.
40
+ #
41
+ # @api private
42
+ # @param klass [Class]
43
+ # @return [Boolean]
44
+ def is_a?(klass)
45
+ if klass == Chef::Provider
46
+ # Lies, damn lies, and Ruby code.
47
+ true
48
+ else
49
+ super
50
+ end
51
+ end
52
+
53
+ # Hack provider_for_action so that the resource is also the provider.
54
+ #
55
+ # @api private
56
+ # @param action [Symbol]
57
+ # @return [Chef::Provider]
58
+ def provider_for_action(action)
59
+ provider(self.class.fused_provider_class) unless provider
60
+ super
61
+ end
62
+
63
+ # @!classmethods
64
+ module ClassMethods
65
+ # Define a provider action. The block should contain the usual provider
66
+ # code.
67
+ #
68
+ # @param name [Symbol] Name of the action.
69
+ # @param block [Proc] Action implementation.
70
+ # @example
71
+ # action(:run) do
72
+ # file '/temp' do
73
+ # user 'root'
74
+ # content 'temp'
75
+ # end
76
+ # end
77
+ def action(name, &block)
78
+ fused_actions[name.to_sym] = block
79
+ # Make sure this action is allowed, also sets the default if first.
80
+ if respond_to?(:actions)
81
+ actions(name.to_sym)
82
+ end
83
+ end
84
+
85
+ # Storage accessor for fused action blocks. Maps action name to proc.
86
+ #
87
+ # @api private
88
+ # @return [Hash<Symbol, Proc>]
89
+ def fused_actions
90
+ (@fused_actions ||= {})
91
+ end
92
+
93
+ # Create a provider class for the fused actions in this resource.
94
+ # Inherits from the fused provider class of the resource's superclass if
95
+ # present.
96
+ #
97
+ # @api private
98
+ # @return [Class]
99
+ def fused_provider_class
100
+ @fused_provider_class ||= begin
101
+ provider_superclass = begin
102
+ self.superclass.fused_provider_class
103
+ rescue NoMethodError
104
+ Chef::Provider
105
+ end
106
+ actions = fused_actions
107
+ class_name = self.name
108
+ Class.new(provider_superclass) do
109
+ include Poise
110
+ define_singleton_method(:name) { class_name + ' (fused)' }
111
+ actions.each do |action, block|
112
+ define_method(:"action_#{action}", &block)
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ def included(klass)
119
+ super
120
+ klass.extend(ClassMethods)
121
+ end
122
+ end
123
+
124
+ extend ClassMethods
125
+ end
126
+ end
127
+ end