compat_resource 12.5.1
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 +1 -0
- data/Gemfile +6 -0
- data/LICENSE +201 -0
- data/README.md +23 -0
- data/Rakefile +191 -0
- data/files/lib/chef_compat.rb +5 -0
- data/files/lib/chef_compat/copied_from_chef.rb +2 -0
- data/files/lib/chef_compat/copied_from_chef/chef/constants.rb +32 -0
- data/files/lib/chef_compat/copied_from_chef/chef/delayed_evaluator.rb +26 -0
- data/files/lib/chef_compat/copied_from_chef/chef/dsl/recipe.rb +8 -0
- data/files/lib/chef_compat/copied_from_chef/chef/mixin/params_validate.rb +484 -0
- data/files/lib/chef_compat/copied_from_chef/chef/property.rb +572 -0
- data/files/lib/chef_compat/copied_from_chef/chef/provider.rb +101 -0
- data/files/lib/chef_compat/copied_from_chef/chef/resource.rb +283 -0
- data/files/lib/chef_compat/copied_from_chef/chef/resource/action_class.rb +97 -0
- data/files/lib/chef_compat/delegating_class.rb +10 -0
- data/files/lib/chef_compat/monkeypatches.rb +5 -0
- data/files/lib/chef_compat/monkeypatches/chef.rb +3 -0
- data/files/lib/chef_compat/monkeypatches/chef/exceptions.rb +7 -0
- data/files/lib/chef_compat/monkeypatches/chef/provider.rb +7 -0
- data/files/lib/chef_compat/monkeypatches/chef/resource.rb +24 -0
- data/files/lib/chef_compat/monkeypatches/chef/resource/lwrp_base.rb +61 -0
- data/files/lib/chef_compat/resource.rb +54 -0
- data/files/lib/chef_compat/version.rb +3 -0
- metadata +123 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'chef_compat/copied_from_chef'
|
2
|
+
module ChefCompat
|
3
|
+
module CopiedFromChef
|
4
|
+
class Chef < (defined?(::Chef) ? ::Chef : Object)
|
5
|
+
class Provider < (defined?(::Chef::Provider) ? ::Chef::Provider : Object)
|
6
|
+
def initialize(new_resource, run_context)
|
7
|
+
@new_resource = new_resource
|
8
|
+
@action = action
|
9
|
+
@current_resource = nil
|
10
|
+
@run_context = run_context
|
11
|
+
@converge_actions = nil
|
12
|
+
|
13
|
+
@recipe_name = nil
|
14
|
+
@cookbook_name = nil
|
15
|
+
self.class.include_resource_dsl_module(new_resource)
|
16
|
+
end
|
17
|
+
def converge_if_changed(*properties, &converge_block)
|
18
|
+
if !converge_block
|
19
|
+
raise ArgumentError, "converge_if_changed must be passed a block!"
|
20
|
+
end
|
21
|
+
|
22
|
+
properties = new_resource.class.state_properties.map { |p| p.name } if properties.empty?
|
23
|
+
properties = properties.map { |p| p.to_sym }
|
24
|
+
if current_resource
|
25
|
+
# Collect the list of modified properties
|
26
|
+
specified_properties = properties.select { |property| new_resource.property_is_set?(property) }
|
27
|
+
modified = specified_properties.select { |p| new_resource.send(p) != current_resource.send(p) }
|
28
|
+
if modified.empty?
|
29
|
+
Chef::Log.debug("Skipping update of #{new_resource.to_s}: has not changed any of the specified properties #{specified_properties.map { |p| "#{p}=#{new_resource.send(p).inspect}" }.join(", ")}.")
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
|
33
|
+
# Print the pretty green text and run the block
|
34
|
+
property_size = modified.map { |p| p.size }.max
|
35
|
+
modified = modified.map { |p| " set #{p.to_s.ljust(property_size)} to #{new_resource.send(p).inspect} (was #{current_resource.send(p).inspect})" }
|
36
|
+
converge_by([ "update #{current_resource.identity}" ] + modified, &converge_block)
|
37
|
+
|
38
|
+
else
|
39
|
+
# The resource doesn't exist. Mark that we are *creating* this, and
|
40
|
+
# write down any properties we are setting.
|
41
|
+
property_size = properties.map { |p| p.size }.max
|
42
|
+
created = []
|
43
|
+
properties.each do |property|
|
44
|
+
if new_resource.property_is_set?(property)
|
45
|
+
created << " set #{property.to_s.ljust(property_size)} to #{new_resource.send(property).inspect}"
|
46
|
+
else
|
47
|
+
created << " set #{property.to_s.ljust(property_size)} to #{new_resource.send(property).inspect} (default value)"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
converge_by([ "create #{new_resource.identity}" ] + created, &converge_block)
|
52
|
+
end
|
53
|
+
true
|
54
|
+
end
|
55
|
+
def self.include_resource_dsl(include_resource_dsl)
|
56
|
+
@include_resource_dsl = include_resource_dsl
|
57
|
+
end
|
58
|
+
def self.include_resource_dsl_module(resource)
|
59
|
+
if @include_resource_dsl && !defined?(@included_resource_dsl_module)
|
60
|
+
provider_class = self
|
61
|
+
@included_resource_dsl_module = Module.new do
|
62
|
+
extend Forwardable
|
63
|
+
define_singleton_method(:to_s) { "#{resource_class} forwarder module" }
|
64
|
+
define_singleton_method(:inspect) { to_s }
|
65
|
+
# Add a delegator for each explicit property that will get the *current* value
|
66
|
+
# of the property by default instead of the *actual* value.
|
67
|
+
resource.class.properties.each do |name, property|
|
68
|
+
class_eval(<<-EOM, __FILE__, __LINE__)
|
69
|
+
def #{name}(*args, &block)
|
70
|
+
# If no arguments were passed, we process "get" by defaulting
|
71
|
+
# the value to current_resource, not new_resource. This helps
|
72
|
+
# avoid issues where resources accidentally overwrite perfectly
|
73
|
+
# valid stuff with default values.
|
74
|
+
if args.empty? && !block
|
75
|
+
if !new_resource.property_is_set?(__method__) && current_resource
|
76
|
+
return current_resource.public_send(__method__)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
new_resource.public_send(__method__, *args, &block)
|
80
|
+
end
|
81
|
+
EOM
|
82
|
+
end
|
83
|
+
dsl_methods =
|
84
|
+
resource.class.public_instance_methods +
|
85
|
+
resource.class.protected_instance_methods -
|
86
|
+
provider_class.instance_methods -
|
87
|
+
resource.class.properties.keys
|
88
|
+
def_delegators(:new_resource, *dsl_methods)
|
89
|
+
end
|
90
|
+
include @included_resource_dsl_module
|
91
|
+
end
|
92
|
+
end
|
93
|
+
def self.use_inline_resources
|
94
|
+
extend InlineResources::ClassMethods
|
95
|
+
include InlineResources
|
96
|
+
end
|
97
|
+
protected
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,283 @@
|
|
1
|
+
require 'chef_compat/copied_from_chef'
|
2
|
+
module ChefCompat
|
3
|
+
module CopiedFromChef
|
4
|
+
require 'chef_compat/copied_from_chef/chef/mixin/params_validate'
|
5
|
+
require 'chef_compat/copied_from_chef/chef/resource/action_class'
|
6
|
+
class Chef < (defined?(::Chef) ? ::Chef : Object)
|
7
|
+
class Resource < (defined?(::Chef::Resource) ? ::Chef::Resource : Object)
|
8
|
+
def initialize(name, run_context=nil)
|
9
|
+
name(name) unless name.nil?
|
10
|
+
@run_context = run_context
|
11
|
+
@noop = nil
|
12
|
+
@before = nil
|
13
|
+
@params = Hash.new
|
14
|
+
@provider = nil
|
15
|
+
@allowed_actions = self.class.allowed_actions.to_a
|
16
|
+
@action = self.class.default_action
|
17
|
+
@updated = false
|
18
|
+
@updated_by_last_action = false
|
19
|
+
@supports = {}
|
20
|
+
@ignore_failure = false
|
21
|
+
@retries = 0
|
22
|
+
@retry_delay = 2
|
23
|
+
@not_if = []
|
24
|
+
@only_if = []
|
25
|
+
@source_line = nil
|
26
|
+
# We would like to raise an error when the user gives us a guard
|
27
|
+
# interpreter and a ruby_block to the guard. In order to achieve this
|
28
|
+
# we need to understand when the user overrides the default guard
|
29
|
+
# interpreter. Therefore we store the default separately in a different
|
30
|
+
# attribute.
|
31
|
+
@guard_interpreter = nil
|
32
|
+
@default_guard_interpreter = :default
|
33
|
+
@elapsed_time = 0
|
34
|
+
@sensitive = false
|
35
|
+
end
|
36
|
+
def self.properties(include_superclass=true)
|
37
|
+
@properties ||= {}
|
38
|
+
if include_superclass
|
39
|
+
if superclass.respond_to?(:properties)
|
40
|
+
superclass.properties.merge(@properties)
|
41
|
+
else
|
42
|
+
@properties.dup
|
43
|
+
end
|
44
|
+
else
|
45
|
+
@properties
|
46
|
+
end
|
47
|
+
end
|
48
|
+
def action(arg=nil)
|
49
|
+
if arg
|
50
|
+
arg = Array(arg).map(&:to_sym)
|
51
|
+
arg.each do |action|
|
52
|
+
validate(
|
53
|
+
{ action: action },
|
54
|
+
{ action: { kind_of: Symbol, equal_to: allowed_actions } }
|
55
|
+
)
|
56
|
+
end
|
57
|
+
@action = arg
|
58
|
+
else
|
59
|
+
@action
|
60
|
+
end
|
61
|
+
end
|
62
|
+
alias_method :action=, :action
|
63
|
+
def state_for_resource_reporter
|
64
|
+
state = {}
|
65
|
+
state_properties = self.class.state_properties
|
66
|
+
state_properties.each do |property|
|
67
|
+
if property.identity? || property.is_set?(self)
|
68
|
+
state[property.name] = send(property.name)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
state
|
72
|
+
end
|
73
|
+
alias_method :state, :state_for_resource_reporter
|
74
|
+
def identity
|
75
|
+
result = {}
|
76
|
+
identity_properties = self.class.identity_properties
|
77
|
+
identity_properties.each do |property|
|
78
|
+
result[property.name] = send(property.name)
|
79
|
+
end
|
80
|
+
return result.values.first if identity_properties.size == 1
|
81
|
+
result
|
82
|
+
end
|
83
|
+
include Chef::Mixin::ParamsValidate
|
84
|
+
def self.property(name, type=NOT_PASSED, **options)
|
85
|
+
name = name.to_sym
|
86
|
+
|
87
|
+
options.each { |k,v| options[k.to_sym] = v if k.is_a?(String) }
|
88
|
+
|
89
|
+
options[:instance_variable_name] = :"@#{name}" if !options.has_key?(:instance_variable_name)
|
90
|
+
options.merge!(name: name, declared_in: self)
|
91
|
+
|
92
|
+
if type == NOT_PASSED
|
93
|
+
# If a type is not passed, the property derives from the
|
94
|
+
# superclass property (if any)
|
95
|
+
if properties.has_key?(name)
|
96
|
+
property = properties[name].derive(**options)
|
97
|
+
else
|
98
|
+
property = property_type(**options)
|
99
|
+
end
|
100
|
+
|
101
|
+
# If a Property is specified, derive a new one from that.
|
102
|
+
elsif type.is_a?(Property) || (type.is_a?(Class) && type <= Property)
|
103
|
+
property = type.derive(**options)
|
104
|
+
|
105
|
+
# If a primitive type was passed, combine it with "is"
|
106
|
+
else
|
107
|
+
if options[:is]
|
108
|
+
options[:is] = ([ type ] + [ options[:is] ]).flatten(1)
|
109
|
+
else
|
110
|
+
options[:is] = type
|
111
|
+
end
|
112
|
+
property = property_type(**options)
|
113
|
+
end
|
114
|
+
|
115
|
+
local_properties = properties(false)
|
116
|
+
local_properties[name] = property
|
117
|
+
|
118
|
+
property.emit_dsl
|
119
|
+
end
|
120
|
+
def self.property_type(**options)
|
121
|
+
Property.derive(**options)
|
122
|
+
end
|
123
|
+
property :name, String, coerce: proc { |v| v.is_a?(Array) ? v.join(', ') : v.to_s }, desired_state: false
|
124
|
+
def property_is_set?(name)
|
125
|
+
property = self.class.properties[name.to_sym]
|
126
|
+
raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
|
127
|
+
property.is_set?(self)
|
128
|
+
end
|
129
|
+
def reset_property(name)
|
130
|
+
property = self.class.properties[name.to_sym]
|
131
|
+
raise ArgumentError, "Property #{name} is not defined in class #{self}" if !property
|
132
|
+
property.reset(self)
|
133
|
+
end
|
134
|
+
def self.lazy(&block)
|
135
|
+
DelayedEvaluator.new(&block)
|
136
|
+
end
|
137
|
+
def self.state_properties(*names)
|
138
|
+
if !names.empty?
|
139
|
+
names = names.map { |name| name.to_sym }.uniq
|
140
|
+
|
141
|
+
local_properties = properties(false)
|
142
|
+
# Add new properties to the list.
|
143
|
+
names.each do |name|
|
144
|
+
property = properties[name]
|
145
|
+
if !property
|
146
|
+
self.property name, instance_variable_name: false, desired_state: true
|
147
|
+
elsif !property.desired_state?
|
148
|
+
self.property name, desired_state: true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# If state_attrs *excludes* something which is currently desired state,
|
153
|
+
# mark it as desired_state: false.
|
154
|
+
local_properties.each do |name,property|
|
155
|
+
if property.desired_state? && !names.include?(name)
|
156
|
+
self.property name, desired_state: false
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
properties.values.select { |property| property.desired_state? }
|
162
|
+
end
|
163
|
+
def self.identity_properties(*names)
|
164
|
+
if !names.empty?
|
165
|
+
names = names.map { |name| name.to_sym }
|
166
|
+
|
167
|
+
# Add or change properties that are not part of the identity.
|
168
|
+
names.each do |name|
|
169
|
+
property = properties[name]
|
170
|
+
if !property
|
171
|
+
self.property name, instance_variable_name: false, identity: true
|
172
|
+
elsif !property.identity?
|
173
|
+
self.property name, identity: true
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# If identity_properties *excludes* something which is currently part of
|
178
|
+
# the identity, mark it as identity: false.
|
179
|
+
properties.each do |name,property|
|
180
|
+
if property.identity? && !names.include?(name)
|
181
|
+
self.property name, identity: false
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
result = properties.values.select { |property| property.identity? }
|
187
|
+
result = [ properties[:name] ] if result.empty?
|
188
|
+
result
|
189
|
+
end
|
190
|
+
def self.identity_property(name=nil)
|
191
|
+
result = identity_properties(*Array(name))
|
192
|
+
if result.size > 1
|
193
|
+
raise Chef::Exceptions::MultipleIdentityError, "identity_property cannot be called on an object with more than one identity property (#{result.map { |r| r.name }.join(", ")})."
|
194
|
+
end
|
195
|
+
result.first
|
196
|
+
end
|
197
|
+
attr_accessor :allowed_actions
|
198
|
+
def allowed_actions(value=NOT_PASSED)
|
199
|
+
if value != NOT_PASSED
|
200
|
+
self.allowed_actions = value
|
201
|
+
end
|
202
|
+
@allowed_actions
|
203
|
+
end
|
204
|
+
def resource_name
|
205
|
+
@resource_name || self.class.resource_name
|
206
|
+
end
|
207
|
+
def self.use_automatic_resource_name
|
208
|
+
automatic_name = convert_to_snake_case(self.name.split('::')[-1])
|
209
|
+
resource_name automatic_name
|
210
|
+
end
|
211
|
+
def self.allowed_actions(*actions)
|
212
|
+
@allowed_actions ||=
|
213
|
+
if superclass.respond_to?(:allowed_actions)
|
214
|
+
superclass.allowed_actions.dup
|
215
|
+
else
|
216
|
+
[ :nothing ]
|
217
|
+
end
|
218
|
+
@allowed_actions |= actions.flatten
|
219
|
+
end
|
220
|
+
def self.allowed_actions=(value)
|
221
|
+
@allowed_actions = value.uniq
|
222
|
+
end
|
223
|
+
def self.default_action(action_name=NOT_PASSED)
|
224
|
+
unless action_name.equal?(NOT_PASSED)
|
225
|
+
@default_action = Array(action_name).map(&:to_sym)
|
226
|
+
self.allowed_actions |= @default_action
|
227
|
+
end
|
228
|
+
|
229
|
+
if @default_action
|
230
|
+
@default_action
|
231
|
+
elsif superclass.respond_to?(:default_action)
|
232
|
+
superclass.default_action
|
233
|
+
else
|
234
|
+
[:nothing]
|
235
|
+
end
|
236
|
+
end
|
237
|
+
def self.default_action=(action_name)
|
238
|
+
default_action action_name
|
239
|
+
end
|
240
|
+
def self.action(action, &recipe_block)
|
241
|
+
action = action.to_sym
|
242
|
+
declare_action_class
|
243
|
+
action_class.action(action, &recipe_block)
|
244
|
+
self.allowed_actions += [ action ]
|
245
|
+
default_action action if Array(default_action) == [:nothing]
|
246
|
+
end
|
247
|
+
def self.load_current_value(&load_block)
|
248
|
+
define_method(:load_current_value!, &load_block)
|
249
|
+
end
|
250
|
+
def current_value_does_not_exist!
|
251
|
+
raise Chef::Exceptions::CurrentValueDoesNotExist
|
252
|
+
end
|
253
|
+
def self.action_class
|
254
|
+
@action_class ||
|
255
|
+
# If the superclass needed one, then we need one as well.
|
256
|
+
if superclass.respond_to?(:action_class) && superclass.action_class
|
257
|
+
declare_action_class
|
258
|
+
end
|
259
|
+
end
|
260
|
+
def self.declare_action_class
|
261
|
+
return @action_class if @action_class
|
262
|
+
|
263
|
+
if superclass.respond_to?(:action_class)
|
264
|
+
base_provider = superclass.action_class
|
265
|
+
end
|
266
|
+
base_provider ||= Chef::Provider
|
267
|
+
|
268
|
+
resource_class = self
|
269
|
+
@action_class = Class.new(base_provider) do
|
270
|
+
include ActionClass
|
271
|
+
self.resource_class = resource_class
|
272
|
+
end
|
273
|
+
end
|
274
|
+
FORBIDDEN_IVARS = [:@run_context, :@not_if, :@only_if, :@enclosing_provider]
|
275
|
+
HIDDEN_IVARS = [:@allowed_actions, :@resource_name, :@source_line, :@run_context, :@name, :@not_if, :@only_if, :@elapsed_time, :@enclosing_provider]
|
276
|
+
class << self
|
277
|
+
end
|
278
|
+
@@sorted_descendants = nil
|
279
|
+
private
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'chef_compat/copied_from_chef'
|
2
|
+
module ChefCompat
|
3
|
+
module CopiedFromChef
|
4
|
+
#
|
5
|
+
# Author:: John Keiser (<jkeiser@chef.io)
|
6
|
+
# Copyright:: Copyright (c) 2015 Opscode, Inc.
|
7
|
+
# License:: Apache License, Version 2.0
|
8
|
+
#
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
10
|
+
# you may not use this file except in compliance with the License.
|
11
|
+
# You may obtain a copy of the License at
|
12
|
+
#
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
14
|
+
#
|
15
|
+
# Unless required by applicable law or agreed to in writing, software
|
16
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
17
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
18
|
+
# See the License for the specific language governing permissions and
|
19
|
+
# limitations under the License.
|
20
|
+
#
|
21
|
+
|
22
|
+
|
23
|
+
class Chef < (defined?(::Chef) ? ::Chef : Object)
|
24
|
+
class Resource < (defined?(::Chef::Resource) ? ::Chef::Resource : Object)
|
25
|
+
module ActionClass
|
26
|
+
if defined?(::Chef::Resource::ActionClass)
|
27
|
+
require 'chef_compat/delegating_class'
|
28
|
+
extend DelegatingClass
|
29
|
+
@delegates_to = ::Chef::Resource::ActionClass
|
30
|
+
end
|
31
|
+
#
|
32
|
+
# If load_current_value! is defined on the resource, use that.
|
33
|
+
#
|
34
|
+
def load_current_resource
|
35
|
+
if new_resource.respond_to?(:load_current_value!)
|
36
|
+
# dup the resource and then reset desired-state properties.
|
37
|
+
current_resource = new_resource.dup
|
38
|
+
|
39
|
+
# We clear desired state in the copy, because it is supposed to be actual state.
|
40
|
+
# We keep identity properties and non-desired-state, which are assumed to be
|
41
|
+
# "control" values like `recurse: true`
|
42
|
+
current_resource.class.properties.each do |name,property|
|
43
|
+
if property.desired_state? && !property.identity? && !property.name_property?
|
44
|
+
property.reset(current_resource)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Call the actual load_current_value! method. If it raises
|
49
|
+
# CurrentValueDoesNotExist, set current_resource to `nil`.
|
50
|
+
begin
|
51
|
+
# If the user specifies load_current_value do |desired_resource|, we
|
52
|
+
# pass in the desired resource as well as the current one.
|
53
|
+
if current_resource.method(:load_current_value!).arity > 0
|
54
|
+
current_resource.load_current_value!(new_resource)
|
55
|
+
else
|
56
|
+
current_resource.load_current_value!
|
57
|
+
end
|
58
|
+
rescue Chef::Exceptions::CurrentValueDoesNotExist
|
59
|
+
current_resource = nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
@current_resource = current_resource
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.included(other)
|
67
|
+
other.extend(ClassMethods)
|
68
|
+
other.use_inline_resources
|
69
|
+
other.include_resource_dsl true
|
70
|
+
end
|
71
|
+
|
72
|
+
module ClassMethods
|
73
|
+
if defined?(::Chef::Resource::ActionClass::ClassMethods)
|
74
|
+
require 'chef_compat/delegating_class'
|
75
|
+
extend DelegatingClass
|
76
|
+
@delegates_to = ::Chef::Resource::ActionClass::ClassMethods
|
77
|
+
end
|
78
|
+
#
|
79
|
+
# The Chef::Resource class this ActionClass was declared against.
|
80
|
+
#
|
81
|
+
# @return [Class] The Chef::Resource class this ActionClass was declared against.
|
82
|
+
#
|
83
|
+
attr_accessor :resource_class
|
84
|
+
|
85
|
+
def to_s
|
86
|
+
"#{resource_class} action provider"
|
87
|
+
end
|
88
|
+
|
89
|
+
def inspect
|
90
|
+
to_s
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|