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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/cheffish.gemspec +1 -0
  3. data/lib/chef/resource/chef_acl.rb +440 -20
  4. data/lib/chef/resource/chef_client.rb +50 -25
  5. data/lib/chef/resource/chef_container.rb +44 -11
  6. data/lib/chef/resource/chef_data_bag.rb +43 -10
  7. data/lib/chef/resource/chef_data_bag_item.rb +292 -82
  8. data/lib/chef/resource/chef_environment.rb +79 -27
  9. data/lib/chef/resource/chef_group.rb +77 -40
  10. data/lib/chef/resource/chef_mirror.rb +170 -21
  11. data/lib/chef/resource/chef_node.rb +77 -11
  12. data/lib/chef/resource/chef_organization.rb +153 -43
  13. data/lib/chef/resource/chef_resolved_cookbooks.rb +40 -9
  14. data/lib/chef/resource/chef_role.rb +81 -29
  15. data/lib/chef/resource/chef_user.rb +64 -33
  16. data/lib/chef/resource/private_key.rb +230 -17
  17. data/lib/chef/resource/public_key.rb +88 -9
  18. data/lib/cheffish/array_property.rb +29 -0
  19. data/lib/cheffish/base_resource.rb +254 -0
  20. data/lib/cheffish/chef_actor_base.rb +135 -0
  21. data/lib/cheffish/node_properties.rb +107 -0
  22. data/lib/cheffish/recipe_dsl.rb +0 -14
  23. data/lib/cheffish/version.rb +1 -1
  24. data/lib/cheffish.rb +4 -108
  25. data/spec/integration/chef_acl_spec.rb +0 -2
  26. data/spec/integration/chef_client_spec.rb +0 -1
  27. data/spec/integration/chef_container_spec.rb +0 -2
  28. data/spec/integration/chef_group_spec.rb +0 -2
  29. data/spec/integration/chef_mirror_spec.rb +0 -2
  30. data/spec/integration/chef_node_spec.rb +0 -2
  31. data/spec/integration/chef_organization_spec.rb +1 -3
  32. data/spec/integration/chef_role_spec.rb +0 -2
  33. data/spec/integration/chef_user_spec.rb +0 -2
  34. data/spec/integration/private_key_spec.rb +0 -4
  35. data/spec/integration/recipe_dsl_spec.rb +0 -2
  36. data/spec/support/spec_support.rb +0 -1
  37. data/spec/unit/get_private_key_spec.rb +13 -0
  38. metadata +22 -20
  39. data/lib/chef/provider/chef_acl.rb +0 -446
  40. data/lib/chef/provider/chef_client.rb +0 -53
  41. data/lib/chef/provider/chef_container.rb +0 -55
  42. data/lib/chef/provider/chef_data_bag.rb +0 -55
  43. data/lib/chef/provider/chef_data_bag_item.rb +0 -278
  44. data/lib/chef/provider/chef_environment.rb +0 -83
  45. data/lib/chef/provider/chef_group.rb +0 -83
  46. data/lib/chef/provider/chef_mirror.rb +0 -169
  47. data/lib/chef/provider/chef_node.rb +0 -87
  48. data/lib/chef/provider/chef_organization.rb +0 -155
  49. data/lib/chef/provider/chef_resolved_cookbooks.rb +0 -46
  50. data/lib/chef/provider/chef_role.rb +0 -84
  51. data/lib/chef/provider/chef_user.rb +0 -59
  52. data/lib/chef/provider/private_key.rb +0 -225
  53. data/lib/chef/provider/public_key.rb +0 -88
  54. data/lib/cheffish/actor_provider_base.rb +0 -131
  55. data/lib/cheffish/chef_provider_base.rb +0 -246
@@ -1,22 +1,55 @@
1
1
  require 'cheffish'
2
- require 'chef/resource/lwrp_base'
2
+ require 'cheffish/base_resource'
3
3
 
4
4
  class Chef
5
5
  class Resource
6
- class ChefDataBag < Chef::Resource::LWRPBase
7
- self.resource_name = 'chef_data_bag'
6
+ class ChefDataBag < Cheffish::BaseResource
7
+ resource_name :chef_data_bag
8
8
 
9
- actions :create, :delete, :nothing
10
- default_action :create
9
+ property :name, Cheffish::NAME_REGEX, name_property: true
11
10
 
12
- def initialize(*args)
13
- super
14
- chef_server run_context.cheffish.current_chef_server
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
- attribute :name, :kind_of => String, :regex => Cheffish::NAME_REGEX, :name_attribute => true
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
- attribute :chef_server, :kind_of => Hash
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 'chef/resource/lwrp_base'
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 < Chef::Resource::LWRPBase
8
- self.resource_name = 'chef_data_bag_item'
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
- name @name
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
- if run_context.cheffish.current_data_bag_item_encryption
20
- @encrypt = true if run_context.cheffish.current_data_bag_item_encryption[:encrypt_all]
21
- @secret = run_context.cheffish.current_data_bag_item_encryption[:secret]
22
- @secret_path = run_context.cheffish.current_data_bag_item_encryption[:secret_path] || run_context.config[:encrypted_data_bag_secret]
23
- @encryption_cipher = run_context.cheffish.current_data_bag_item_encryption[:encryption_cipher]
24
- @encryption_version = run_context.cheffish.current_data_bag_item_encryption[:encryption_version] || run_context.config[:data_bag_encrypt_version]
25
- @old_secret = run_context.cheffish.current_data_bag_item_encryption[:old_secret]
26
- @old_secret_path = run_context.cheffish.current_data_bag_item_encryption[:old_secret_path]
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
- def name(*args)
32
- result = super(*args)
33
- if args.size == 1
34
- parts = name.split('/')
35
- if parts.size == 1
36
- @id = parts[0]
37
- elsif parts.size == 2
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
- # `NOT_PASSED` is defined in chef-12.5.0, this guard will ensure we
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
- attribute :encrypt, :kind_of => [TrueClass, FalseClass]
71
- #attribute :secret, :kind_of => String
72
- def secret(new_secret = nil)
73
- if !new_secret
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
- attribute :old_secret, :kind_of => [String, Array]
93
- attribute :old_secret_path, :kind_of => [String, Array]
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 'chef/resource/lwrp_base'
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 < Chef::Resource::LWRPBase
8
- self.resource_name = 'chef_environment'
8
+ class ChefEnvironment < Cheffish::BaseResource
9
+ resource_name :chef_environment
9
10
 
10
- actions :create, :delete, :nothing
11
- default_action :create
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
- attribute :default_attributes, :kind_of => Hash
24
- attribute :override_attributes, :kind_of => Hash
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