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,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