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