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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +23 -0
- data/LICENSE +201 -0
- data/README.md +264 -0
- data/Rakefile +8 -0
- data/files/lib/chef_resource.rb +24 -0
- data/files/lib/chef_resource/camel_case.rb +23 -0
- data/files/lib/chef_resource/chef.rb +102 -0
- data/files/lib/chef_resource/chef_dsl/chef_cookbook_compiler.rb +44 -0
- data/files/lib/chef_resource/chef_dsl/chef_recipe.rb +10 -0
- data/files/lib/chef_resource/chef_dsl/chef_recipe_dsl_extensions.rb +84 -0
- data/files/lib/chef_resource/chef_dsl/chef_resource_base.rb +12 -0
- data/files/lib/chef_resource/chef_dsl/chef_resource_class_extensions.rb +30 -0
- data/files/lib/chef_resource/chef_dsl/chef_resource_extensions.rb +224 -0
- data/files/lib/chef_resource/chef_dsl/chef_resource_log.rb +54 -0
- data/files/lib/chef_resource/chef_dsl/resource_container_module.rb +80 -0
- data/files/lib/chef_resource/chef_dsl/resource_definition_dsl.rb +128 -0
- data/files/lib/chef_resource/constants.rb +8 -0
- data/files/lib/chef_resource/errors.rb +31 -0
- data/files/lib/chef_resource/lazy_proc.rb +82 -0
- data/files/lib/chef_resource/output/nested_converge.rb +91 -0
- data/files/lib/chef_resource/output/nested_converge/open_resource.rb +113 -0
- data/files/lib/chef_resource/output/region_stream.rb +145 -0
- data/files/lib/chef_resource/output/simple_output.rb +83 -0
- data/files/lib/chef_resource/resource.rb +428 -0
- data/files/lib/chef_resource/resource/resource_log.rb +197 -0
- data/files/lib/chef_resource/resource/resource_type.rb +74 -0
- data/files/lib/chef_resource/resource/struct_property.rb +39 -0
- data/files/lib/chef_resource/resource/struct_property_type.rb +185 -0
- data/files/lib/chef_resource/resource/struct_resource.rb +410 -0
- data/files/lib/chef_resource/resource/struct_resource_base.rb +11 -0
- data/files/lib/chef_resource/resource/struct_resource_type.rb +275 -0
- data/files/lib/chef_resource/simple_struct.rb +121 -0
- data/files/lib/chef_resource/type.rb +371 -0
- data/files/lib/chef_resource/types.rb +4 -0
- data/files/lib/chef_resource/types/boolean.rb +16 -0
- data/files/lib/chef_resource/types/byte_size.rb +10 -0
- data/files/lib/chef_resource/types/date_time_type.rb +18 -0
- data/files/lib/chef_resource/types/date_type.rb +18 -0
- data/files/lib/chef_resource/types/float_type.rb +28 -0
- data/files/lib/chef_resource/types/integer_type.rb +53 -0
- data/files/lib/chef_resource/types/interval.rb +21 -0
- data/files/lib/chef_resource/types/path.rb +39 -0
- data/files/lib/chef_resource/types/pathname_type.rb +34 -0
- data/files/lib/chef_resource/types/string_type.rb +16 -0
- data/files/lib/chef_resource/types/symbol_type.rb +18 -0
- data/files/lib/chef_resource/types/uri_type.rb +37 -0
- data/files/lib/chef_resource/version.rb +3 -0
- data/spec/integration/chef.rb +81 -0
- data/spec/integration/struct_spec.rb +611 -0
- data/spec/integration/struct_state_spec.rb +538 -0
- data/spec/integration/type_spec.rb +1123 -0
- data/spec/integration/validation_spec.rb +207 -0
- data/spec/support/spec_support.rb +7 -0
- 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
|