chef 0.9.18 → 0.10.0.beta.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 (177) hide show
  1. data/README.rdoc +0 -3
  2. data/distro/arch/etc/rc.d/chef-server +0 -4
  3. data/distro/arch/etc/rc.d/chef-server-webui +0 -4
  4. data/distro/arch/etc/rc.d/chef-solr +0 -4
  5. data/distro/arch/etc/rc.d/chef-solr-indexer +0 -4
  6. data/lib/chef.rb +3 -3
  7. data/lib/chef/api_client.rb +1 -1
  8. data/lib/chef/application.rb +11 -1
  9. data/lib/chef/application/client.rb +18 -22
  10. data/lib/chef/application/knife.rb +28 -29
  11. data/lib/chef/application/solo.rb +14 -12
  12. data/lib/chef/client.rb +112 -54
  13. data/lib/chef/config.rb +4 -0
  14. data/lib/chef/cookbook/chefignore.rb +66 -0
  15. data/lib/chef/cookbook/cookbook_collection.rb +6 -5
  16. data/lib/chef/cookbook/cookbook_version_loader.rb +151 -0
  17. data/lib/chef/cookbook/file_system_file_vendor.rb +10 -8
  18. data/lib/chef/cookbook/metadata.rb +200 -108
  19. data/lib/chef/cookbook_loader.rb +39 -163
  20. data/lib/chef/cookbook_uploader.rb +100 -78
  21. data/lib/chef/cookbook_version.rb +92 -47
  22. data/lib/chef/cookbook_version_selector.rb +163 -0
  23. data/lib/chef/couchdb.rb +9 -1
  24. data/lib/chef/data_bag.rb +1 -1
  25. data/lib/chef/data_bag_item.rb +1 -1
  26. data/lib/chef/encrypted_data_bag_item.rb +126 -0
  27. data/lib/chef/environment.rb +386 -0
  28. data/lib/chef/exceptions.rb +82 -1
  29. data/lib/chef/index_queue/amqp_client.rb +15 -12
  30. data/lib/chef/index_queue/indexable.rb +38 -4
  31. data/lib/chef/json_compat.rb +3 -3
  32. data/lib/chef/knife.rb +97 -202
  33. data/lib/chef/knife/bootstrap.rb +27 -61
  34. data/lib/chef/knife/bootstrap/archlinux-gems.erb +4 -2
  35. data/lib/chef/knife/bootstrap/centos5-gems.erb +6 -15
  36. data/lib/chef/knife/bootstrap/fedora13-gems.erb +3 -4
  37. data/lib/chef/knife/bootstrap/ubuntu10.04-apt.erb +2 -2
  38. data/lib/chef/knife/bootstrap/ubuntu10.04-gems.erb +6 -5
  39. data/lib/chef/knife/client_bulk_delete.rb +6 -3
  40. data/lib/chef/knife/client_create.rb +13 -10
  41. data/lib/chef/knife/client_delete.rb +10 -7
  42. data/lib/chef/knife/client_edit.rb +9 -6
  43. data/lib/chef/knife/client_list.rb +8 -5
  44. data/lib/chef/knife/client_reregister.rb +9 -6
  45. data/lib/chef/knife/client_show.rb +9 -6
  46. data/lib/chef/knife/configure.rb +15 -19
  47. data/lib/chef/knife/configure_client.rb +4 -4
  48. data/lib/chef/knife/cookbook_bulk_delete.rb +11 -8
  49. data/lib/chef/knife/cookbook_create.rb +120 -55
  50. data/lib/chef/knife/cookbook_delete.rb +18 -12
  51. data/lib/chef/knife/cookbook_download.rb +10 -6
  52. data/lib/chef/knife/cookbook_list.rb +15 -6
  53. data/lib/chef/knife/cookbook_metadata.rb +41 -21
  54. data/lib/chef/knife/cookbook_metadata_from_file.rb +4 -0
  55. data/lib/chef/knife/cookbook_show.rb +16 -5
  56. data/lib/chef/knife/cookbook_site_download.rb +2 -2
  57. data/lib/chef/knife/cookbook_site_share.rb +18 -13
  58. data/lib/chef/knife/cookbook_site_unshare.rb +7 -4
  59. data/lib/chef/knife/cookbook_site_vendor.rb +21 -18
  60. data/lib/chef/knife/cookbook_test.rb +14 -14
  61. data/lib/chef/knife/cookbook_upload.rb +91 -40
  62. data/lib/chef/knife/data_bag_create.rb +41 -6
  63. data/lib/chef/knife/data_bag_delete.rb +5 -3
  64. data/lib/chef/knife/data_bag_edit.rb +55 -11
  65. data/lib/chef/knife/data_bag_from_file.rb +47 -7
  66. data/lib/chef/knife/data_bag_list.rb +4 -1
  67. data/lib/chef/knife/data_bag_show.rb +44 -4
  68. data/lib/chef/knife/environment_create.rb +53 -0
  69. data/lib/chef/knife/environment_delete.rb +45 -0
  70. data/lib/chef/knife/environment_edit.rb +45 -0
  71. data/lib/chef/knife/environment_from_file.rb +39 -0
  72. data/lib/chef/knife/environment_list.rb +42 -0
  73. data/lib/chef/knife/environment_show.rb +46 -0
  74. data/lib/chef/knife/exec.rb +1 -1
  75. data/lib/chef/knife/index_rebuild.rb +8 -9
  76. data/lib/chef/knife/node_bulk_delete.rb +9 -6
  77. data/lib/chef/knife/node_create.rb +9 -6
  78. data/lib/chef/knife/node_delete.rb +10 -7
  79. data/lib/chef/knife/node_edit.rb +129 -10
  80. data/lib/chef/knife/node_from_file.rb +10 -7
  81. data/lib/chef/knife/node_list.rb +11 -6
  82. data/lib/chef/knife/node_run_list_add.rb +10 -7
  83. data/lib/chef/knife/node_run_list_remove.rb +9 -6
  84. data/lib/chef/knife/node_show.rb +15 -7
  85. data/lib/chef/knife/recipe_list.rb +4 -3
  86. data/lib/chef/knife/role_bulk_delete.rb +9 -6
  87. data/lib/chef/knife/role_create.rb +9 -6
  88. data/lib/chef/knife/role_delete.rb +10 -7
  89. data/lib/chef/knife/role_edit.rb +11 -8
  90. data/lib/chef/knife/role_from_file.rb +10 -7
  91. data/lib/chef/knife/role_list.rb +8 -5
  92. data/lib/chef/knife/role_show.rb +11 -8
  93. data/lib/chef/knife/search.rb +33 -10
  94. data/lib/chef/knife/ssh.rb +33 -61
  95. data/lib/chef/knife/status.rb +7 -4
  96. data/lib/chef/knife/subcommand_loader.rb +101 -0
  97. data/lib/chef/knife/tag_create.rb +31 -0
  98. data/lib/chef/knife/tag_delete.rb +31 -0
  99. data/lib/chef/knife/tag_list.rb +29 -0
  100. data/lib/chef/knife/ui.rb +229 -0
  101. data/lib/chef/knife/windows_bootstrap.rb +8 -5
  102. data/lib/chef/log.rb +5 -59
  103. data/lib/chef/mash.rb +211 -0
  104. data/lib/chef/mixins.rb +1 -2
  105. data/lib/chef/nil_argument.rb +3 -0
  106. data/lib/chef/node.rb +96 -34
  107. data/lib/chef/platform.rb +27 -0
  108. data/lib/chef/provider/cookbook_file.rb +21 -20
  109. data/lib/chef/provider/deploy/revision.rb +3 -0
  110. data/lib/chef/provider/file.rb +20 -11
  111. data/lib/chef/provider/git.rb +26 -26
  112. data/lib/chef/provider/group/aix.rb +70 -0
  113. data/lib/chef/provider/group/groupadd.rb +7 -4
  114. data/lib/chef/provider/group/usermod.rb +1 -1
  115. data/lib/chef/provider/package.rb +28 -28
  116. data/lib/chef/provider/package/dpkg.rb +1 -1
  117. data/lib/chef/provider/package/portage.rb +50 -39
  118. data/lib/chef/provider/package/rubygems.rb +1 -1
  119. data/lib/chef/provider/package/zypper.rb +3 -20
  120. data/lib/chef/provider/remote_directory.rb +0 -2
  121. data/lib/chef/provider/remote_file.rb +2 -3
  122. data/lib/chef/provider/service/arch.rb +28 -35
  123. data/lib/chef/provider/service/simple.rb +1 -1
  124. data/lib/chef/provider/subversion.rb +22 -22
  125. data/lib/chef/providers.rb +1 -0
  126. data/lib/chef/recipe.rb +10 -12
  127. data/lib/chef/resource.rb +49 -42
  128. data/lib/chef/resource/gem_package.rb +7 -3
  129. data/lib/chef/resource/git.rb +5 -5
  130. data/lib/chef/resource/package.rb +7 -7
  131. data/lib/chef/resource/scm.rb +2 -1
  132. data/lib/chef/resource/solaris_package.rb +0 -1
  133. data/lib/chef/resource/yum_package.rb +0 -1
  134. data/lib/chef/rest.rb +7 -16
  135. data/lib/chef/rest/rest_request.rb +0 -16
  136. data/lib/chef/role.rb +67 -13
  137. data/lib/chef/run_context.rb +37 -21
  138. data/lib/chef/run_list.rb +30 -15
  139. data/lib/chef/run_list/run_list_expansion.rb +41 -20
  140. data/lib/chef/run_list/run_list_item.rb +20 -6
  141. data/lib/chef/run_list/versioned_recipe_list.rb +68 -0
  142. data/lib/chef/runner.rb +7 -15
  143. data/lib/chef/search/query.rb +12 -7
  144. data/lib/chef/shef.rb +6 -7
  145. data/lib/chef/shef/shef_session.rb +40 -35
  146. data/lib/chef/shell_out.rb +22 -201
  147. data/lib/chef/shell_out/unix.rb +224 -0
  148. data/lib/chef/shell_out/windows.rb +95 -0
  149. data/lib/chef/solr_query.rb +187 -0
  150. data/lib/chef/solr_query/lucene.treetop +145 -0
  151. data/lib/chef/solr_query/lucene_nodes.rb +285 -0
  152. data/lib/chef/solr_query/query_transform.rb +65 -0
  153. data/lib/chef/solr_query/solr_http_request.rb +118 -0
  154. data/lib/chef/version.rb +4 -2
  155. data/lib/chef/version_class.rb +70 -0
  156. data/lib/chef/version_constraint.rb +116 -0
  157. metadata +68 -37
  158. data/lib/chef/cookbook/metadata/version.rb +0 -87
  159. data/lib/chef/knife/bluebox_images_list.rb +0 -54
  160. data/lib/chef/knife/bluebox_server_create.rb +0 -157
  161. data/lib/chef/knife/bluebox_server_delete.rb +0 -63
  162. data/lib/chef/knife/bluebox_server_list.rb +0 -59
  163. data/lib/chef/knife/ec2_instance_data.rb +0 -46
  164. data/lib/chef/knife/ec2_server_create.rb +0 -218
  165. data/lib/chef/knife/ec2_server_delete.rb +0 -87
  166. data/lib/chef/knife/ec2_server_list.rb +0 -89
  167. data/lib/chef/knife/rackspace_server_create.rb +0 -184
  168. data/lib/chef/knife/rackspace_server_delete.rb +0 -57
  169. data/lib/chef/knife/rackspace_server_list.rb +0 -59
  170. data/lib/chef/knife/slicehost_images_list.rb +0 -53
  171. data/lib/chef/knife/slicehost_server_create.rb +0 -103
  172. data/lib/chef/knife/slicehost_server_delete.rb +0 -61
  173. data/lib/chef/knife/slicehost_server_list.rb +0 -64
  174. data/lib/chef/knife/terremark_server_create.rb +0 -152
  175. data/lib/chef/knife/terremark_server_delete.rb +0 -87
  176. data/lib/chef/knife/terremark_server_list.rb +0 -77
  177. data/lib/chef/mixin/find_preferred_file.rb +0 -92
@@ -1,17 +1,17 @@
1
- #
2
1
  # Author:: Adam Jacob (<adam@opscode.com>)
3
2
  # Author:: Nuo Yan (<nuo@opscode.com>)
4
3
  # Author:: Christopher Walters (<cw@opscode.com>)
5
4
  # Author:: Tim Hinderliter (<tim@opscode.com>)
6
- # Copyright:: Copyright (c) 2008-2010 Opscode, Inc.
5
+ # Author:: Seth Falcon (<seth@opscode.com>)
6
+ # Copyright:: Copyright 2008-2010 Opscode, Inc.
7
7
  # License:: Apache License, Version 2.0
8
8
  #
9
9
  # Licensed under the Apache License, Version 2.0 (the "License");
10
10
  # you may not use this file except in compliance with the License.
11
11
  # You may obtain a copy of the License at
12
- #
12
+ #
13
13
  # http://www.apache.org/licenses/LICENSE-2.0
14
- #
14
+ #
15
15
  # Unless required by applicable law or agreed to in writing, software
16
16
  # distributed under the License is distributed on an "AS IS" BASIS,
17
17
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -25,6 +25,8 @@ require 'chef/resource_definition_list'
25
25
  require 'chef/recipe'
26
26
  require 'chef/cookbook/file_vendor'
27
27
  require 'chef/checksum'
28
+ require 'chef/cookbook/metadata'
29
+ require 'chef/version_class'
28
30
 
29
31
  class Chef
30
32
  # == Chef::CookbookVersion
@@ -37,16 +39,17 @@ class Chef
37
39
  # recipe_filenames.insert) should dirty the manifest so it gets regenerated.
38
40
  class CookbookVersion
39
41
  include Chef::IndexQueue::Indexable
42
+ include Comparable
40
43
 
41
44
  COOKBOOK_SEGMENTS = [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates, :root_files ]
42
-
45
+
43
46
  DESIGN_DOCUMENT = {
44
47
  "version" => 7,
45
48
  "language" => "javascript",
46
49
  "views" => {
47
50
  "all" => {
48
51
  "map" => <<-EOJS
49
- function(doc) {
52
+ function(doc) {
50
53
  if (doc.chef_type == "cookbook_version") {
51
54
  emit(doc.name, doc);
52
55
  }
@@ -55,7 +58,7 @@ class Chef
55
58
  },
56
59
  "all_id" => {
57
60
  "map" => <<-EOJS
58
- function(doc) {
61
+ function(doc) {
59
62
  if (doc.chef_type == "cookbook_version") {
60
63
  emit(doc.name, doc.name);
61
64
  }
@@ -64,7 +67,7 @@ class Chef
64
67
  },
65
68
  "all_with_version" => {
66
69
  "map" => <<-EOJS
67
- function(doc) {
70
+ function(doc) {
68
71
  if (doc.chef_type == "cookbook_version") {
69
72
  emit(doc.cookbook_name, doc.version);
70
73
  }
@@ -73,7 +76,7 @@ class Chef
73
76
  },
74
77
  "all_latest_version" => {
75
78
  "map" => %q@
76
- function(doc) {
79
+ function(doc) {
77
80
  if (doc.chef_type == "cookbook_version") {
78
81
  emit(doc.cookbook_name, doc.version);
79
82
  }
@@ -85,12 +88,12 @@ class Chef
85
88
 
86
89
  for (var idx in values) {
87
90
  var value = values[idx];
88
-
91
+
89
92
  if (idx == 0) {
90
93
  result = value;
91
94
  continue;
92
95
  }
93
-
96
+
94
97
  var valueParts = value.split('.').map(function(v) { return parseInt(v); });
95
98
  var resultParts = result.split('.').map(function(v) { return parseInt(v); });
96
99
 
@@ -187,7 +190,7 @@ class Chef
187
190
 
188
191
  attr_reader :recipe_filenames_by_name
189
192
  attr_reader :attribute_filenames_by_short_filename
190
-
193
+
191
194
  # This is the one and only method that knows how cookbook files'
192
195
  # checksums are generated.
193
196
  def self.checksum_cookbook_file(filepath)
@@ -196,7 +199,7 @@ class Chef
196
199
  Chef::Log.debug("File #{filepath} does not exist, so there is no checksum to generate")
197
200
  nil
198
201
  end
199
-
202
+
200
203
  # Keep track of the filenames that we use in both eager cookbook
201
204
  # downloading (during sync_cookbooks) and lazy (during the run
202
205
  # itself, through FileVendor). After the run is over, clean up the
@@ -224,7 +227,8 @@ class Chef
224
227
  # === Returns
225
228
  # true:: Always returns true
226
229
  def self.sync_cookbooks(cookbook_hash)
227
- Chef::Log.debug("Cookbooks to load: #{cookbook_hash.inspect}")
230
+ Chef::Log.info("Loading cookbooks [#{cookbook_hash.keys.sort.join(', ')}]")
231
+ Chef::Log.debug("Cookbooks detail: #{cookbook_hash.inspect}")
228
232
 
229
233
  clear_obsoleted_cookbooks(cookbook_hash)
230
234
 
@@ -317,7 +321,7 @@ class Chef
317
321
  # manifest.
318
322
  cache.find(File.join(%w{cookbooks ** *})).each do |cache_filename|
319
323
  unless valid_cache_entries[cache_filename]
320
- Chef::Log.info("Removing #{cache_filename} from the cache; it is no longer on the server.")
324
+ Chef::Log.info("Removing #{cache_filename} from the cache; it is no longer needed by chef-client.")
321
325
  cache.delete(cache_filename)
322
326
  end
323
327
  end
@@ -329,12 +333,13 @@ class Chef
329
333
  cleanup_file_cache
330
334
  end
331
335
 
332
- # Creates a new Chef::CookbookVersion object.
336
+ # Creates a new Chef::CookbookVersion object.
333
337
  #
334
338
  # === Returns
335
339
  # object<Chef::CookbookVersion>:: Duh. :)
336
340
  def initialize(name, couchdb=nil)
337
341
  @name = name
342
+ @frozen = false
338
343
  @attribute_filenames = Array.new
339
344
  @definition_filenames = Array.new
340
345
  @template_filenames = Array.new
@@ -359,7 +364,18 @@ class Chef
359
364
  def version
360
365
  metadata.version
361
366
  end
362
-
367
+
368
+ # Indicates if this version is frozen or not. Freezing a coobkook version
369
+ # indicates that a new cookbook with the same name and version number
370
+ # shoule
371
+ def frozen_version?
372
+ @frozen
373
+ end
374
+
375
+ def freeze_version
376
+ @frozen = true
377
+ end
378
+
363
379
  def version=(new_version)
364
380
  manifest["version"] = new_version
365
381
  metadata.version(new_version)
@@ -375,7 +391,7 @@ class Chef
375
391
  # :version = "1.0",
376
392
  # :name = "Apache 2"
377
393
  # :metadata = ???TODO: timh/cw: 5-24-2010: describe this format,
378
- #
394
+ #
379
395
  # :files => [
380
396
  # {
381
397
  # :name => "afile.rb",
@@ -393,7 +409,7 @@ class Chef
393
409
  end
394
410
  @manifest
395
411
  end
396
-
412
+
397
413
  def manifest=(new_manifest)
398
414
  @manifest = Mash.new new_manifest
399
415
  @checksums = extract_checksums_from_manifest(@manifest)
@@ -402,7 +418,7 @@ class Chef
402
418
  COOKBOOK_SEGMENTS.each do |segment|
403
419
  next unless @manifest.has_key?(segment)
404
420
  filenames = @manifest[segment].map{|manifest_record| manifest_record['name']}
405
-
421
+
406
422
  if segment == :recipes
407
423
  self.recipe_filenames = filenames
408
424
  elsif segment == :attributes
@@ -413,7 +429,7 @@ class Chef
413
429
  end
414
430
  end
415
431
  end
416
-
432
+
417
433
  # Returns a hash of checksums to either nil or the on disk path (which is
418
434
  # done by generate_manifest).
419
435
  def checksums
@@ -426,17 +442,17 @@ class Chef
426
442
  def full_name
427
443
  "#{name}-#{version}"
428
444
  end
429
-
445
+
430
446
  def attribute_filenames=(*filenames)
431
447
  @attribute_filenames = filenames.flatten
432
448
  @attribute_filenames_by_short_filename = filenames_by_name(attribute_filenames)
433
449
  attribute_filenames
434
450
  end
435
-
451
+
436
452
  ## BACKCOMPAT/DEPRECATED - Remove these and fix breakage before release [DAN - 5/20/2010]##
437
453
  alias :attribute_files :attribute_filenames
438
454
  alias :attribute_files= :attribute_filenames=
439
-
455
+
440
456
  # Return recipe names in the form of cookbook_name::recipe_name
441
457
  def fully_qualified_recipe_names
442
458
  results = Array.new
@@ -445,17 +461,17 @@ class Chef
445
461
  end
446
462
  results
447
463
  end
448
-
464
+
449
465
  def recipe_filenames=(*filenames)
450
466
  @recipe_filenames = filenames.flatten
451
467
  @recipe_filenames_by_name = filenames_by_name(recipe_filenames)
452
468
  recipe_filenames
453
469
  end
454
-
470
+
455
471
  ## BACKCOMPAT/DEPRECATED - Remove these and fix breakage before release [DAN - 5/20/2010]##
456
472
  alias :recipe_files :recipe_filenames
457
473
  alias :recipe_files= :recipe_filenames=
458
-
474
+
459
475
  # called from DSL
460
476
  def load_recipe(recipe_name, run_context)
461
477
  unless recipe_filenames_by_name.has_key?(recipe_name)
@@ -469,7 +485,7 @@ class Chef
469
485
  unless recipe_filename
470
486
  raise Chef::Exceptions::RecipeNotFound, "could not find recipe #{recipe_name} for cookbook #{name}"
471
487
  end
472
-
488
+
473
489
  recipe.from_file(recipe_filename)
474
490
  recipe
475
491
  end
@@ -519,7 +535,7 @@ class Chef
519
535
  # ensure that we generate the manifest, which will also generate
520
536
  # @manifest_records_by_path
521
537
  manifest
522
-
538
+
523
539
  # in order of prefernce, look for the filename in the manifest
524
540
  found_pref = preferences.find {|preferred_filename| @manifest_records_by_path[preferred_filename] }
525
541
  if found_pref
@@ -528,7 +544,7 @@ class Chef
528
544
  raise Chef::Exceptions::FileNotFound, "cookbook #{name} does not contain file #{segment}/#{filename}"
529
545
  end
530
546
  end
531
-
547
+
532
548
  def preferred_filename_on_disk_location(node, segment, filename, current_filepath=nil)
533
549
  manifest_record = preferred_manifest_record(node, segment, filename)
534
550
  if current_filepath && (manifest_record['checksum'] == self.class.checksum_cookbook_file(current_filepath))
@@ -594,7 +610,7 @@ class Chef
594
610
  # preferences_for_path returns. It could be
595
611
  # "files/ubuntu-9.10/dirname", for example.
596
612
  specificity_dirname = $1
597
-
613
+
598
614
  # Record the specificity_dirname only if it's in the list of
599
615
  # valid preferences
600
616
  if records_by_pref[specificity_dirname]
@@ -602,9 +618,9 @@ class Chef
602
618
  end
603
619
  end
604
620
  end
605
-
621
+
606
622
  best_pref = preferences.find { |pref| !records_by_pref[pref].empty? }
607
-
623
+
608
624
  raise Chef::Exceptions::FileNotFound, "cookbook #{name} has no directory #{segment}/#{dirname}" unless best_pref
609
625
 
610
626
  records_by_pref[best_pref]
@@ -628,7 +644,7 @@ class Chef
628
644
  raise
629
645
  end
630
646
  end
631
-
647
+
632
648
  fqdn = node[:fqdn]
633
649
 
634
650
  # Most specific to least specific places to find the path
@@ -646,6 +662,7 @@ class Chef
646
662
 
647
663
  def to_hash
648
664
  result = manifest.dup
665
+ result['frozen?'] = frozen_version?
649
666
  result['chef_type'] = 'cookbook_version'
650
667
  result["_rev"] = couchdb_rev if couchdb_rev
651
668
  result.to_hash
@@ -668,12 +685,17 @@ class Chef
668
685
  cookbook_version.index_id = cookbook_version.couchdb_id
669
686
  o.delete("_id")
670
687
  end
671
- cookbook_version.manifest = o
672
688
  # We want the Chef::Cookbook::Metadata object to always be inflated
673
689
  cookbook_version.metadata = Chef::Cookbook::Metadata.from_hash(o["metadata"])
690
+ cookbook_version.manifest = o
691
+
692
+ # We don't need the following step when we decide to stop supporting deprecated operators in the metadata (e.g. <<, >>)
693
+ cookbook_version.manifest["metadata"] = JSON.parse(cookbook_version.metadata.to_json)
694
+
695
+ cookbook_version.freeze_version if o["frozen?"]
674
696
  cookbook_version
675
697
  end
676
-
698
+
677
699
  def generate_manifest_with_urls(&url_generator)
678
700
  rendered_manifest = manifest.dup
679
701
  COOKBOOK_SEGMENTS.each do |segment|
@@ -712,12 +734,23 @@ class Chef
712
734
  self.class.chef_server_rest
713
735
  end
714
736
 
737
+ # Save this object to the server via the REST api. If there is an existing
738
+ # document on the server and it is marked frozen, a
739
+ # Net::HTTPServerException will be raised for 409 Conflict.
715
740
  def save
716
741
  chef_server_rest.put_rest("cookbooks/#{name}/#{version}", self)
717
742
  self
718
743
  end
719
744
  alias :create :save
720
745
 
746
+ # Adds the `force=true` parameter to the upload. This allows the user to
747
+ # overwrite a frozen cookbook (normal #save raises a
748
+ # Net::HTTPServerException for 409 Conflict in this case).
749
+ def force_save
750
+ chef_server_rest.put_rest("cookbooks/#{name}/#{version}?force=true", self)
751
+ self
752
+ end
753
+
721
754
  def destroy
722
755
  chef_server_rest.delete_rest("cookbooks/#{name}/#{version}")
723
756
  self
@@ -757,7 +790,7 @@ class Chef
757
790
  ##
758
791
  # Couchdb
759
792
  ##
760
-
793
+
761
794
  def self.cdb_by_name(cookbook_name, couchdb=nil)
762
795
  cdb = (couchdb || Chef::CouchDB.new)
763
796
  options = { :startkey => cookbook_name, :endkey => cookbook_name }
@@ -787,9 +820,13 @@ class Chef
787
820
  end
788
821
 
789
822
  def self.cdb_list(inflate=false, couchdb=nil)
790
- rs = (couchdb || Chef::CouchDB.new).list("cookbooks", inflate)
791
- lookup = (inflate ? "value" : "key")
792
- rs["rows"].collect { |r| r[lookup] }
823
+ couchdb ||= Chef::CouchDB.new
824
+ if inflate
825
+ couchdb.list("cookbooks", true)["rows"].collect{|r| r["value"]}
826
+ else
827
+ # If you modify this, please make sure the desc sorted order on the versions doesn't get broken.
828
+ couchdb.get_view("cookbooks", "all_with_version")["rows"].inject({}) { |mapped, row| mapped[row["key"]]||=Array.new; mapped[row["key"]].push(Chef::Version.new(row["value"])); mapped[row["key"]].sort!.reverse!; mapped}
829
+ end
793
830
  end
794
831
 
795
832
  def self.cdb_load(name, version='latest', couchdb=nil)
@@ -807,7 +844,7 @@ class Chef
807
844
  end
808
845
 
809
846
  # Runs on Chef Server (API); deletes the cookbook from couchdb and also destroys associated
810
- # checksum documents
847
+ # checksum documents
811
848
  def purge
812
849
  checksums.keys.each do |checksum|
813
850
  begin
@@ -827,8 +864,16 @@ class Chef
827
864
  @index_id = value
828
865
  end
829
866
 
867
+ def <=>(o)
868
+ raise Chef::Exceptions::CookbookVersionNameMismatch if self.name != o.name
869
+ # FIXME: can we change the interface to the Metadata class such
870
+ # that metadata.version returns a Chef::Version instance instead
871
+ # of a string?
872
+ Chef::Version.new(self.version) <=> Chef::Version.new(o.version)
873
+ end
874
+
830
875
  private
831
-
876
+
832
877
  # For each filename, produce a mapping of base filename (i.e. recipe name
833
878
  # or attribute file) to on disk location
834
879
  def filenames_by_name(filenames)
@@ -858,7 +903,7 @@ class Chef
858
903
  file_name = nil
859
904
  path = nil
860
905
  specificity = "default"
861
-
906
+
862
907
  if segment == :root_files
863
908
  matcher = segment_file.match(".+/#{Regexp.escape(name.to_s)}/(.+)")
864
909
  file_name = matcher[1]
@@ -877,7 +922,7 @@ class Chef
877
922
  path = matcher[1]
878
923
  file_name = matcher[2]
879
924
  end
880
-
925
+
881
926
  csum = self.class.checksum_cookbook_file(segment_file)
882
927
  checksums_to_on_disk_paths[csum] = segment_file
883
928
  rs = Mash.new({
@@ -900,7 +945,7 @@ class Chef
900
945
  @manifest = manifest
901
946
  @manifest_records_by_path = extract_manifest_records_by_path(manifest)
902
947
  end
903
-
948
+
904
949
  def file_vendor
905
950
  unless @file_vendor
906
951
  @file_vendor = Chef::Cookbook::FileVendor.create_from_manifest(manifest)
@@ -918,7 +963,7 @@ class Chef
918
963
  end
919
964
  checksums
920
965
  end
921
-
966
+
922
967
  def extract_manifest_records_by_path(manifest)
923
968
  manifest_records_by_path = {}
924
969
  COOKBOOK_SEGMENTS.each do |segment|
@@ -929,6 +974,6 @@ class Chef
929
974
  end
930
975
  manifest_records_by_path
931
976
  end
932
-
977
+
933
978
  end
934
979
  end
@@ -0,0 +1,163 @@
1
+ #
2
+ # Author:: Tim Hinderliter (<tim@opscode.com>)
3
+ # Copyright:: Copyright (c) 2011 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require 'dep_selector'
19
+
20
+ class Chef
21
+ module CookbookVersionSelector
22
+ # This method replaces verbiage from DepSelector messages with
23
+ # Chef-domain-specific verbiage, such as replacing package with
24
+ # cookbook.
25
+ #
26
+ # TODO [cw, 2011/2/25]: this is a near-term hack. In the long run,
27
+ # we'll do this better.
28
+ def self.filter_dep_selector_message(message)
29
+ m = message
30
+ m.gsub!("Package", "Cookbook")
31
+ m.gsub!("package", "cookbook")
32
+ m.gsub!("Solution constraint", "Run list item")
33
+ m.gsub!("solution constraint", "run list item")
34
+ m
35
+ end
36
+
37
+ # all_cookbooks - a hash mapping cookbook names to an array of
38
+ # available CookbookVersions.
39
+ #
40
+ # Creates a DependencyGraph from CookbookVersion objects
41
+ def self.create_dependency_graph_from_cookbooks(all_cookbooks)
42
+ dep_graph = DepSelector::DependencyGraph.new
43
+
44
+ all_cookbooks.each do |cb_name, cb_versions|
45
+ cb_versions.each do |cb_version|
46
+ cb_version_deps = cb_version.metadata.dependencies
47
+ # TODO [cw. 2011/2/10]: CookbookVersion#version returns a
48
+ # String even though we're storing as a DepSelector::Version
49
+ # object underneath. This should be changed so that we
50
+ # return the object and handle proper serialization and
51
+ # de-serialization. For now, I'm just going to create a
52
+ # Version object from the String representation.
53
+ pv = dep_graph.package(cb_name).add_version(Chef::Version.new(cb_version.version))
54
+ cb_version_deps.each_pair do |dep_name, constraint_str|
55
+ # if the dependency is specified as cookbook::recipe,
56
+ # extract the cookbook component
57
+ dep_cb_name = dep_name.split("::").first
58
+ constraint = Chef::VersionConstraint.new(constraint_str)
59
+ pv.dependencies << DepSelector::Dependency.new(dep_graph.package(dep_cb_name), constraint)
60
+ end
61
+ end
62
+ end
63
+
64
+ dep_graph
65
+ end
66
+
67
+ # Return a hash mapping cookbook names to a CookbookVersion
68
+ # object. If there is no solution that satisfies the constraints,
69
+ # the first run list item that caused unsatisfiability is
70
+ # returned.
71
+ #
72
+ # This is the final version-resolved list of cookbooks for the
73
+ # RunList.
74
+ #
75
+ # all_cookbooks - a hash mapping cookbook names to an array of
76
+ # available CookbookVersions.
77
+ #
78
+ # recipe_constraints - an array of hashes describing the expanded
79
+ # run list. Each element is a hash containing keys :name and
80
+ # :version_constraint. The :name component is either the
81
+ # fully-qualified recipe name (e.g. "cookbook1::non_default_recipe")
82
+ # or just a cookbook name, indicating the default recipe is to be
83
+ # run (e.g. "cookbook1").
84
+ def self.constrain(all_cookbooks, recipe_constraints)
85
+ dep_graph = create_dependency_graph_from_cookbooks(all_cookbooks)
86
+
87
+ # extract cookbook names from (possibly) fully-qualified recipe names
88
+ cookbook_constraints = recipe_constraints.map do |recipe_spec|
89
+ cookbook_name = (recipe_spec[:name][/^(.+)::/, 1] || recipe_spec[:name])
90
+ DepSelector::SolutionConstraint.new(dep_graph.package(cookbook_name),
91
+ recipe_spec[:version_constraint])
92
+ end
93
+
94
+ # Pass in the list of all available cookbooks (packages) so that
95
+ # DepSelector can distinguish between "no version available for
96
+ # cookbook X" and "no such cookbook X" when an environment
97
+ # filters out all versions for a given cookbook.
98
+ all_packages = all_cookbooks.inject([]) do |acc, (cookbook_name, cookbook_versions)|
99
+ acc << dep_graph.package(cookbook_name)
100
+ acc
101
+ end
102
+
103
+ # find a valid assignment of CoookbookVersions. If no valid
104
+ # assignment exists, indicate which run_list_item causes the
105
+ # unsatisfiability and try to hint at what might be wrong.
106
+ soln =
107
+ begin
108
+ DepSelector::Selector.new(dep_graph).find_solution(cookbook_constraints, all_packages)
109
+ rescue DepSelector::Exceptions::InvalidSolutionConstraints => e
110
+ non_existent_cookbooks = e.non_existent_packages.map {|constraint| constraint.package.name}
111
+ cookbooks_with_no_matching_versions = e.constrained_to_no_versions.map {|constraint| constraint.package.name}
112
+
113
+ # Spend a whole lot of effort for pluralizing and
114
+ # prettifying the message.
115
+ message = ""
116
+ if non_existent_cookbooks.length > 0
117
+ message += "no such " + (non_existent_cookbooks.length > 1 ? "cookbooks" : "cookbook")
118
+ message += " #{non_existent_cookbooks.join(", ")}"
119
+ end
120
+
121
+ if cookbooks_with_no_matching_versions.length > 0
122
+ if message.length > 0
123
+ message += "; "
124
+ end
125
+
126
+ message += "no versions match the constraints on " + (cookbooks_with_no_matching_versions.length > 1 ? "cookbooks" : "cookbook")
127
+ message += " #{cookbooks_with_no_matching_versions.join(", ")}"
128
+ end
129
+
130
+ message = "Run list contains invalid items: #{message}."
131
+
132
+ raise Chef::Exceptions::CookbookVersionSelection::InvalidRunListItems.new(message, non_existent_cookbooks, cookbooks_with_no_matching_versions)
133
+ rescue DepSelector::Exceptions::NoSolutionExists => e
134
+ raise Chef::Exceptions::CookbookVersionSelection::UnsatisfiableRunListItem.new(filter_dep_selector_message(e.message), e.unsatisfiable_solution_constraint, e.disabled_non_existent_packages, e.disabled_most_constrained_packages)
135
+ end
136
+
137
+
138
+ # map assignment back to CookbookVersion objects
139
+ selected_cookbooks = {}
140
+ soln.each_pair do |cb_name, cb_version|
141
+ # TODO [cw, 2011/2/10]: related to the TODO in
142
+ # create_dependency_graph_from_cookbooks, cbv.version
143
+ # currently returns a String, so we must compare to
144
+ # cb_version.to_s, since it's a for-real Version object.
145
+ selected_cookbooks[cb_name] = all_cookbooks[cb_name].find{|cbv| cbv.version == cb_version.to_s}
146
+ end
147
+ selected_cookbooks
148
+ end
149
+
150
+ # Expands the run_list, constrained to the environment's CookbookVersion
151
+ # constraints.
152
+ #
153
+ # Returns:
154
+ # Hash of: name to CookbookVersion
155
+ def self.expand_to_cookbook_versions(run_list, environment, couchdb=nil)
156
+ # expand any roles in this run_list.
157
+ expanded_run_list = run_list.expand(environment, 'couchdb', :couchdb => couchdb).recipes.with_version_constraints
158
+
159
+ cookbooks_for_environment = Chef::Environment.cdb_load_filtered_cookbook_versions(environment, couchdb)
160
+ constrain(cookbooks_for_environment, expanded_run_list)
161
+ end
162
+ end
163
+ end