ridley 0.10.2 → 0.11.0.rc1

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 (85) hide show
  1. data/README.md +147 -216
  2. data/lib/ridley.rb +2 -0
  3. data/lib/ridley/bootstrap_bindings/unix_template_binding.rb +21 -25
  4. data/lib/ridley/bootstrap_bindings/windows_template_binding.rb +29 -34
  5. data/lib/ridley/bootstrapper.rb +2 -2
  6. data/lib/ridley/bootstrapper/context.rb +5 -5
  7. data/lib/ridley/chef.rb +0 -1
  8. data/lib/ridley/chef/cookbook.rb +0 -9
  9. data/lib/ridley/chef_object.rb +128 -0
  10. data/lib/ridley/chef_objects.rb +3 -0
  11. data/lib/ridley/chef_objects/client_object.rb +55 -0
  12. data/lib/ridley/chef_objects/cookbook_object.rb +190 -0
  13. data/lib/ridley/chef_objects/data_bag_item_obect.rb +104 -0
  14. data/lib/ridley/chef_objects/data_bag_object.rb +31 -0
  15. data/lib/ridley/chef_objects/environment_object.rb +59 -0
  16. data/lib/ridley/chef_objects/node_object.rb +161 -0
  17. data/lib/ridley/chef_objects/role_object.rb +62 -0
  18. data/lib/ridley/chef_objects/sandbox_object.rb +58 -0
  19. data/lib/ridley/client.rb +76 -45
  20. data/lib/ridley/connection.rb +1 -1
  21. data/lib/ridley/errors.rb +8 -1
  22. data/lib/ridley/host_connector.rb +26 -6
  23. data/lib/ridley/host_connector/ssh.rb +3 -3
  24. data/lib/ridley/host_connector/ssh/worker.rb +7 -9
  25. data/lib/ridley/host_connector/winrm/worker.rb +4 -5
  26. data/lib/ridley/mixin/bootstrap_binding.rb +1 -12
  27. data/lib/ridley/resource.rb +51 -171
  28. data/lib/ridley/resources/client_resource.rb +18 -68
  29. data/lib/ridley/resources/cookbook_resource.rb +181 -381
  30. data/lib/ridley/resources/data_bag_item_resource.rb +55 -161
  31. data/lib/ridley/resources/data_bag_resource.rb +20 -61
  32. data/lib/ridley/resources/environment_resource.rb +9 -64
  33. data/lib/ridley/resources/node_resource.rb +135 -311
  34. data/lib/ridley/resources/role_resource.rb +1 -57
  35. data/lib/ridley/resources/sandbox_resource.rb +80 -65
  36. data/lib/ridley/resources/search_resource.rb +99 -0
  37. data/lib/ridley/sandbox_uploader.rb +12 -52
  38. data/lib/ridley/version.rb +1 -1
  39. data/spec/acceptance/bootstrapping_spec.rb +1 -1
  40. data/spec/acceptance/client_resource_spec.rb +15 -37
  41. data/spec/acceptance/data_bag_item_resource_spec.rb +8 -14
  42. data/spec/acceptance/data_bag_resource_spec.rb +1 -1
  43. data/spec/acceptance/environment_resource_spec.rb +13 -22
  44. data/spec/acceptance/node_resource_spec.rb +10 -29
  45. data/spec/acceptance/role_resource_spec.rb +14 -13
  46. data/spec/acceptance/sandbox_resource_spec.rb +2 -2
  47. data/spec/support/shared_examples/ridley_resource.rb +2 -23
  48. data/spec/unit/ridley/bootstrap_bindings/unix_template_binding_spec.rb +3 -4
  49. data/spec/unit/ridley/bootstrap_bindings/windows_template_binding_spec.rb +3 -5
  50. data/spec/unit/ridley/bootstrapper/context_spec.rb +2 -3
  51. data/spec/unit/ridley/bootstrapper_spec.rb +1 -1
  52. data/spec/unit/ridley/chef_object_spec.rb +240 -0
  53. data/spec/unit/ridley/chef_objects/client_object_spec.rb +11 -0
  54. data/spec/unit/ridley/chef_objects/cookbook_object_spec.rb +93 -0
  55. data/spec/unit/ridley/chef_objects/data_bag_item_object_spec.rb +74 -0
  56. data/spec/unit/ridley/chef_objects/data_bag_object_spec.rb +9 -0
  57. data/spec/unit/ridley/chef_objects/environment_object_spec.rb +57 -0
  58. data/spec/unit/ridley/chef_objects/node_object_spec.rb +252 -0
  59. data/spec/unit/ridley/chef_objects/role_object_spec.rb +57 -0
  60. data/spec/unit/ridley/chef_objects/sandbox_object_spec.rb +66 -0
  61. data/spec/unit/ridley/client_spec.rb +51 -51
  62. data/spec/unit/ridley/host_connector/ssh/worker_spec.rb +4 -4
  63. data/spec/unit/ridley/host_connector/ssh_spec.rb +26 -24
  64. data/spec/unit/ridley/host_connector/winrm/worker_spec.rb +3 -4
  65. data/spec/unit/ridley/host_connector/winrm_spec.rb +4 -4
  66. data/spec/unit/ridley/host_connector_spec.rb +40 -3
  67. data/spec/unit/ridley/mixin/bootstrap_binding_spec.rb +1 -1
  68. data/spec/unit/ridley/resource_spec.rb +81 -109
  69. data/spec/unit/ridley/resources/client_resource_spec.rb +18 -33
  70. data/spec/unit/ridley/resources/cookbook_resource_spec.rb +56 -230
  71. data/spec/unit/ridley/resources/data_bag_item_resource_spec.rb +2 -57
  72. data/spec/unit/ridley/resources/data_bag_resource_spec.rb +12 -7
  73. data/spec/unit/ridley/resources/environment_resource_spec.rb +10 -118
  74. data/spec/unit/ridley/resources/node_resource_spec.rb +83 -394
  75. data/spec/unit/ridley/resources/role_resource_spec.rb +2 -56
  76. data/spec/unit/ridley/resources/sandbox_resource_spec.rb +139 -136
  77. data/spec/unit/ridley/resources/search_resource_spec.rb +234 -0
  78. data/spec/unit/ridley/sandbox_uploader_spec.rb +13 -58
  79. metadata +36 -17
  80. data/lib/ridley/chef/chefignore.rb +0 -76
  81. data/lib/ridley/resources/encrypted_data_bag_item_resource.rb +0 -55
  82. data/lib/ridley/resources/search.rb +0 -101
  83. data/spec/fixtures/chefignore +0 -8
  84. data/spec/unit/ridley/chef/chefignore_spec.rb +0 -40
  85. data/spec/unit/ridley/resources/search_spec.rb +0 -221
@@ -1,422 +1,222 @@
1
1
  module Ridley
2
2
  # @author Jamie Winsor <reset@riotgames.com>
3
3
  class CookbookResource < Ridley::Resource
4
- class << self
5
- # List all of the cookbooks and their versions present on the remote
6
- #
7
- # @example return value
8
- # {
9
- # "ant" => [
10
- # "0.10.1"
11
- # ],
12
- # "apache2" => [
13
- # "1.4.0"
14
- # ]
15
- # }
16
- #
17
- # @param [Ridley::Client] client
18
- #
19
- # @return [Hash]
20
- # a hash containing keys which represent cookbook names and values which contain
21
- # an array of strings representing the available versions
22
- def all(client)
23
- response = client.connection.get(self.resource_path).body
24
-
25
- {}.tap do |cookbooks|
26
- response.each do |name, details|
27
- cookbooks[name] = details["versions"].collect { |version| version["version"] }
28
- end
29
- end
30
- end
31
-
32
- # Delete a cookbook of the given name and version on the remote Chef server
33
- #
34
- # @param [Ridley::Client] client
35
- # @param [String] name
36
- # @param [String] version
37
- #
38
- # @option options [Boolean] purge (false)
39
- #
40
- # @return [Boolean]
41
- def delete(client, name, version, options = {})
42
- options = options.reverse_merge(purge: false)
43
- url = "#{self.resource_path}/#{name}/#{version}"
44
- url += "?purge=true" if options[:purge]
45
-
46
- client.connection.delete(url).body
47
- true
48
- rescue Errors::HTTPNotFound
49
- true
50
- end
51
-
52
- # Delete all of the versions of a given cookbook on the remote Chef server
53
- #
54
- # @param [Ridley::Client] client
55
- # @param [String] name
56
- # name of the cookbook to delete
57
- #
58
- # @option options [Boolean] purge (false)
59
- def delete_all(client, name, options = {})
60
- versions(client, name).each do |version|
61
- delete(client, name, version, options)
62
- end
63
- end
64
-
65
- # Download the entire cookbook
66
- #
67
- # @param [Ridley::Client] client
68
- # @param [String] name
69
- # @param [String] version
70
- # @param [String] destination (Dir.mktmpdir)
71
- # the place to download the cookbook too. If no value is provided the cookbook
72
- # will be downloaded to a temporary location
73
- #
74
- # @return [String]
75
- # the path to the directory the cookbook was downloaded to
76
- def download(client, name, version, destination = Dir.mktmpdir)
77
- cookbook = find(client, name, version)
78
-
79
- unless cookbook.nil?
80
- cookbook.download(destination)
81
- end
82
- end
83
-
84
- # @param [Ridley::Client] client
85
- # @param [String, #chef_id] object
86
- # @param [String] version
87
- #
88
- # @return [nil, CookbookResource]
89
- def find(client, object, version)
90
- find!(client, object, version)
91
- rescue Errors::HTTPNotFound
92
- nil
93
- end
94
-
95
- # @param [Ridley::Client] client
96
- # @param [String, #chef_id] object
97
- # @param [String] version
98
- #
99
- # @raise [Errors::HTTPNotFound]
100
- # if a resource with the given chef_id is not found
101
- #
102
- # @return [CookbookResource]
103
- def find!(client, object, version)
104
- chef_id = object.respond_to?(:chef_id) ? object.chef_id : object
105
- new(client, client.connection.get("#{self.resource_path}/#{chef_id}/#{version}").body)
106
- end
107
-
108
- # Return the latest version of the given cookbook found on the remote Chef server
109
- #
110
- # @param [Ridley::Client] client
111
- # @param [String] name
112
- #
113
- # @return [String, nil]
114
- def latest_version(client, name)
115
- ver = versions(client, name).collect do |version|
116
- Solve::Version.new(version)
117
- end.sort.last
118
-
119
- ver.nil? ? nil : ver.to_s
120
- end
121
-
122
- # Return the version of the given cookbook which best stasifies the given constraint
123
- #
124
- # @param [Ridley::Client] client
125
- # @param [String] name
126
- # name of the cookbook
127
- # @param [String, Solve::Constraint] constraint
128
- # constraint to solve for
129
- #
130
- # @return [CookbookResource, nil]
131
- # returns the cookbook resource for the best solution or nil if no solution exists
132
- def satisfy(client, name, constraint)
133
- version = Solve::Solver.satisfy_best(constraint, versions(client, name)).to_s
134
- find(client, name, version)
135
- rescue Solve::Errors::NoSolutionError
136
- nil
137
- end
138
-
139
- # Update or create a new Cookbook Version of the given name, version with the
140
- # given manifest of files and checksums.
141
- #
142
- # @param [Ridley::Client] client
143
- # @param [Ridley::Chef::Cookbook] cookbook
144
- # the cookbook to save
145
- #
146
- # @option options [Boolean] :force
147
- # Upload the Cookbook even if the version already exists and is frozen on
148
- # the target Chef Server
149
- # @option options [Boolean] :freeze
150
- # Freeze the uploaded Cookbook on the Chef Server so that it cannot be
151
- # overwritten
152
- #
153
- # @raise [Ridley::Errors::FrozenCookbook]
154
- # if a cookbook of the same name and version already exists on the remote Chef server
155
- # and is frozen. If the :force option is provided the given cookbook will be saved
156
- # regardless.
157
- #
158
- # @return [Hash]
159
- def update(client, cookbook, options = {})
160
- options.reverse_merge(force: false, freeze: false)
161
-
162
- cookbook.frozen = options[:freeze]
163
-
164
- url = "cookbooks/#{cookbook.cookbook_name}/#{cookbook.version}"
165
- url << "?force=true" if options[:force]
166
-
167
- client.connection.put(url, cookbook.to_json)
168
- rescue Ridley::Errors::HTTPConflict => ex
169
- raise Ridley::Errors::FrozenCookbook, ex
170
- end
171
- alias_method :create, :update
172
-
173
- # Uploads a cookbook to the remote Chef server from the contents of a filepath
174
- #
175
- # @param [Ridley::Client] client
176
- # @param [String] path
177
- # path to a cookbook on local disk
178
- #
179
- # @option options [String] :name
180
- # automatically populated by the metadata of the cookbook at the given path, but
181
- # in the event that the metadata does not contain a name it can be specified with
182
- # this option
183
- # @option options [Boolean] :force (false)
184
- # Upload the Cookbook even if the version already exists and is frozen on
185
- # the target Chef Server
186
- # @option options [Boolean] :freeze (false)
187
- # Freeze the uploaded Cookbook on the Chef Server so that it cannot be
188
- # overwritten
189
- # @option options [Boolean] :validate (true)
190
- # Validate the contents of the cookbook before uploading
191
- #
192
- # @return [Hash]
193
- def upload(client, path, options = {})
194
- options = options.reverse_merge(validate: true, force: false, freeze: false)
195
- cookbook = Ridley::Chef::Cookbook.from_path(path, options.slice(:name))
196
-
197
- unless (existing = find(client, cookbook.cookbook_name, cookbook.version)).nil?
198
- if existing.frozen? && options[:force] == false
199
- msg = "The cookbook #{cookbook.cookbook_name} (#{cookbook.version}) already exists and is"
200
- msg << " frozen on the Chef server. Use the 'force' option to override."
201
- raise Ridley::Errors::FrozenCookbook, msg
202
- end
203
- end
204
-
205
- if options[:validate]
206
- cookbook.validate
207
- end
208
-
209
- checksums = cookbook.checksums.dup
210
- sandbox = client.sandbox.create(checksums.keys)
211
-
212
- sandbox.upload(checksums)
213
- sandbox.commit
214
- update(client, cookbook, options.slice(:force, :freeze))
215
- end
4
+ set_resource_path "cookbooks"
5
+ represented_by Ridley::CookbookObject
216
6
 
217
- # Return a list of versions for the given cookbook present on the remote Chef server
218
- #
219
- # @param [Ridley::Client] client
220
- # @param [String] name
221
- #
222
- # @example
223
- # versions(client, "nginx") => [ "1.0.0", "1.2.0" ]
224
- #
225
- # @return [Array<String>]
226
- def versions(client, name)
227
- response = client.connection.get("#{self.resource_path}/#{name}").body
7
+ def initialize(connection_registry, client_name, client_key, options = {})
8
+ super(connection_registry)
9
+ # @sandbox_resource = SandboxResource.new_link(connection_registry, client_name, client_key, options)
10
+ end
228
11
 
229
- response[name]["versions"].collect do |cb_ver|
230
- cb_ver["version"]
12
+ # List all of the cookbooks and their versions present on the remote
13
+ #
14
+ # @example return value
15
+ # {
16
+ # "ant" => [
17
+ # "0.10.1"
18
+ # ],
19
+ # "apache2" => [
20
+ # "1.4.0"
21
+ # ]
22
+ # }
23
+ #
24
+ # @return [Hash]
25
+ # a hash containing keys which represent cookbook names and values which contain
26
+ # an array of strings representing the available versions
27
+ def all
28
+ response = connection.get(self.class.resource_path).body
29
+
30
+ {}.tap do |cookbooks|
31
+ response.each do |name, details|
32
+ cookbooks[name] = details["versions"].collect { |version| version["version"] }
231
33
  end
232
34
  end
233
35
  end
234
36
 
235
- include Ridley::Logging
236
-
237
- FILE_TYPES = [
238
- :resources,
239
- :providers,
240
- :recipes,
241
- :definitions,
242
- :libraries,
243
- :attributes,
244
- :files,
245
- :templates,
246
- :root_files
247
- ].freeze
248
-
249
- set_chef_id "name"
250
- set_chef_type "cookbook"
251
- set_chef_json_class "Chef::Cookbook"
252
- set_resource_path "cookbooks"
253
-
254
- attribute :name,
255
- required: true
256
-
257
- attribute :attributes,
258
- type: Array,
259
- default: Array.new
260
-
261
- attribute :cookbook_name,
262
- type: String
263
-
264
- attribute :definitions,
265
- type: Array,
266
- default: Array.new
267
-
268
- attribute :files,
269
- type: Array,
270
- default: Array.new
271
-
272
- attribute :libraries,
273
- type: Array,
274
- default: Array.new
275
-
276
- attribute :metadata,
277
- type: Hashie::Mash
278
-
279
- attribute :providers,
280
- type: Array,
281
- default: Array.new
282
-
283
- attribute :recipes,
284
- type: Array,
285
- default: Array.new
286
-
287
- attribute :resources,
288
- type: Array,
289
- default: Array.new
290
-
291
- attribute :root_files,
292
- type: Array,
293
- default: Array.new
294
-
295
- attribute :templates,
296
- type: Array,
297
- default: Array.new
298
-
299
- attribute :version,
300
- type: String
37
+ # Delete a cookbook of the given name and version on the remote Chef server
38
+ #
39
+ # @param [String] name
40
+ # @param [String] version
41
+ #
42
+ # @option options [Boolean] purge (false)
43
+ #
44
+ # @return [Boolean]
45
+ def delete(name, version, options = {})
46
+ options = options.reverse_merge(purge: false)
47
+ url = "#{self.class.resource_path}/#{name}/#{version}"
48
+ url += "?purge=true" if options[:purge]
49
+
50
+ connection.delete(url).body
51
+ true
52
+ rescue Errors::HTTPNotFound
53
+ true
54
+ end
301
55
 
302
- attribute :frozen?,
303
- type: Boolean
56
+ # Delete all of the versions of a given cookbook on the remote Chef server
57
+ #
58
+ # @param [String] name
59
+ # name of the cookbook to delete
60
+ #
61
+ # @option options [Boolean] purge (false)
62
+ def delete_all(name, options = {})
63
+ versions(name).each do |version|
64
+ future(:delete, name, version, options)
65
+ end.map(&:value)
66
+ end
304
67
 
305
68
  # Download the entire cookbook
306
69
  #
70
+ # @param [String] name
71
+ # @param [String] version
307
72
  # @param [String] destination (Dir.mktmpdir)
308
73
  # the place to download the cookbook too. If no value is provided the cookbook
309
74
  # will be downloaded to a temporary location
310
75
  #
311
76
  # @return [String]
312
77
  # the path to the directory the cookbook was downloaded to
313
- def download(destination = Dir.mktmpdir)
314
- destination = File.expand_path(destination)
315
- log.debug { "downloading cookbook: '#{name}'" }
316
-
317
- FILE_TYPES.each do |filetype|
318
- next unless manifest.has_key?(filetype)
78
+ def download(name, version, destination = Dir.mktmpdir)
79
+ cookbook = find(name, version)
319
80
 
320
- manifest[filetype].each do |file|
321
- file_destination = File.join(destination, file[:path].gsub('/', File::SEPARATOR))
322
- FileUtils.mkdir_p(File.dirname(file_destination))
323
- download_file(filetype, file[:path], file_destination)
324
- end
81
+ unless cookbook.nil?
82
+ cookbook.download(destination)
325
83
  end
84
+ end
326
85
 
327
- destination
86
+ # @param [String, #chef_id] object
87
+ # @param [String] version
88
+ #
89
+ # @return [nil, CookbookResource]
90
+ def find(object, version)
91
+ chef_id = object.respond_to?(:chef_id) ? object.chef_id : object
92
+ new(connection.get("#{self.class.resource_path}/#{chef_id}/#{version}").body)
93
+ rescue Errors::HTTPNotFound
94
+ nil
328
95
  end
329
96
 
330
- # Download a single file from a cookbook
97
+ # Return the latest version of the given cookbook found on the remote Chef server
331
98
  #
332
- # @param [#to_sym] filetype
333
- # the type of file to download. These are broken up into the following types in Chef:
334
- # - attribute (unsupported until resolved https://github.com/reset/chozo/issues/17)
335
- # - definition
336
- # - file
337
- # - library
338
- # - provider
339
- # - recipe
340
- # - resource
341
- # - root_file
342
- # - template
343
- # these types are where the files are stored in your cookbook's structure. For example, a
344
- # recipe would be stored in the recipes directory while a root_file is stored at the root
345
- # of your cookbook
346
- # @param [String] path
347
- # path of the file to download
348
- # @param [String] destination
349
- # where to download the file to
99
+ # @param [String] name
350
100
  #
351
- # @return [nil]
352
- def download_file(filetype, path, destination)
353
- download_fun(filetype).call(path, destination)
101
+ # @return [String, nil]
102
+ def latest_version(name)
103
+ ver = versions(name).collect do |version|
104
+ Solve::Version.new(version)
105
+ end.sort.last
106
+
107
+ ver.nil? ? nil : ver.to_s
354
108
  end
355
109
 
356
- # A hash containing keys for all of the different cookbook filetypes with values
357
- # representing each file of that type this cookbook contains
110
+ # Return the version of the given cookbook which best stasifies the given constraint
358
111
  #
359
- # @example
360
- # {
361
- # root_files: [
362
- # {
363
- # :name => "afile.rb",
364
- # :path => "files/ubuntu-9.10/afile.rb",
365
- # :checksum => "2222",
366
- # :specificity => "ubuntu-9.10"
367
- # },
368
- # ],
369
- # templates: [ manifest_record1, ... ],
370
- # ...
371
- # }
112
+ # @param [String] name
113
+ # name of the cookbook
114
+ # @param [String, Solve::Constraint] constraint
115
+ # constraint to solve for
372
116
  #
373
- # @return [Hash]
374
- def manifest
375
- {}.tap do |manifest|
376
- FILE_TYPES.each do |filetype|
377
- manifest[filetype] = get_attribute(filetype)
378
- end
379
- end
117
+ # @return [CookbookResource, nil]
118
+ # returns the cookbook resource for the best solution or nil if no solution exists
119
+ def satisfy(name, constraint)
120
+ version = Solve::Solver.satisfy_best(constraint, versions(name)).to_s
121
+ find(name, version)
122
+ rescue Solve::Errors::NoSolutionError
123
+ nil
380
124
  end
381
125
 
382
- def to_s
383
- "#{name}: #{manifest}"
384
- end
126
+ # Update or create a new Cookbook Version of the given name, version with the
127
+ # given manifest of files and checksums.
128
+ #
129
+ # @param [Ridley::Chef::Cookbook] cookbook
130
+ # the cookbook to save
131
+ #
132
+ # @option options [Boolean] :force
133
+ # Upload the Cookbook even if the version already exists and is frozen on
134
+ # the target Chef Server
135
+ # @option options [Boolean] :freeze
136
+ # Freeze the uploaded Cookbook on the Chef Server so that it cannot be
137
+ # overwritten
138
+ #
139
+ # @raise [Ridley::Errors::FrozenCookbook]
140
+ # if a cookbook of the same name and version already exists on the remote Chef server
141
+ # and is frozen. If the :force option is provided the given cookbook will be saved
142
+ # regardless.
143
+ #
144
+ # @return [Hash]
145
+ def update(cookbook, options = {})
146
+ options.reverse_merge(force: false, freeze: false)
385
147
 
386
- private
148
+ cookbook.frozen = options[:freeze]
149
+
150
+ url = "cookbooks/#{cookbook.cookbook_name}/#{cookbook.version}"
151
+ url << "?force=true" if options[:force]
387
152
 
388
- # Return a lambda for downloading a file from the cookbook of the given type
389
- #
390
- # @param [#to_sym] filetype
391
- #
392
- # @return [lambda]
393
- # a lambda which takes to parameters: target and path. Target is the URL to download from
394
- # and path is the location on disk to steam the contents of the remote URL to.
395
- def download_fun(filetype)
396
- collection = case filetype.to_sym
397
- when :attribute, :attributes; method(:attributes)
398
- when :definition, :definitions; method(:definitions)
399
- when :file, :files; method(:files)
400
- when :library, :libraries; method(:libraries)
401
- when :provider, :providers; method(:providers)
402
- when :recipe, :recipes; method(:recipes)
403
- when :resource, :resources; method(:resources)
404
- when :root_file, :root_files; method(:root_files)
405
- when :template, :templates; method(:templates)
406
- else
407
- raise Errors::UnknownCookbookFileType.new(filetype)
153
+ connection.put(url, cookbook.to_json)
154
+ rescue Ridley::Errors::HTTPConflict => ex
155
+ abort Ridley::Errors::FrozenCookbook.new(ex)
156
+ end
157
+ alias_method :create, :update
158
+
159
+ # Uploads a cookbook to the remote Chef server from the contents of a filepath
160
+ #
161
+ # @param [String] path
162
+ # path to a cookbook on local disk
163
+ #
164
+ # @option options [String] :name
165
+ # automatically populated by the metadata of the cookbook at the given path, but
166
+ # in the event that the metadata does not contain a name it can be specified with
167
+ # this option
168
+ # @option options [Boolean] :force (false)
169
+ # Upload the Cookbook even if the version already exists and is frozen on
170
+ # the target Chef Server
171
+ # @option options [Boolean] :freeze (false)
172
+ # Freeze the uploaded Cookbook on the Chef Server so that it cannot be
173
+ # overwritten
174
+ # @option options [Boolean] :validate (true)
175
+ # Validate the contents of the cookbook before uploading
176
+ #
177
+ # @return [Hash]
178
+ def upload(path, options = {})
179
+ options = options.reverse_merge(validate: true, force: false, freeze: false)
180
+ cookbook = Ridley::Chef::Cookbook.from_path(path, options.slice(:name))
181
+
182
+ unless (existing = find(cookbook.cookbook_name, cookbook.version)).nil?
183
+ if existing.frozen? && options[:force] == false
184
+ msg = "The cookbook #{cookbook.cookbook_name} (#{cookbook.version}) already exists and is"
185
+ msg << " frozen on the Chef server. Use the 'force' option to override."
186
+ abort Ridley::Errors::FrozenCookbook.new(msg)
408
187
  end
188
+ end
189
+
190
+ if options[:validate]
191
+ cookbook.validate
192
+ end
409
193
 
410
- ->(target, destination) {
411
- files = collection.call # JW: always chaining .call.find results in a nil value. WHY?
412
- file = files.find { |f| f[:path] == target }
413
- return nil if file.nil?
194
+ checksums = cookbook.checksums.dup
195
+ sandbox = sandbox_resource.create(checksums.keys)
414
196
 
415
- destination = File.expand_path(destination)
416
- log.debug { "downloading '#{filetype}' file: #{file} to: '#{destination}'" }
197
+ sandbox.upload(checksums)
198
+ sandbox.commit
199
+ update(cookbook, options.slice(:force, :freeze))
200
+ end
417
201
 
418
- client.connection.stream(file[:url], destination)
419
- }
202
+ # Return a list of versions for the given cookbook present on the remote Chef server
203
+ #
204
+ # @param [String] name
205
+ #
206
+ # @example
207
+ # versions("nginx") => [ "1.0.0", "1.2.0" ]
208
+ #
209
+ # @return [Array<String>]
210
+ def versions(name)
211
+ response = connection.get("#{self.class.resource_path}/#{name}").body
212
+
213
+ response[name]["versions"].collect do |cb_ver|
214
+ cb_ver["version"]
420
215
  end
216
+ end
217
+
218
+ private
219
+
220
+ attr_reader :sandbox_resource
421
221
  end
422
222
  end