chef-resource 0.2.2

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