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,197 @@
1
+ module ChefResource
2
+ module Resource
3
+ class ResourceLog
4
+ def initialize(resource)
5
+ @resource = resource
6
+ end
7
+ attr_reader :resource
8
+
9
+ # Keep track of descriptions of the current load/update/action
10
+ attr_accessor :current_load
11
+ attr_accessor :current_update
12
+ def current_load_values
13
+ @current_load_values ||= {}
14
+ end
15
+ def current_action_stack
16
+ @current_action_stack ||= []
17
+ end
18
+
19
+ def debug(str)
20
+ log(:debug, str)
21
+ end
22
+ def info(str)
23
+ log(:info, str)
24
+ end
25
+ def warn(str)
26
+ log(:warn, str)
27
+ end
28
+ def error(str)
29
+ log(:error, str)
30
+ end
31
+ def fatal(str)
32
+ log(:fatal, str)
33
+ end
34
+ def log(level, str)
35
+ end
36
+
37
+ #
38
+ # Resource phases
39
+ #
40
+
41
+ #
42
+ # Fired when the resource is created (but identity is not yet defined)
43
+ #
44
+ def created
45
+ end
46
+
47
+ #
48
+ # Fired when the resource identity is defined
49
+ #
50
+ def identity_defined
51
+ end
52
+
53
+ #
54
+ # Fired when the resource desired state is fully defined
55
+ #
56
+ def fully_defined
57
+ end
58
+
59
+ #
60
+ # Resource.load and load_value
61
+ #
62
+
63
+ #
64
+ # Fired when Resource.load starts
65
+ #
66
+ def load_started
67
+ self.current_load = true
68
+ end
69
+
70
+ #
71
+ # Fired when Resource.load succeeds
72
+ #
73
+ # @param exists if the resource exists, false if it doesn't
74
+ #
75
+ def load_succeeded(exists: true)
76
+ raise "load succeeded when load was never started!" if !current_load
77
+ self.current_load = false
78
+ end
79
+
80
+ #
81
+ # Fired when Resource.load fails.
82
+ #
83
+ # @param error The error that was raised.
84
+ #
85
+ def load_failed(error)
86
+ raise "load failed when load was never started!" if !current_load
87
+ self.current_load = false
88
+ end
89
+
90
+ #
91
+ # Fired when load_value for a given property starts
92
+ #
93
+ def load_value_started(name)
94
+ raise "load_value(#{name}) started twice!" if current_load_values[name]
95
+ self.current_load_values[name] = true
96
+ end
97
+
98
+ #
99
+ # Fired when load_value for a given property succeeds
100
+ #
101
+ # @param name The name of the property
102
+ #
103
+ def load_value_succeeded(name)
104
+ raise "load_value(name) succeeded but was never started!" if !current_load_values[name]
105
+ current_load_values.delete(name)
106
+ end
107
+
108
+ #
109
+ # Fired when Resource.load_value for a given property fails
110
+ #
111
+ # @param name The name of the property
112
+ # @param error The error that was raised
113
+ #
114
+ def load_value_failed(name, error)
115
+ raise "load_value(name) failed but was never started!" if !current_load_values[name]
116
+ current_load_values.delete(name)
117
+ end
118
+
119
+ #
120
+ # Resource.update and actions
121
+ #
122
+
123
+ #
124
+ # Fired when update starts.
125
+ #
126
+ def update_started
127
+ raise "update started twice!" if current_update
128
+ self.current_update = true
129
+ end
130
+
131
+ #
132
+ # Fired when the update succeeds.
133
+ #
134
+ def update_succeeded
135
+ raise "update succeeded, but was never started!" if !current_update
136
+ raise "update succeeded when actions are still running!" if !current_action_stack.empty?
137
+ self.current_update = nil
138
+ end
139
+
140
+ #
141
+ # Fired when the update fails.
142
+ #
143
+ # @param error [String] The error that was raised
144
+ #
145
+ def update_failed(error)
146
+ raise "update failed when no update is taking place!" if !current_update
147
+ self.current_action_stack.clear
148
+ self.current_update = nil
149
+ end
150
+
151
+ #
152
+ # Fired when an action is skipped due to why-run.
153
+ #
154
+ # @param description [String] A description of the action being taken.
155
+ #
156
+ def action_skipped(description, update_guaranteed: true)
157
+ raise "action started when no update is taking place!" if !current_update
158
+ end
159
+
160
+ #
161
+ # Fired when an action starts.
162
+ #
163
+ # @param description [String] A description of the action being taken.
164
+ # @param update_guaranteed [Boolean] Whether an update is guaranteed on success or not.
165
+ #
166
+ def action_started(description, update_guaranteed: true)
167
+ raise "action started when no update is taking place!" if !current_update
168
+ current_action_stack.push [ description, update_guaranteed ]
169
+ end
170
+
171
+ #
172
+ # Fired when an action succeeds.
173
+ #
174
+ # @param description [String] A more detailed description of what the action did.
175
+ # @param updated [Boolean] Whether anything was actually updated.
176
+ # @return [String, Boolean] The action that succeeded (a pair of [ description, updated ])
177
+ #
178
+ def action_succeeded(description: nil, updated: true)
179
+ raise "action succeeded when no action was started" if current_action_stack.empty?
180
+ original_description, _ = current_action_stack.pop
181
+ [ description || original_description, updated ]
182
+ end
183
+
184
+ #
185
+ # Fired when an action fails.
186
+ #
187
+ # @param error [String] The error that was raised.
188
+ # @return [String] The description of the action that failed.
189
+ #
190
+ def action_failed(error)
191
+ raise "action failed when no action was started" if current_action_stack.empty?
192
+ original_description, _ = current_action_stack.pop
193
+ original_description
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,74 @@
1
+ require 'chef_resource/type'
2
+
3
+ module ChefResource
4
+ module Resource
5
+ #
6
+ # The methods on a Resource class. Generally extended into the class, and
7
+ # become class-level methods (Class.default(x))
8
+ #
9
+ module ResourceType
10
+ include Type
11
+
12
+ #
13
+ # Opens a resource for reading and updating.
14
+ #
15
+ # This method should be passed enough data to uniquely identify the
16
+ # resource so it can be retrieved.
17
+ #
18
+ # The resulting object is generally *lazily* loaded, and may not hit the
19
+ # network or file until you read a value. This allows you to update
20
+ # values in some cases without incurring the cost of a read.
21
+ #
22
+ # @return An open Resource which:
23
+ # - Is a Resource
24
+ # - Is a Resource even if the actual value does not exist
25
+ # - Can be modified for update
26
+ # - `type.is_valid?(resource, resource)` is true
27
+ #
28
+ def open(&define_identity_block)
29
+ resource = new
30
+ resource.instance_eval(&define_identity_block)
31
+ resource.resource_identity_defined if resource.resource_state == :created
32
+ resource
33
+ end
34
+
35
+ #
36
+ # Gets the current value of a resource (immediately). This method must
37
+ # be passed enough data to uniquely identify the resource so it can be
38
+ # retrieved.
39
+ #
40
+ # The default implementation (ResourceType.get) calls open(*args) { load }
41
+ #
42
+ # @return A readonly resource value with values filled in; or nil if the
43
+ # resource does not exist.
44
+ #
45
+ def get(*args, &define_identity_block)
46
+ resource = open(*args) do
47
+ instance_eval(&define_identity_block) if define_identity_block
48
+ load
49
+ end
50
+ resource.resource_fully_defined
51
+ resource
52
+ end
53
+
54
+ #
55
+ # Updates a resource. By default, opens the resource, evaluates the
56
+ # block, and calls update().
57
+ #
58
+ # @param open_args Arguments to pass to open()
59
+ # @param update_block The block to run instance_eval on.
60
+ #
61
+ # @example
62
+ # FileResource.update('/x/y.txt') do
63
+ # mode 0777
64
+ # end
65
+ #
66
+ def update(*open_args, &update_block)
67
+ resource = open(*args)
68
+ resource.instance_eval(&update_block)
69
+ resource.resource_fully_defined
70
+ resource.update_resource
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,39 @@
1
+ require 'chef_resource/simple_struct'
2
+
3
+ module ChefResource
4
+ module Resource
5
+ #
6
+ # Struct properties all create a class for each property. This is
7
+ # included in that class, and StructPropertyType is extended in that class.
8
+ #
9
+ module StructProperty
10
+ #
11
+ # The struct containing this property.
12
+ #
13
+ extend SimpleStruct
14
+ property :parent_struct
15
+
16
+ #
17
+ # The actual value defaults to parent.current_resource.attr_name. If
18
+ # parent.current_resource is `nil`, current_resource defaults to `nil`.
19
+ #
20
+ def current_resource
21
+ actual_struct = parent_struct.current_resource
22
+ actual_struct.public_send(self.class.property_name) if actual_struct
23
+ end
24
+
25
+ #
26
+ # This property exists if its parent does.
27
+ #
28
+ def resource_exists?
29
+ parent_struct.resource_exists?
30
+ end
31
+
32
+ protected
33
+
34
+ def initialize(parent_struct)
35
+ self.parent_struct = parent_struct
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,185 @@
1
+ require 'chef_resource/type'
2
+ require 'chef_resource/simple_struct'
3
+
4
+ module ChefResource
5
+ module Resource
6
+ #
7
+ # Struct properties all create a class for each property. This is
8
+ # included in that class, and StructPropertyType is extended in that class.
9
+ #
10
+ module StructPropertyType
11
+ include Type
12
+
13
+ extend SimpleStruct
14
+
15
+ #
16
+ # The name of this property
17
+ #
18
+ property :property_name
19
+
20
+ #
21
+ # The type of this property
22
+ #
23
+ property :property_type
24
+
25
+ #
26
+ # The parent type (struct type) of this property
27
+ #
28
+ property :property_parent_type
29
+
30
+ #
31
+ # True if this is an identity property.
32
+ #
33
+ boolean_property :identity
34
+
35
+ #
36
+ # True if this property is required.
37
+ #
38
+ # Required identity properties can be specified positionally in `open`,
39
+ # like so: `FileResource.open('/x/y.txt')` is equivalent to
40
+ # `FileResource.open(path: '/x/y.txt')`
41
+ #
42
+ # By default, this is false for most properties, but true for identity
43
+ # properties that have no default set.
44
+ #
45
+ boolean_property :required, default: "identity? && !defined?(@default)"
46
+
47
+ #
48
+ # A block which loads the property from reality.
49
+ #
50
+ # @param value The load proc. If this is a LazyProc, the block will
51
+ # be run in the context of the struct (`struct.instance_eval`) unless
52
+ # the block is explicitly set to `should_instance_eval: false`. If it is not,
53
+ # it will always be instance_eval'd.
54
+ #
55
+ block_property :load_value
56
+
57
+ #
58
+ # Return a value of this type by coercion or construction.
59
+ #
60
+ # @param args The value to coerce or the values to construct with.
61
+ # @return A value of this Type.
62
+ #
63
+ def coerce(parent, *args)
64
+ property_type.is_a?(ResourceType) ? property_type.coerce(*args) : super
65
+ end
66
+
67
+ #
68
+ # Emit property methods into the struct class (property_parent_type)
69
+ #
70
+ def emit_property_methods
71
+ name = property_name
72
+ class_name = CamelCase.from_snake_case(name)
73
+ property_parent_type.class_eval <<-EOM, __FILE__, __LINE__+1
74
+ def #{name}(*args)
75
+ if args.empty?
76
+ #{class_name}.get_property(self)
77
+ else
78
+ #{class_name}.set_property(self, *args)
79
+ end
80
+ end
81
+ EOM
82
+
83
+ property_parent_type.class_eval <<-EOM, __FILE__, __LINE__+1
84
+ def #{name}=(value)
85
+ #{class_name}.set_property(self, value)
86
+ end
87
+ EOM
88
+ end
89
+
90
+ #
91
+ # Set the property value.
92
+ #
93
+ def set_property(struct, *args)
94
+ if identity?
95
+ if struct.resource_state && struct.resource_state != :created
96
+ raise PropertyDefinedError.new("Cannot modify identity property #{property_name} of #{struct.class}: identity properties cannot be modified after the resource's identity is defined. (State: #{struct.resource_state})", struct, self)
97
+ end
98
+ else
99
+ if struct.resource_state && struct.resource_state != :created && struct.resource_state != :identity_defined
100
+ raise PropertyDefinedError.new("Cannot modify property #{property_name} of #{struct.class}: identity properties cannot be modified after the resource's identity is defined. (State: #{struct.resource_state})", struct, self)
101
+ end
102
+ end
103
+ if args.size == 1 && args[0].is_a?(ChefResource::LazyProc)
104
+ struct.explicit_property_values[property_name] = args[0]
105
+ else
106
+ struct.explicit_property_values[property_name] = coerce(struct, *args)
107
+ end
108
+ end
109
+
110
+ #
111
+ # Get the property value from the struct.
112
+ #
113
+ # First tries to get the desired value. If there is none, tries to get
114
+ # the actual value from the struct. If the actual value doesn't have
115
+ # the value, it looks for a load_value method. Finally, if that isn't
116
+ # there, it runs default.
117
+ #
118
+ def get_property(struct)
119
+ value = struct.explicit_property_values.fetch(property_name) do
120
+ return current_property_value(struct)
121
+ end
122
+ coerce_to_user(struct, value)
123
+ end
124
+
125
+ #
126
+ # Get the current property value from the struct (i.e. the value not
127
+ # counting anything the user has actually set--including actual value
128
+ # and default value).
129
+ #
130
+ # @param struct The struct we're loading from
131
+ #
132
+ def current_property_value(struct)
133
+ # Try to grab a known (non-default) value from current_resource
134
+ has_value, value = explicit_current_property_value(struct)
135
+ if !has_value
136
+ value = default(parent: struct)
137
+ end
138
+ coerce_to_user(struct, value)
139
+ end
140
+
141
+ #
142
+ # Ensure the current value of the property is loaded and set.
143
+ #
144
+ # @param struct The struct we're loading from
145
+ # @return [Boolean, Value] `true, <value>` if the current_resource exists and has that explicit value, `false, nil` if not
146
+ # @raise Any error raised by load_value or load will pass through.
147
+ #
148
+ def explicit_current_property_value(struct)
149
+ # First get current_struct
150
+ current_struct = struct.is_current_resource? ? struct : struct.current_resource
151
+ if !current_struct
152
+ return [ false, nil ]
153
+ end
154
+
155
+ # First, check quickly if we already have it.
156
+ if current_struct.explicit_property_values.has_key?(property_name)
157
+ return [ true, current_struct.public_send(property_name) ]
158
+ end
159
+
160
+ if current_struct.resource_exists?
161
+ # Since we were already brought up, we must already be loaded, yet the
162
+ # property isn't there. Use load_value if it has it.
163
+ if !load_value
164
+ return [ false, nil ]
165
+ end
166
+
167
+ struct.log.load_value_started(property_name)
168
+
169
+ begin
170
+ value = load_value.get(instance: current_struct, instance_eval_by_default: true)
171
+ # Set the value (if it gets coerced, catch the result)
172
+ value = current_struct.public_send(property_name, value)
173
+ struct.log.load_value_succeeded(property_name)
174
+ return [ true, value ]
175
+ rescue
176
+ # short circuit this from happening again
177
+ current_struct.explicit_property_values[property_name] = nil
178
+ struct.log.load_value_failed(property_name, $!)
179
+ raise
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end