cheffish 1.6.0 → 2.0.0
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 +4 -4
- data/cheffish.gemspec +1 -0
- data/lib/chef/resource/chef_acl.rb +440 -20
- data/lib/chef/resource/chef_client.rb +50 -25
- data/lib/chef/resource/chef_container.rb +44 -11
- data/lib/chef/resource/chef_data_bag.rb +43 -10
- data/lib/chef/resource/chef_data_bag_item.rb +292 -82
- data/lib/chef/resource/chef_environment.rb +79 -27
- data/lib/chef/resource/chef_group.rb +77 -40
- data/lib/chef/resource/chef_mirror.rb +170 -21
- data/lib/chef/resource/chef_node.rb +77 -11
- data/lib/chef/resource/chef_organization.rb +153 -43
- data/lib/chef/resource/chef_resolved_cookbooks.rb +40 -9
- data/lib/chef/resource/chef_role.rb +81 -29
- data/lib/chef/resource/chef_user.rb +64 -33
- data/lib/chef/resource/private_key.rb +230 -17
- data/lib/chef/resource/public_key.rb +88 -9
- data/lib/cheffish/array_property.rb +29 -0
- data/lib/cheffish/base_resource.rb +254 -0
- data/lib/cheffish/chef_actor_base.rb +135 -0
- data/lib/cheffish/node_properties.rb +107 -0
- data/lib/cheffish/recipe_dsl.rb +0 -14
- data/lib/cheffish/version.rb +1 -1
- data/lib/cheffish.rb +4 -108
- data/spec/integration/chef_acl_spec.rb +0 -2
- data/spec/integration/chef_client_spec.rb +0 -1
- data/spec/integration/chef_container_spec.rb +0 -2
- data/spec/integration/chef_group_spec.rb +0 -2
- data/spec/integration/chef_mirror_spec.rb +0 -2
- data/spec/integration/chef_node_spec.rb +0 -2
- data/spec/integration/chef_organization_spec.rb +1 -3
- data/spec/integration/chef_role_spec.rb +0 -2
- data/spec/integration/chef_user_spec.rb +0 -2
- data/spec/integration/private_key_spec.rb +0 -4
- data/spec/integration/recipe_dsl_spec.rb +0 -2
- data/spec/support/spec_support.rb +0 -1
- data/spec/unit/get_private_key_spec.rb +13 -0
- metadata +22 -20
- data/lib/chef/provider/chef_acl.rb +0 -446
- data/lib/chef/provider/chef_client.rb +0 -53
- data/lib/chef/provider/chef_container.rb +0 -55
- data/lib/chef/provider/chef_data_bag.rb +0 -55
- data/lib/chef/provider/chef_data_bag_item.rb +0 -278
- data/lib/chef/provider/chef_environment.rb +0 -83
- data/lib/chef/provider/chef_group.rb +0 -83
- data/lib/chef/provider/chef_mirror.rb +0 -169
- data/lib/chef/provider/chef_node.rb +0 -87
- data/lib/chef/provider/chef_organization.rb +0 -155
- data/lib/chef/provider/chef_resolved_cookbooks.rb +0 -46
- data/lib/chef/provider/chef_role.rb +0 -84
- data/lib/chef/provider/chef_user.rb +0 -59
- data/lib/chef/provider/private_key.rb +0 -225
- data/lib/chef/provider/public_key.rb +0 -88
- data/lib/cheffish/actor_provider_base.rb +0 -131
- data/lib/cheffish/chef_provider_base.rb +0 -246
@@ -1,22 +1,55 @@
|
|
1
1
|
require 'cheffish'
|
2
|
-
require '
|
2
|
+
require 'cheffish/base_resource'
|
3
3
|
|
4
4
|
class Chef
|
5
5
|
class Resource
|
6
|
-
class ChefDataBag <
|
7
|
-
|
6
|
+
class ChefDataBag < Cheffish::BaseResource
|
7
|
+
resource_name :chef_data_bag
|
8
8
|
|
9
|
-
|
10
|
-
default_action :create
|
9
|
+
property :name, Cheffish::NAME_REGEX, name_property: true
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
action :create do
|
12
|
+
if !current_resource_exists?
|
13
|
+
converge_by "create data bag #{new_resource.name} at #{rest.url}" do
|
14
|
+
rest.post("data", { 'name' => new_resource.name })
|
15
|
+
end
|
16
|
+
end
|
15
17
|
end
|
16
18
|
|
17
|
-
|
19
|
+
action :delete do
|
20
|
+
if current_resource_exists?
|
21
|
+
converge_by "delete data bag #{new_resource.name} at #{rest.url}" do
|
22
|
+
rest.delete("data/#{new_resource.name}")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
action_class.class_eval do
|
28
|
+
def load_current_resource
|
29
|
+
begin
|
30
|
+
@current_resource = json_to_resource(rest.get("data/#{new_resource.name}"))
|
31
|
+
rescue Net::HTTPServerException => e
|
32
|
+
if e.response.code == "404"
|
33
|
+
@current_resource = not_found_resource
|
34
|
+
else
|
35
|
+
raise
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Helpers
|
42
|
+
#
|
43
|
+
# Gives us new_json, current_json, not_found_json, etc.
|
18
44
|
|
19
|
-
|
45
|
+
def resource_class
|
46
|
+
Chef::Resource::ChefDataBag
|
47
|
+
end
|
48
|
+
|
49
|
+
def json_to_resource(json)
|
50
|
+
Chef::Resource::ChefDataBag.new(json['name'], run_context)
|
51
|
+
end
|
52
|
+
end
|
20
53
|
end
|
21
54
|
end
|
22
55
|
end
|
@@ -1,103 +1,50 @@
|
|
1
1
|
require 'cheffish'
|
2
2
|
require 'chef/config'
|
3
|
-
require '
|
3
|
+
require 'cheffish/base_resource'
|
4
|
+
require 'chef/chef_fs/data_handler/data_bag_item_data_handler'
|
5
|
+
require 'chef/encrypted_data_bag_item'
|
4
6
|
|
5
7
|
class Chef
|
6
8
|
class Resource
|
7
|
-
class ChefDataBagItem <
|
8
|
-
|
9
|
-
|
10
|
-
actions :create, :delete, :nothing
|
11
|
-
default_action :create
|
9
|
+
class ChefDataBagItem < Cheffish::BaseResource
|
10
|
+
resource_name :chef_data_bag_item
|
12
11
|
|
13
12
|
def initialize(*args)
|
14
13
|
super
|
15
|
-
|
16
|
-
if !data_bag
|
14
|
+
if !property_is_set?(:data_bag) && run_context.cheffish.current_data_bag
|
17
15
|
data_bag run_context.cheffish.current_data_bag
|
18
16
|
end
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
17
|
+
encryption = run_context.cheffish.current_data_bag_item_encryption
|
18
|
+
if encryption
|
19
|
+
encrypt true if encryption[:encrypt_all]
|
20
|
+
secret encryption[:secret] if encryption[:secret]
|
21
|
+
secret_path encryption[:secret_path] || run_context.config[:encrypted_data_bag_secret] if encryption[:secret_path] || run_context.config[:encrypted_data_bag_secret]
|
22
|
+
encryption_cipher encryption[:encryption_cipher] if encryption[:encryption_cipher]
|
23
|
+
encryption_version encryption[:encryption_version] || run_context.config[:data_bag_encrypt_version] if encryption[:encryption_version] || run_context.config[:data_bag_encrypt_version]
|
24
|
+
old_secret encryption[:old_secret] if encryption[:old_secret]
|
25
|
+
old_secret_path encryption[:old_secret_path] if encryption[:old_secret_path]
|
27
26
|
end
|
28
|
-
chef_server run_context.cheffish.current_chef_server
|
29
27
|
end
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
@data_bag = parts[0]
|
39
|
-
@id = parts[1]
|
40
|
-
else
|
41
|
-
raise "Name #{args[0].inspect} must be a string with 1 or 2 parts, either 'id' or 'data_bag/id"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
result
|
45
|
-
end
|
29
|
+
# If data_bag and id are not specified, take them from name.
|
30
|
+
# name can either be id, or data_bag/id
|
31
|
+
property :id, String, default: lazy { name.split('/', 2)[-1] }
|
32
|
+
property :data_bag, String, default: lazy {
|
33
|
+
split = name.split('/', 2)[0]
|
34
|
+
split.size >= 2 ? split[0] : nil
|
35
|
+
}
|
46
36
|
|
47
|
-
|
48
|
-
# don't redefine it if it's already there
|
49
|
-
NOT_PASSED = Object.new unless defined?(NOT_PASSED)
|
50
|
-
|
51
|
-
def id(value = NOT_PASSED)
|
52
|
-
if value == NOT_PASSED
|
53
|
-
@id
|
54
|
-
else
|
55
|
-
@id = value
|
56
|
-
name data_bag ? "#{data_bag}/#{id}" : id
|
57
|
-
end
|
58
|
-
end
|
59
|
-
def data_bag(value = NOT_PASSED)
|
60
|
-
if value == NOT_PASSED
|
61
|
-
@data_bag
|
62
|
-
else
|
63
|
-
@data_bag = value
|
64
|
-
name data_bag ? "#{data_bag}/#{id}" : id
|
65
|
-
end
|
66
|
-
end
|
67
|
-
attribute :raw_data, :kind_of => Hash
|
37
|
+
property :raw_data, Hash
|
68
38
|
|
69
39
|
# If secret or secret_path are set, encrypt is assumed true. encrypt exists mainly for with_secret and with_secret_path
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
@secret
|
75
|
-
else
|
76
|
-
@secret = new_secret
|
77
|
-
@encrypt = true if @encrypt.nil?
|
78
|
-
end
|
79
|
-
end
|
80
|
-
#attribute :secret_path, :kind_of => String
|
81
|
-
def secret_path(new_secret_path = nil)
|
82
|
-
if !new_secret_path
|
83
|
-
@secret_path
|
84
|
-
else
|
85
|
-
@secret_path = new_secret_path
|
86
|
-
@encrypt = true if @encrypt.nil?
|
87
|
-
end
|
88
|
-
end
|
89
|
-
attribute :encryption_version, :kind_of => Integer
|
40
|
+
property :encrypt, Boolean, default: lazy { secret || secret_path }
|
41
|
+
property :secret, String
|
42
|
+
property :secret_path, String
|
43
|
+
property :encryption_version, Integer
|
90
44
|
|
91
45
|
# Old secret (or secrets) to read the old data bag when we are changing keys and re-encrypting data
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
# Specifies that this is a complete specification for the environment (i.e. attributes you don't specify will be
|
96
|
-
# reset to their defaults)
|
97
|
-
attribute :complete, :kind_of => [TrueClass, FalseClass]
|
98
|
-
|
99
|
-
attribute :raw_json, :kind_of => Hash
|
100
|
-
attribute :chef_server, :kind_of => Hash
|
46
|
+
property :old_secret, [String, Array]
|
47
|
+
property :old_secret_path, [String, Array]
|
101
48
|
|
102
49
|
# value 'ip_address', '127.0.0.1'
|
103
50
|
# value [ 'pushy', 'port' ], '9000'
|
@@ -116,6 +63,269 @@ class Chef
|
|
116
63
|
raise "value requires either a value or a block"
|
117
64
|
end
|
118
65
|
end
|
66
|
+
|
67
|
+
action :create do
|
68
|
+
differences = calculate_differences
|
69
|
+
|
70
|
+
if current_resource_exists?
|
71
|
+
if differences.size > 0
|
72
|
+
description = [ "update data bag item #{new_resource.id} at #{rest.url}" ] + differences
|
73
|
+
converge_by description do
|
74
|
+
rest.put("data/#{new_resource.data_bag}/#{new_resource.id}", normalize_for_put(new_json))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
else
|
78
|
+
description = [ "create data bag item #{new_resource.id} at #{rest.url}" ] + differences
|
79
|
+
converge_by description do
|
80
|
+
rest.post("data/#{new_resource.data_bag}", normalize_for_post(new_json))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
action :delete do
|
86
|
+
if current_resource_exists?
|
87
|
+
converge_by "delete data bag item #{new_resource.id} at #{rest.url}" do
|
88
|
+
rest.delete("data/#{new_resource.data_bag}/#{new_resource.id}")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
action_class.class_eval do
|
94
|
+
def load_current_resource
|
95
|
+
begin
|
96
|
+
json = rest.get("data/#{new_resource.data_bag}/#{new_resource.id}")
|
97
|
+
resource = Chef::Resource::ChefDataBagItem.new(new_resource.name, run_context)
|
98
|
+
resource.raw_data json
|
99
|
+
@current_resource = resource
|
100
|
+
rescue Net::HTTPServerException => e
|
101
|
+
if e.response.code == "404"
|
102
|
+
@current_resource = not_found_resource
|
103
|
+
else
|
104
|
+
raise
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Determine if data bag is encrypted and if so, what its version is
|
109
|
+
first_real_key, first_real_value = (current_resource.raw_data || {}).select { |key, value| key != 'id' && !value.nil? }.first
|
110
|
+
if first_real_value
|
111
|
+
if first_real_value.is_a?(Hash) &&
|
112
|
+
first_real_value['version'].is_a?(Integer) &&
|
113
|
+
first_real_value['version'] > 0 &&
|
114
|
+
first_real_value.has_key?('encrypted_data')
|
115
|
+
|
116
|
+
current_resource.encrypt true
|
117
|
+
current_resource.encryption_version first_real_value['version']
|
118
|
+
|
119
|
+
decrypt_error = nil
|
120
|
+
|
121
|
+
# Check if the desired secret is the one (which it generally should be)
|
122
|
+
|
123
|
+
if new_resource.secret || new_resource.secret_path
|
124
|
+
begin
|
125
|
+
Chef::EncryptedDataBagItem::Decryptor.for(first_real_value, new_secret).for_decrypted_item
|
126
|
+
current_resource.secret new_secret
|
127
|
+
rescue Chef::EncryptedDataBagItem::DecryptionFailure
|
128
|
+
decrypt_error = $!
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# If the current secret doesn't work, look through the specified old secrets
|
133
|
+
|
134
|
+
if !current_resource.secret
|
135
|
+
old_secrets = []
|
136
|
+
if new_resource.old_secret
|
137
|
+
old_secrets += Array(new_resource.old_secret)
|
138
|
+
end
|
139
|
+
if new_resource.old_secret_path
|
140
|
+
old_secrets += Array(new_resource.old_secret_path).map do |secret_path|
|
141
|
+
Chef::EncryptedDataBagItem.load_secret(new_resource.old_secret_file)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
old_secrets.each do |secret|
|
145
|
+
begin
|
146
|
+
Chef::EncryptedDataBagItem::Decryptor.for(first_real_value, secret).for_decrypted_item
|
147
|
+
current_resource.secret secret
|
148
|
+
rescue Chef::EncryptedDataBagItem::DecryptionFailure
|
149
|
+
decrypt_error = $!
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# If we couldn't figure out the secret, emit a warning (this isn't a fatal flaw unless we
|
154
|
+
# need to reuse one of the values from the data bag)
|
155
|
+
if !current_resource.secret
|
156
|
+
if decrypt_error
|
157
|
+
Chef::Log.warn "Existing data bag is encrypted, but could not decrypt: #{decrypt_error.message}."
|
158
|
+
else
|
159
|
+
Chef::Log.warn "Existing data bag is encrypted, but no secret was specified."
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
else
|
165
|
+
|
166
|
+
# There are no encryptable values, so pretend encryption is the same as desired
|
167
|
+
|
168
|
+
current_resource.encrypt new_resource.encrypt
|
169
|
+
current_resource.encryption_version new_resource.encryption_version
|
170
|
+
if new_resource.secret || new_resource.secret_path
|
171
|
+
current_resource.secret new_secret
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def new_json
|
177
|
+
@new_json ||= begin
|
178
|
+
if new_encrypt
|
179
|
+
# Encrypt new stuff
|
180
|
+
result = encrypt(new_decrypted, new_secret, new_resource.encryption_version)
|
181
|
+
else
|
182
|
+
result = new_decrypted
|
183
|
+
end
|
184
|
+
result
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def new_encrypt
|
189
|
+
new_resource.encrypt.nil? ? current_resource.encrypt : new_resource.encrypt
|
190
|
+
end
|
191
|
+
|
192
|
+
def new_secret
|
193
|
+
@new_secret ||= begin
|
194
|
+
if new_resource.secret
|
195
|
+
new_resource.secret
|
196
|
+
elsif new_resource.secret_path
|
197
|
+
Chef::EncryptedDataBagItem.load_secret(new_resource.secret_path)
|
198
|
+
elsif new_resource.encrypt.nil?
|
199
|
+
current_resource.secret
|
200
|
+
else
|
201
|
+
raise "Data bag item #{new_resource.name} has encryption on but no secret or secret_path is specified"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def decrypt(json, secret)
|
207
|
+
Chef::EncryptedDataBagItem.new(json, secret).to_hash
|
208
|
+
end
|
209
|
+
|
210
|
+
def encrypt(json, secret, version)
|
211
|
+
old_version = run_context.config[:data_bag_encrypt_version]
|
212
|
+
run_context.config[:data_bag_encrypt_version] = version
|
213
|
+
begin
|
214
|
+
Chef::EncryptedDataBagItem.encrypt_data_bag_item(json, secret)
|
215
|
+
ensure
|
216
|
+
run_context.config[:data_bag_encrypt_version] = old_version
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# Get the desired (new) json pre-encryption, for comparison purposes
|
221
|
+
def new_decrypted
|
222
|
+
@new_decrypted ||= begin
|
223
|
+
if new_resource.complete
|
224
|
+
result = new_resource.raw_data || {}
|
225
|
+
else
|
226
|
+
result = current_decrypted.merge(new_resource.raw_data || {})
|
227
|
+
end
|
228
|
+
result['id'] = new_resource.id
|
229
|
+
result = apply_modifiers(new_resource.raw_data_modifiers, result)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Get the current json decrypted, for comparison purposes
|
234
|
+
def current_decrypted
|
235
|
+
@current_decrypted ||= begin
|
236
|
+
if current_resource.secret
|
237
|
+
decrypt(current_resource.raw_data || { 'id' => new_resource.id }, current_resource.secret)
|
238
|
+
elsif current_resource.encrypt
|
239
|
+
raise "Could not decrypt current data bag item #{current_resource.name}"
|
240
|
+
else
|
241
|
+
current_resource.raw_data || { 'id' => new_resource.id }
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Figure out the differences between new and current
|
247
|
+
def calculate_differences
|
248
|
+
if new_encrypt
|
249
|
+
if current_resource.encrypt
|
250
|
+
# Both are encrypted, check if the encryption type is the same
|
251
|
+
description = ''
|
252
|
+
if new_secret != current_resource.secret
|
253
|
+
description << ' with new secret'
|
254
|
+
end
|
255
|
+
if new_resource.encryption_version != current_resource.encryption_version
|
256
|
+
description << " from v#{current_resource.encryption_version} to v#{new_resource.encryption_version} encryption"
|
257
|
+
end
|
258
|
+
|
259
|
+
if description != ''
|
260
|
+
# Encryption is different, we're reencrypting
|
261
|
+
differences = [ "re-encrypt#{description}"]
|
262
|
+
else
|
263
|
+
# Encryption is the same, we're just updating
|
264
|
+
differences = []
|
265
|
+
end
|
266
|
+
else
|
267
|
+
# New stuff should be encrypted, old is not. Encrypting.
|
268
|
+
differences = [ "encrypt with v#{new_resource.encryption_version} encryption" ]
|
269
|
+
end
|
270
|
+
|
271
|
+
# Get differences in the actual json
|
272
|
+
if current_resource.secret
|
273
|
+
json_differences(current_decrypted, new_decrypted, false, '', differences)
|
274
|
+
elsif current_resource.encrypt
|
275
|
+
# Encryption is different and we can't read the old values. Only allow the change
|
276
|
+
# if we're overwriting the data bag item
|
277
|
+
if !new_resource.complete
|
278
|
+
raise "Cannot encrypt #{new_resource.name} due to failure to decrypt existing resource. Set 'complete true' to overwrite or add the old secret as old_secret / old_secret_path."
|
279
|
+
end
|
280
|
+
differences = [ "overwrite data bag item (cannot decrypt old data bag item)"]
|
281
|
+
differences = (new_resource.raw_data.keys & current_resource.raw_data.keys).map { |key| "overwrite #{key}"}
|
282
|
+
differences += (new_resource.raw_data.keys - current_resource.raw_data.keys).map { |key| "add #{key}"}
|
283
|
+
differences += (current_resource.raw_data.keys - new_resource.raw_data.keys).map { |key| "remove #{key}" }
|
284
|
+
else
|
285
|
+
json_differences(current_decrypted, new_decrypted, false, '', differences)
|
286
|
+
end
|
287
|
+
else
|
288
|
+
if current_resource.encrypt
|
289
|
+
# New stuff should not be encrypted, old is. Decrypting.
|
290
|
+
differences = [ "decrypt data bag item to plaintext" ]
|
291
|
+
else
|
292
|
+
differences = []
|
293
|
+
end
|
294
|
+
json_differences(current_decrypted, new_decrypted, true, '', differences)
|
295
|
+
end
|
296
|
+
differences
|
297
|
+
end
|
298
|
+
|
299
|
+
#
|
300
|
+
# Helpers
|
301
|
+
#
|
302
|
+
|
303
|
+
def resource_class
|
304
|
+
Chef::Resource::ChefDataBagItem
|
305
|
+
end
|
306
|
+
|
307
|
+
def data_handler
|
308
|
+
Chef::ChefFS::DataHandler::DataBagItemDataHandler.new
|
309
|
+
end
|
310
|
+
|
311
|
+
def keys
|
312
|
+
{
|
313
|
+
'id' => :id,
|
314
|
+
'data_bag' => :data_bag,
|
315
|
+
'raw_data' => :raw_data
|
316
|
+
}
|
317
|
+
end
|
318
|
+
|
319
|
+
def not_found_resource
|
320
|
+
resource = super
|
321
|
+
resource.data_bag new_resource.data_bag
|
322
|
+
resource
|
323
|
+
end
|
324
|
+
|
325
|
+
def fake_entry
|
326
|
+
FakeEntry.new("#{new_resource.id}.json", FakeEntry.new(new_resource.data_bag))
|
327
|
+
end
|
328
|
+
end
|
119
329
|
end
|
120
330
|
end
|
121
331
|
end
|
@@ -1,38 +1,20 @@
|
|
1
1
|
require 'cheffish'
|
2
|
-
require '
|
2
|
+
require 'cheffish/base_resource'
|
3
3
|
require 'chef/environment'
|
4
|
+
require 'chef/chef_fs/data_handler/environment_data_handler'
|
4
5
|
|
5
6
|
class Chef
|
6
7
|
class Resource
|
7
|
-
class ChefEnvironment <
|
8
|
-
|
8
|
+
class ChefEnvironment < Cheffish::BaseResource
|
9
|
+
resource_name :chef_environment
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def initialize(*args)
|
14
|
-
super
|
15
|
-
chef_server run_context.cheffish.current_chef_server
|
16
|
-
end
|
17
|
-
|
18
|
-
attribute :name, :kind_of => String, :regex => Cheffish::NAME_REGEX, :name_attribute => true
|
19
|
-
attribute :description, :kind_of => String
|
20
|
-
attribute :cookbook_versions, :kind_of => Hash, :callbacks => {
|
11
|
+
property :name, Cheffish::NAME_REGEX, name_property: true
|
12
|
+
property :description, String
|
13
|
+
property :cookbook_versions, Hash, callbacks: {
|
21
14
|
"should have valid cookbook versions" => lambda { |value| Chef::Environment.validate_cookbook_versions(value) }
|
22
15
|
}
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
# Specifies that this is a complete specification for the environment (i.e. attributes you don't specify will be
|
27
|
-
# reset to their defaults)
|
28
|
-
attribute :complete, :kind_of => [TrueClass, FalseClass]
|
29
|
-
|
30
|
-
attribute :raw_json, :kind_of => Hash
|
31
|
-
attribute :chef_server, :kind_of => Hash
|
32
|
-
|
33
|
-
# `NOT_PASSED` is defined in chef-12.5.0, this guard will ensure we
|
34
|
-
# don't redefine it if it's already there
|
35
|
-
NOT_PASSED=Object.new unless defined?(NOT_PASSED)
|
16
|
+
property :default_attributes, Hash
|
17
|
+
property :override_attributes, Hash
|
36
18
|
|
37
19
|
# default 'ip_address', '127.0.0.1'
|
38
20
|
# default [ 'pushy', 'port' ], '9000'
|
@@ -72,6 +54,76 @@ class Chef
|
|
72
54
|
|
73
55
|
alias :attributes :default_attributes
|
74
56
|
alias :attribute :default
|
57
|
+
|
58
|
+
|
59
|
+
action :create do
|
60
|
+
differences = json_differences(current_json, new_json)
|
61
|
+
|
62
|
+
if current_resource_exists?
|
63
|
+
if differences.size > 0
|
64
|
+
description = [ "update environment #{new_resource.name} at #{rest.url}" ] + differences
|
65
|
+
converge_by description do
|
66
|
+
rest.put("environments/#{new_resource.name}", normalize_for_put(new_json))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
else
|
70
|
+
description = [ "create environment #{new_resource.name} at #{rest.url}" ] + differences
|
71
|
+
converge_by description do
|
72
|
+
rest.post("environments", normalize_for_post(new_json))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
action :delete do
|
78
|
+
if current_resource_exists?
|
79
|
+
converge_by "delete environment #{new_resource.name} at #{rest.url}" do
|
80
|
+
rest.delete("environments/#{new_resource.name}")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
action_class.class_eval do
|
86
|
+
def load_current_resource
|
87
|
+
begin
|
88
|
+
@current_resource = json_to_resource(rest.get("environments/#{new_resource.name}"))
|
89
|
+
rescue Net::HTTPServerException => e
|
90
|
+
if e.response.code == "404"
|
91
|
+
@current_resource = not_found_resource
|
92
|
+
else
|
93
|
+
raise
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def augment_new_json(json)
|
99
|
+
# Apply modifiers
|
100
|
+
json['default_attributes'] = apply_modifiers(new_resource.default_attribute_modifiers, json['default_attributes'])
|
101
|
+
json['override_attributes'] = apply_modifiers(new_resource.override_attribute_modifiers, json['override_attributes'])
|
102
|
+
json
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Helpers
|
107
|
+
#
|
108
|
+
|
109
|
+
def resource_class
|
110
|
+
Chef::Resource::ChefEnvironment
|
111
|
+
end
|
112
|
+
|
113
|
+
def data_handler
|
114
|
+
Chef::ChefFS::DataHandler::EnvironmentDataHandler.new
|
115
|
+
end
|
116
|
+
|
117
|
+
def keys
|
118
|
+
{
|
119
|
+
'name' => :name,
|
120
|
+
'description' => :description,
|
121
|
+
'cookbook_versions' => :cookbook_versions,
|
122
|
+
'default_attributes' => :default_attributes,
|
123
|
+
'override_attributes' => :override_attributes
|
124
|
+
}
|
125
|
+
end
|
126
|
+
end
|
75
127
|
end
|
76
128
|
end
|
77
129
|
end
|