chef-resource 0.2.2

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 (55) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +23 -0
  3. data/LICENSE +201 -0
  4. data/README.md +264 -0
  5. data/Rakefile +8 -0
  6. data/files/lib/chef_resource.rb +24 -0
  7. data/files/lib/chef_resource/camel_case.rb +23 -0
  8. data/files/lib/chef_resource/chef.rb +102 -0
  9. data/files/lib/chef_resource/chef_dsl/chef_cookbook_compiler.rb +44 -0
  10. data/files/lib/chef_resource/chef_dsl/chef_recipe.rb +10 -0
  11. data/files/lib/chef_resource/chef_dsl/chef_recipe_dsl_extensions.rb +84 -0
  12. data/files/lib/chef_resource/chef_dsl/chef_resource_base.rb +12 -0
  13. data/files/lib/chef_resource/chef_dsl/chef_resource_class_extensions.rb +30 -0
  14. data/files/lib/chef_resource/chef_dsl/chef_resource_extensions.rb +224 -0
  15. data/files/lib/chef_resource/chef_dsl/chef_resource_log.rb +54 -0
  16. data/files/lib/chef_resource/chef_dsl/resource_container_module.rb +80 -0
  17. data/files/lib/chef_resource/chef_dsl/resource_definition_dsl.rb +128 -0
  18. data/files/lib/chef_resource/constants.rb +8 -0
  19. data/files/lib/chef_resource/errors.rb +31 -0
  20. data/files/lib/chef_resource/lazy_proc.rb +82 -0
  21. data/files/lib/chef_resource/output/nested_converge.rb +91 -0
  22. data/files/lib/chef_resource/output/nested_converge/open_resource.rb +113 -0
  23. data/files/lib/chef_resource/output/region_stream.rb +145 -0
  24. data/files/lib/chef_resource/output/simple_output.rb +83 -0
  25. data/files/lib/chef_resource/resource.rb +428 -0
  26. data/files/lib/chef_resource/resource/resource_log.rb +197 -0
  27. data/files/lib/chef_resource/resource/resource_type.rb +74 -0
  28. data/files/lib/chef_resource/resource/struct_property.rb +39 -0
  29. data/files/lib/chef_resource/resource/struct_property_type.rb +185 -0
  30. data/files/lib/chef_resource/resource/struct_resource.rb +410 -0
  31. data/files/lib/chef_resource/resource/struct_resource_base.rb +11 -0
  32. data/files/lib/chef_resource/resource/struct_resource_type.rb +275 -0
  33. data/files/lib/chef_resource/simple_struct.rb +121 -0
  34. data/files/lib/chef_resource/type.rb +371 -0
  35. data/files/lib/chef_resource/types.rb +4 -0
  36. data/files/lib/chef_resource/types/boolean.rb +16 -0
  37. data/files/lib/chef_resource/types/byte_size.rb +10 -0
  38. data/files/lib/chef_resource/types/date_time_type.rb +18 -0
  39. data/files/lib/chef_resource/types/date_type.rb +18 -0
  40. data/files/lib/chef_resource/types/float_type.rb +28 -0
  41. data/files/lib/chef_resource/types/integer_type.rb +53 -0
  42. data/files/lib/chef_resource/types/interval.rb +21 -0
  43. data/files/lib/chef_resource/types/path.rb +39 -0
  44. data/files/lib/chef_resource/types/pathname_type.rb +34 -0
  45. data/files/lib/chef_resource/types/string_type.rb +16 -0
  46. data/files/lib/chef_resource/types/symbol_type.rb +18 -0
  47. data/files/lib/chef_resource/types/uri_type.rb +37 -0
  48. data/files/lib/chef_resource/version.rb +3 -0
  49. data/spec/integration/chef.rb +81 -0
  50. data/spec/integration/struct_spec.rb +611 -0
  51. data/spec/integration/struct_state_spec.rb +538 -0
  52. data/spec/integration/type_spec.rb +1123 -0
  53. data/spec/integration/validation_spec.rb +207 -0
  54. data/spec/support/spec_support.rb +7 -0
  55. metadata +167 -0
@@ -0,0 +1,8 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'stove/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ Stove::RakeTask.new
7
+
8
+ task default: :spec
@@ -0,0 +1,24 @@
1
+ require 'chef_resource/version'
2
+ require 'chef_resource/constants'
3
+
4
+ module ChefResource
5
+ #
6
+ # Print a list of values with a conjunction like 'and' or 'or'
7
+ #
8
+ # @param conjunction [String] A word like 'and' or 'or'
9
+ # @param separator [String] A separator like ', ' or '; '
10
+ # @param *values A list of values to join with `to_s`
11
+ # @return the list, joined with the separator
12
+ # @example
13
+ # english_list(1, 2, 3, 4) #=> "1, 2, 3 and 4"
14
+ def self.english_list(*values, conjunction: 'and', separator: ', ')
15
+ case values.size
16
+ when 0
17
+ nil
18
+ when 1
19
+ values[0].to_s
20
+ else
21
+ "#{values[0..-2].join(separator)} #{conjunction} #{values[-1]}"
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+ module ChefResource
2
+ module CamelCase
3
+ # foo_bar/baz_bonk -> FooBar::BazBonk
4
+ def self.from_snake_case(snake_case)
5
+ snake_case.to_s.split('/').map do |str|
6
+ str.split('_').map { |s| s.capitalize }.join('')
7
+ end.join('::')
8
+ end
9
+
10
+ # FooBar::BazBonk -> foo_bar/baz_bonk
11
+ if RUBY_VERSION.to_f >= 2
12
+ UPPERCASE_SPLIT = Regexp.new('(?=\p{Lu})')
13
+ else
14
+ UPPERCASE_SPLIT = Regexp.new('(?=[A-Z])')
15
+ end
16
+
17
+ def self.to_snake_case(camel_case)
18
+ camel_case.to_s.split('::').map do |str|
19
+ str.split(UPPERCASE_SPLIT).map { |s| s.downcase! }.join('_')
20
+ end.join('/')
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,102 @@
1
+ #
2
+ # This file includes all the monkeypatches to Chef. (They are fairly minimal,
3
+ # and generally designed to only affect ChefResource cookbooks, as well as to allow
4
+ # other cookbooks to use resources defined in ChefResource cookbooks.)
5
+ #
6
+
7
+ #
8
+ # Redefine Chef's DelayedEvaluator (same basic def, with added goodies
9
+ # for cache and instance_eval)
10
+ #
11
+
12
+ require 'chef_resource/lazy_proc'
13
+ require 'chef/mixin/params_validate' # for DelayedEvaluator
14
+
15
+ Chef.send(:remove_const, :DelayedEvaluator)
16
+ Chef.const_set(:DelayedEvaluator, ChefResource::LazyProc)
17
+
18
+ #
19
+ # Add "ChefResource.define", "Chef.resource" and "ChefResource.defaults"
20
+ #
21
+
22
+ require 'chef_resource/chef_dsl/resource_definition_dsl'
23
+
24
+ class Chef
25
+ extend ChefResource::ChefDSL::ResourceDefinitionDSL
26
+ end
27
+
28
+ #
29
+ # Add "define" "resource" and "defaults" directly to ChefResource recipes/ and resource DSLs
30
+ #
31
+
32
+ require 'chef_resource/chef_dsl/chef_recipe'
33
+ require 'chef/recipe'
34
+
35
+ class Chef
36
+ class Recipe
37
+ def self.new(cookbook_name, recipe_name, run_context)
38
+ if run_context
39
+ cookbook = run_context.cookbook_collection[cookbook_name]
40
+ if cookbook.metadata.dependencies.has_key?('resource') && !(self <= ChefResource::ChefDSL::ChefRecipe)
41
+ return ChefResource::ChefDSL::ChefRecipe.new(cookbook_name, recipe_name, run_context)
42
+ end
43
+ end
44
+ super
45
+ end
46
+ end
47
+ end
48
+
49
+ #
50
+ # Turn "resources/" into ChefResource resources in ChefResource cookbooks
51
+ #
52
+
53
+ require 'chef/run_context/cookbook_compiler'
54
+ require 'chef_resource/chef_dsl/chef_cookbook_compiler'
55
+
56
+ class Chef
57
+ class RunContext
58
+ class CookbookCompiler
59
+ prepend ChefResource::ChefDSL::ChefCookbookCompiler
60
+ end
61
+ end
62
+ end
63
+
64
+ #
65
+ # Support new-style resources in Chef recipes by adding a method to update the
66
+ # list of definitions when resources are defined
67
+ #
68
+
69
+ require 'chef_resource/chef_dsl/resource_container_module'
70
+ require 'chef/dsl/recipe'
71
+ require 'chef/resource'
72
+
73
+ class Chef
74
+ class Resource
75
+ extend ChefResource::ChefDSL::ResourceContainerModule
76
+ def self.recipe_dsl_module
77
+ Chef::DSL::Recipe
78
+ end
79
+ end
80
+ end
81
+
82
+
83
+ #
84
+ # Build ChefResource resources all special-like
85
+ #
86
+
87
+ require 'chef_resource/chef_dsl/chef_recipe_dsl_extensions'
88
+ require 'chef/dsl/recipe'
89
+ require 'chef/recipe'
90
+ require 'chef/resource/lwrp_base'
91
+
92
+ class Chef
93
+ module DSL::Recipe
94
+ prepend ChefResource::ChefDSL::ChefRecipeDSLExtensions
95
+ end
96
+ class Recipe
97
+ prepend ChefResource::ChefDSL::ChefRecipeDSLExtensions
98
+ end
99
+ class Resource::LWRPBase
100
+ prepend ChefResource::ChefDSL::ChefRecipeDSLExtensions
101
+ end
102
+ end
@@ -0,0 +1,44 @@
1
+ require 'chef/resource'
2
+ require 'chef/log'
3
+ require 'chef/mixin/convert_to_class_name'
4
+ require 'chef_resource/chef'
5
+
6
+ module ChefResource
7
+ module ChefDSL
8
+ module ChefCookbookCompiler
9
+ def depends_on_resource_cookbook?(cookbook_name)
10
+ cookbook_collection[cookbook_name].metadata.dependencies.has_key?('resource')
11
+ end
12
+
13
+ # After compiling libraries, we update the resource collection
14
+ def compile_libraries
15
+ super
16
+ Chef::Resource.update_resource_definition_methods!
17
+ end
18
+
19
+ def compile_lwrps
20
+ super
21
+ Chef::Resource.update_resource_definition_methods!
22
+ end
23
+
24
+ def load_lwrp_resource(cookbook_name, filename)
25
+ if depends_on_resource_cookbook?(cookbook_name)
26
+ begin
27
+ Chef::Log.debug("Loading cookbook #{cookbook_name}'s resources from #{filename}")
28
+ resource_name = Chef::Mixin::ConvertToClassName.filename_to_qualified_string(cookbook_name, filename)
29
+ resource_class = Chef.resource resource_name do
30
+ class_eval IO.read(filename), filename
31
+ end
32
+ Chef::Log.debug("Loaded contents of #{filename} into a resource named #{resource_name} defined in #{resource_class.name}")
33
+ @events.lwrp_file_loaded(filename)
34
+ rescue Exception => e
35
+ @events.lwrp_file_load_failed(filename, e)
36
+ raise
37
+ end
38
+ else
39
+ super
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,10 @@
1
+ require 'chef/recipe'
2
+ require 'chef_resource/chef_dsl/resource_definition_dsl'
3
+
4
+ module ChefResource
5
+ module ChefDSL
6
+ class ChefRecipe < Chef::Recipe
7
+ include ChefResource::ChefDSL::ResourceDefinitionDSL
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,84 @@
1
+ require 'chef_resource/resource'
2
+
3
+ module ChefResource
4
+ module ChefDSL
5
+ module ChefRecipeDSLExtensions
6
+ #
7
+ # Allow ChefResource resources to be declared with a different syntax:
8
+ #
9
+ # declare_resource(:resource, ..., created_at)
10
+ # declare_resource(ResourceClass, ..., created_at)
11
+ #
12
+ # Which translates to open() in the end.
13
+ #
14
+ def declare_resource(type, *identity, created_at, &update_block)
15
+ resource_class = resource_class_for(type)
16
+
17
+ # Handle normal resources
18
+ if !resource_class.is_a?(ChefResource::Resource)
19
+ case identity.size
20
+ when 0
21
+ name, created_at = created_at, caller[0]
22
+ when 1
23
+ name = identity[0]
24
+ else
25
+ raise ArgumentError, "wrong number of arguments (#{identity.size+1} for 1..2)"
26
+ end
27
+ return super(type, name, created_at, &update_block)
28
+ end
29
+
30
+ # ChefResource resources!
31
+ resource = buildbuild_resource_v2(resource_class, *identity, created_at, &update_block)
32
+ run_context.resource_collection.insert(resource,
33
+ resource_type: resource_class.dsl_name,
34
+ instance_name: resource.resource_identity_string)
35
+ end
36
+
37
+ def build_resource(type, *identity, created_at, &update_block)
38
+ resource_class = resource_class_for(type)
39
+ if !resource_class.is_a?(ChefResource::Resource)
40
+ # Handle normal resources
41
+ case identity.size
42
+ when 0
43
+ name, created_at = created_at, caller[0]
44
+ when 1
45
+ name = identity[0]
46
+ else
47
+ raise ArgumentError, "wrong number of arguments (#{identity.size+1} for 1..2)"
48
+ end
49
+ return super(type, name, created_at, &update_block)
50
+ end
51
+
52
+ # ChefResource!
53
+ buildbuild_resource_v2(resource_class, *identity, created_at, &update_block)
54
+ end
55
+
56
+ def buildbuild_resource_v2(resource_class, *identity, created_at, &update_block)
57
+ resource = resource_class.open(*identity)
58
+ resource.run_context = run_context
59
+ resource.cookbook_name = cookbook_name
60
+ resource.recipe_name = recipe_name
61
+ resource.source_line = created_at
62
+ resource.declared_type = resource_class.dsl_name
63
+ # Determine whether this resource is being created in the context of an enclosing Provider
64
+ resource.enclosing_provider = self.is_a?(Chef::Provider) ? self : nil
65
+ resource.params = @params
66
+
67
+ # Evaluate resource attribute DSL
68
+ resource.instance_eval(&update_block) if block_given?
69
+
70
+ # Freeze resource
71
+ resource.resource_fully_defined
72
+
73
+ # Run optional resource hook
74
+ resource.after_created
75
+
76
+ resource
77
+ end
78
+
79
+ def resource_class_for(type)
80
+ type.is_a?(Class) ? type : super
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,12 @@
1
+ require 'chef/resource'
2
+ require 'chef_resource/chef_dsl/chef_resource_extensions'
3
+ require 'chef_resource/chef_dsl/chef_resource_class_extensions'
4
+
5
+ module ChefResource
6
+ module ChefDSL
7
+ class ChefResourceBase < Chef::Resource
8
+ include ChefResourceExtensions
9
+ extend ChefResourceClassExtensions
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ require 'chef_resource/resource/struct_resource'
2
+ require 'chef_resource/resource/struct_resource_type'
3
+ require 'chef_resource/camel_case'
4
+ require 'chef/dsl/recipe'
5
+ require 'chef/resource'
6
+
7
+ module ChefResource
8
+ module ChefDSL
9
+ #
10
+ # Define properties on the type itself
11
+ #
12
+ module ChefResourceClassExtensions
13
+ # We are a StructResource (so that we can use "property" for properties
14
+ # of the ChefResource class itself) and a StructResourceType (because
15
+ # Chef Resources are traditionally structs).
16
+ include ChefResource::Resource::StructResource
17
+ include ChefResource::Resource::StructResourceType
18
+ extend ChefResource::Resource::StructResourceType
19
+
20
+ #
21
+ # recipe do
22
+ # ...
23
+ # end
24
+ #
25
+ def recipe(&recipe_block)
26
+ define_method(:update_resource, &recipe_block)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,224 @@
1
+ require 'chef/resource_collection'
2
+ require 'chef/runner'
3
+ require 'chef/dsl/recipe'
4
+ require 'chef_resource/resource/struct_resource'
5
+ require 'chef_resource/chef_dsl/chef_resource_log'
6
+ require 'chef_resource/chef_dsl/chef_recipe_dsl_extensions'
7
+ require 'chef_resource/types'
8
+
9
+ module ChefResource
10
+ module ChefDSL
11
+ module ChefResourceExtensions
12
+ include Chef::DSL::Recipe
13
+ include ChefResource::ChefDSL::ChefRecipeDSLExtensions
14
+ include ChefResource::Resource::StructResource
15
+
16
+ #
17
+ # We don't set name, and we expect our run_context to be set elsewhere.
18
+ #
19
+ def initialize
20
+ super(nil, nil)
21
+ end
22
+
23
+ #
24
+ # Returns a super-friendly string showing this struct.
25
+ #
26
+ # @param only [Symbol] Which values to include. Default: `:only_known`. One of:
27
+ # - :only_known :: Values explicitly set by the user or current values.
28
+ # If the current value has not been loaded, this will NOT load it or
29
+ # show any of those values.
30
+ # - :only_changed :: Values which the user has set and which have
31
+ # actually changed from their current or default value.
32
+ # - :only_explicit :: Values explicitly set by the user.
33
+ # - :all :: All values, including default values.
34
+ #
35
+ def to_text(only=:only_known)
36
+ # return "suppressed sensitive resource output" if sensitive
37
+ text = self.class.dsl_name + "(\"#{name}\") do\n"
38
+ text << " # Declared in #{@source_line}\n"
39
+ results = to_h(only)
40
+ results_width = results.keys.map { |k| k.size }.max
41
+ results.each do |name, value|
42
+ text << " #{name.to_s.ljust(results_width)} "
43
+ if value.respond_to?(:to_text)
44
+ lines = value.to_text.lines
45
+ text << lines.map do |line|
46
+ "#{line}#{line.end_with?("\n") ? "" : "\n"}"
47
+ end.join(" ")
48
+ else
49
+ text << "#{value.inspect}\n"
50
+ end
51
+ end
52
+ [@not_if, @only_if].flatten.each do |conditional|
53
+ text << " #{conditional.to_text}\n"
54
+ end
55
+ text << "end\n"
56
+ end
57
+
58
+ #
59
+ # Returns this struct as a string with <resource_name name: value name: value name
60
+ #
61
+ # @param only [Symbol] Which values to include. Default: `:only_known`. One of:
62
+ # - :only_known :: Values explicitly set by the user or current values.
63
+ # If the current value has not been loaded, this will NOT load it or
64
+ # show any of those values.
65
+ # - :only_changed :: Values which the user has set and which have
66
+ # actually changed from their current or default value.
67
+ # - :only_explicit :: Values explicitly set by the user.
68
+ # - :all :: All values, including default values.
69
+ #
70
+ def inspect(only=:only_known)
71
+ to_h(only).inject("<#{to_s}") do |str, (name, value)|
72
+ str << " #{name}: #{value.inspect}"
73
+ end << ">"
74
+ end
75
+
76
+ #
77
+ # No such thing as a provider, yo. (Also not supporting
78
+ # multiple actions yet.)
79
+ #
80
+ def provider_for_action(action)
81
+ self
82
+ end
83
+
84
+ #
85
+ # Mimic Chef::Provider.run_action
86
+ #
87
+ # This is where we do the work to get rid of Providers!
88
+ #
89
+ # NOTE: both Chef::Resource and Chef::Provider have a
90
+ # run_action. In fact, they can both technically take a single-argument
91
+ # form. However, in practice in Chef, Chef::Provider.run_action() is the
92
+ # only method ever called. So we say that if we are passed multiple args,
93
+ # it is the Resource version ("super") and if we are passed only one arg,
94
+ # it is the Provider version.
95
+ #
96
+ def run_action(*args)
97
+ if args.size > 0
98
+ return super
99
+ end
100
+
101
+ # Call update.
102
+ log.update_started
103
+ begin
104
+
105
+ # Enable update to run its own resources, inline.
106
+
107
+ # Executes the given block in a temporary run_context with its own
108
+ # resource collection. After the block is executed, any resources
109
+ # declared inside are converged, and if any are updated, the
110
+ # new_resource will be marked updated.
111
+ saved_run_context = @run_context
112
+ temp_run_context = @run_context.dup
113
+ @run_context = temp_run_context
114
+ @run_context.resource_collection = Chef::ResourceCollection.new
115
+
116
+ update_resource
117
+
118
+ Chef::Runner.new(@run_context).converge
119
+ rescue
120
+ log.update_failed($!)
121
+ raise
122
+ ensure
123
+ @run_context = saved_run_context
124
+ if temp_run_context.resource_collection.any? {|r| r.updated? }
125
+ updated_by_last_action(true)
126
+ end
127
+ end
128
+ log.update_succeeded
129
+ end
130
+
131
+ #
132
+ # This is how we support inline resources that can access their parent
133
+ # resource's data: enclosing_provider is how Chef delegates to our scope.
134
+ #
135
+ def build_resource(*args, &block)
136
+ parent = self
137
+ super(*args) do
138
+ self.enclosing_provider = parent
139
+ instance_eval(&block) if block
140
+ end
141
+ end
142
+
143
+ #
144
+ # The name of this resource type. Just calls self.class.resource_name.
145
+ #
146
+ def resource_name
147
+ self.class.dsl_name
148
+ end
149
+
150
+ #
151
+ # We only support one action, presently. Hardcode it.
152
+ #
153
+ def action
154
+ [ :update ]
155
+ end
156
+
157
+ #
158
+ # Subtle difference from ChefResource::Resource::StructResource.reopen_resource:
159
+ # this method passes the name and run_context into the resource constructor
160
+ # (since Resource requires that).
161
+ #
162
+ def reopen_resource
163
+ resource = super
164
+ resource.run_context = run_context
165
+ resource.cookbook_name = cookbook_name
166
+ resource.recipe_name = recipe_name
167
+ resource.source_line = source_line
168
+ resource.declared_type = declared_type
169
+ resource.enclosing_provider = enclosing_provider
170
+ resource.params = params
171
+ resource
172
+ end
173
+
174
+ def to_s
175
+ "#{self.class.dsl_name}[#{resource_identity_string}]"
176
+ end
177
+
178
+ #
179
+ # For our short_name, we just grab #to_s, which outputs the right notification
180
+ # syntax.
181
+ #
182
+ def resource_short_name
183
+ to_s
184
+ end
185
+
186
+ #
187
+ # Log to Chef!
188
+ #
189
+ def log(*args)
190
+ @resource_log ||= ChefResourceLog.new(self)
191
+ super
192
+ end
193
+
194
+ #
195
+ # Make damn sure we're not doing the whole silly cloning thing
196
+ #
197
+ def load_from(*args)
198
+ raise NotImplementedError, "load_from is not implemented on ChefResource resources."
199
+ end
200
+
201
+ #
202
+ # Take an action that will update the resource.
203
+ #
204
+ # @param description [String] The action being taken.
205
+ # @yield A block that will perform the actual update.
206
+ # @raise Any error raised by the block is passed through.
207
+ #
208
+ def take_action(description, &action_block)
209
+ if Chef::Config[:why_run]
210
+ log.action_skipped(description)
211
+ else
212
+ log.action_started(description)
213
+ begin
214
+ instance_eval(&action_block)
215
+ rescue
216
+ log.action_failed($!)
217
+ raise
218
+ end
219
+ log.action_succeeded
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end