chef 0.9.18 → 0.10.0.beta.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -7,9 +7,9 @@
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
8
8
  # you may not use this file except in compliance with the License.
9
9
  # You may obtain a copy of the License at
10
- #
10
+ #
11
11
  # http://www.apache.org/licenses/LICENSE-2.0
12
- #
12
+ #
13
13
  # Unless required by applicable law or agreed to in writing, software
14
14
  # distributed under the License is distributed on an "AS IS" BASIS,
15
15
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -30,25 +30,27 @@ class Chef
30
30
  # locations, since in the chef-client case, that information is
31
31
  # non-sensical.
32
32
  class FileSystemFileVendor < FileVendor
33
-
34
- def initialize(manifest)
33
+
34
+ def initialize(manifest, *repo_paths)
35
35
  @cookbook_name = manifest[:cookbook_name]
36
+ @repo_paths = repo_paths.flatten
37
+ raise ArgumentError, "You must specify at least one repo path" if @repo_paths.empty?
36
38
  end
37
-
39
+
38
40
  # Implements abstract base's requirement. It looks in the
39
41
  # Chef::Config.cookbook_path file hierarchy for the requested
40
42
  # file.
41
43
  def get_filename(filename)
42
- location = Array(Chef::Config.cookbook_path).inject(nil) do |memo, basepath|
44
+ location = @repo_paths.inject(nil) do |memo, basepath|
43
45
  candidate_location = File.join(basepath, @cookbook_name, filename)
44
46
  memo = candidate_location if File.exist?(candidate_location)
45
47
  memo
46
48
  end
47
49
  raise "File #{filename} does not exist for cookbook #{@cookbook_name}" unless location
48
-
50
+
49
51
  location
50
52
  end
51
-
53
+
52
54
  end
53
55
  end
54
56
  end
@@ -1,15 +1,16 @@
1
1
  #
2
2
  # Author:: Adam Jacob (<adam@opscode.com>)
3
3
  # Author:: AJ Christensen (<aj@opscode.com>)
4
- # Copyright:: Copyright (c) 2008 Opscode, Inc.
4
+ # Author:: Seth Falcon (<seth@opscode.com>)
5
+ # Copyright:: Copyright 2008-2010 Opscode, Inc.
5
6
  # License:: Apache License, Version 2.0
6
7
  #
7
8
  # Licensed under the Apache License, Version 2.0 (the "License");
8
9
  # you may not use this file except in compliance with the License.
9
10
  # You may obtain a copy of the License at
10
- #
11
+ #
11
12
  # http://www.apache.org/licenses/LICENSE-2.0
12
- #
13
+ #
13
14
  # Unless required by applicable law or agreed to in writing, software
14
15
  # distributed under the License is distributed on an "AS IS" BASIS,
15
16
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -21,7 +22,8 @@ require 'chef/mixin/from_file'
21
22
  require 'chef/mixin/params_validate'
22
23
  require 'chef/mixin/check_helper'
23
24
  require 'chef/log'
24
- require 'chef/cookbook/metadata/version'
25
+ require 'chef/version_class'
26
+ require 'chef/version_constraint'
25
27
 
26
28
  class Chef
27
29
  class Cookbook
@@ -34,12 +36,19 @@ class Chef
34
36
  :maintainer_email, :license, :platforms, :dependencies,
35
37
  :recommendations, :suggestions, :conflicting, :providing,
36
38
  :replacing, :attributes, :groupings, :recipes, :version]
37
-
39
+
40
+ VERSION_CONSTRAINTS = {:depends => "dependencies",
41
+ :recommends => "recommendations",
42
+ :suggests => "suggestions",
43
+ :conflicts => "conflicting",
44
+ :provides => "providing",
45
+ :replaces => "replacing" }
46
+
38
47
  include Chef::Mixin::CheckHelper
39
48
  include Chef::Mixin::ParamsValidate
40
49
  include Chef::Mixin::FromFile
41
50
 
42
- attr_reader :cookbook,
51
+ attr_reader :cookbook,
43
52
  :platforms,
44
53
  :dependencies,
45
54
  :recommendations,
@@ -53,7 +62,7 @@ class Chef
53
62
  :version
54
63
 
55
64
  # Builds a new Chef::Cookbook::Metadata object.
56
- #
65
+ #
57
66
  # === Parameters
58
67
  # cookbook<String>:: An optional cookbook object
59
68
  # maintainer<String>:: An optional maintainer
@@ -64,7 +73,7 @@ class Chef
64
73
  # metadata<Chef::Cookbook::Metadata>
65
74
  def initialize(cookbook=nil, maintainer='Your Name', maintainer_email='youremail@example.com', license='Apache v2.0')
66
75
  @cookbook = cookbook
67
- @name = cookbook ? cookbook.name : ""
76
+ @name = cookbook ? cookbook.name : ""
68
77
  @long_description = ""
69
78
  self.maintainer(maintainer)
70
79
  self.maintainer_email(maintainer_email)
@@ -82,8 +91,8 @@ class Chef
82
91
  @recipes = Mash.new
83
92
  @version = Version.new "0.0.0"
84
93
  if cookbook
85
- @recipes = cookbook.fully_qualified_recipe_names.inject({}) do |r, e|
86
- e = self.name if e =~ /::default$/
94
+ @recipes = cookbook.fully_qualified_recipe_names.inject({}) do |r, e|
95
+ e = self.name if e =~ /::default$/
87
96
  r[e] = ""
88
97
  self.provides e
89
98
  r
@@ -115,7 +124,7 @@ class Chef
115
124
  # Sets the maintainers email address, or returns it.
116
125
  #
117
126
  # === Parameters
118
- # maintainer_email<String>:: The maintainers email address
127
+ # maintainer_email<String>:: The maintainers email address
119
128
  #
120
129
  # === Returns
121
130
  # maintainer_email<String>:: Returns the current maintainer email.
@@ -133,7 +142,7 @@ class Chef
133
142
  # license<String>:: The current license.
134
143
  #
135
144
  # === Returns
136
- # license<String>:: Returns the current license
145
+ # license<String>:: Returns the current license
137
146
  def license(arg=nil)
138
147
  set_or_return(
139
148
  :license,
@@ -145,10 +154,10 @@ class Chef
145
154
  # Sets the current description, or returns it. Should be short - one line only!
146
155
  #
147
156
  # === Parameters
148
- # description<String>:: The new description
157
+ # description<String>:: The new description
149
158
  #
150
159
  # === Returns
151
- # description<String>:: Returns the description
160
+ # description<String>:: Returns the description
152
161
  def description(arg=nil)
153
162
  set_or_return(
154
163
  :description,
@@ -157,13 +166,13 @@ class Chef
157
166
  )
158
167
  end
159
168
 
160
- # Sets the current long description, or returns it. Might come from a README, say.
169
+ # Sets the current long description, or returns it. Might come from a README, say.
161
170
  #
162
171
  # === Parameters
163
172
  # long_description<String>:: The new long description
164
173
  #
165
174
  # === Returns
166
- # long_description<String>:: Returns the long description
175
+ # long_description<String>:: Returns the long description
167
176
  def long_description(arg=nil)
168
177
  set_or_return(
169
178
  :long_description,
@@ -179,10 +188,10 @@ class Chef
179
188
  # version<String>:: The curent version, as a string
180
189
  #
181
190
  # === Returns
182
- # version<String>:: Returns the current version
191
+ # version<String>:: Returns the current version
183
192
  def version(arg=nil)
184
193
  if arg
185
- @version = Version.new(arg)
194
+ @version = Chef::Version.new(arg)
186
195
  end
187
196
 
188
197
  @version.to_s
@@ -191,10 +200,10 @@ class Chef
191
200
  # Sets the name of the cookbook, or returns it.
192
201
  #
193
202
  # === Parameters
194
- # name<String>:: The curent cookbook name.
203
+ # name<String>:: The curent cookbook name.
195
204
  #
196
205
  # === Returns
197
- # name<String>:: Returns the current cookbook name.
206
+ # name<String>:: Returns the current cookbook name.
198
207
  def name(arg=nil)
199
208
  set_or_return(
200
209
  :name,
@@ -203,121 +212,141 @@ class Chef
203
212
  )
204
213
  end
205
214
 
206
- # Adds a supported platform, with version checking strings.
215
+ # Adds a supported platform, with version checking strings.
207
216
  #
208
217
  # === Parameters
209
218
  # platform<String>,<Symbol>:: The platform (like :ubuntu or :mac_os_x)
210
- # *versions<String>:: A list of versions matching << <= = >= >> followed by a version.
219
+ # version<String>:: A version constraint of the form "OP VERSION",
220
+ # where OP is one of < <= = > >= ~> and VERSION has
221
+ # the form x.y.z or x.y.
211
222
  #
212
223
  # === Returns
213
- # versions<Array>:: Returns the list of versions for the platform
214
- def supports(platform, *versions)
215
- versions.each { |v| _check_version_expression(v) }
216
- @platforms[platform] = versions
224
+ # versions<Array>:: Returns the list of versions for the platform
225
+ def supports(platform, *version_args)
226
+ version = new_args_format(:supports, platform, version_args)
227
+ validate_version_constraint(:supports, platform, version)
228
+ @platforms[platform] = version
217
229
  @platforms[platform]
218
230
  end
219
231
 
220
232
  # Adds a dependency on another cookbook, with version checking strings.
221
233
  #
222
234
  # === Parameters
223
- # cookbook<String>:: The cookbook
224
- # *versions<String>:: A list of versions matching << <= = >= >> followed by a version.
235
+ # cookbook<String>:: The cookbook
236
+ # version<String>:: A version constraint of the form "OP VERSION",
237
+ # where OP is one of < <= = > >= ~> and VERSION has
238
+ # the form x.y.z or x.y.
225
239
  #
226
240
  # === Returns
227
- # versions<Array>:: Returns the list of versions for the platform
228
- def depends(cookbook, *versions)
229
- versions.each { |v| _check_version_expression(v) }
230
- @dependencies[cookbook] = versions
241
+ # versions<Array>:: Returns the list of versions for the platform
242
+ def depends(cookbook, *version_args)
243
+ version = new_args_format(:depends, cookbook, version_args)
244
+ validate_version_constraint(:depends, cookbook, version)
245
+ @dependencies[cookbook] = version
231
246
  @dependencies[cookbook]
232
247
  end
233
248
 
234
249
  # Adds a recommendation for another cookbook, with version checking strings.
235
250
  #
236
251
  # === Parameters
237
- # cookbook<String>:: The cookbook
238
- # *versions<String>:: A list of versions matching << <= = >= >> followed by a version.
252
+ # cookbook<String>:: The cookbook
253
+ # version<String>:: A version constraint of the form "OP VERSION",
254
+ # where OP is one of < <= = > >= ~> and VERSION has
255
+ # the form x.y.z or x.y.
239
256
  #
240
257
  # === Returns
241
- # versions<Array>:: Returns the list of versions for the platform
242
- def recommends(cookbook, *versions)
243
- versions.each { |v| _check_version_expression(v) }
244
- @recommendations[cookbook] = versions
258
+ # versions<Array>:: Returns the list of versions for the platform
259
+ def recommends(cookbook, *version_args)
260
+ version = new_args_format(:recommends, cookbook, version_args)
261
+ validate_version_constraint(:recommends, cookbook, version)
262
+ @recommendations[cookbook] = version
245
263
  @recommendations[cookbook]
246
264
  end
247
265
 
248
266
  # Adds a suggestion for another cookbook, with version checking strings.
249
267
  #
250
268
  # === Parameters
251
- # cookbook<String>:: The cookbook
252
- # *versions<String>:: A list of versions matching << <= = >= >> followed by a version.
269
+ # cookbook<String>:: The cookbook
270
+ # version<String>:: A version constraint of the form "OP VERSION",
271
+ # where OP is one of < <= = > >= ~> and VERSION has the
272
+ # formx.y.z or x.y.
253
273
  #
254
274
  # === Returns
255
- # versions<Array>:: Returns the list of versions for the platform
256
- def suggests(cookbook, *versions)
257
- versions.each { |v| _check_version_expression(v) }
258
- @suggestions[cookbook] = versions
259
- @suggestions[cookbook]
275
+ # versions<Array>:: Returns the list of versions for the platform
276
+ def suggests(cookbook, *version_args)
277
+ version = new_args_format(:suggests, cookbook, version_args)
278
+ validate_version_constraint(:suggests, cookbook, version)
279
+ @suggestions[cookbook] = version
280
+ @suggestions[cookbook]
260
281
  end
261
282
 
262
283
  # Adds a conflict for another cookbook, with version checking strings.
263
284
  #
264
285
  # === Parameters
265
- # cookbook<String>:: The cookbook
266
- # *versions<String>:: A list of versions matching << <= = >= >> followed by a version.
286
+ # cookbook<String>:: The cookbook
287
+ # version<String>:: A version constraint of the form "OP VERSION",
288
+ # where OP is one of < <= = > >= ~> and VERSION has
289
+ # the form x.y.z or x.y.
267
290
  #
268
291
  # === Returns
269
- # versions<Array>:: Returns the list of versions for the platform
270
- def conflicts(cookbook, *versions)
271
- versions.each { |v| _check_version_expression(v) }
272
- @conflicting[cookbook] = versions
273
- @conflicting[cookbook]
292
+ # versions<Array>:: Returns the list of versions for the platform
293
+ def conflicts(cookbook, *version_args)
294
+ version = new_args_format(:conflicts, cookbook, version_args)
295
+ validate_version_constraint(:conflicts, cookbook, version)
296
+ @conflicting[cookbook] = version
297
+ @conflicting[cookbook]
274
298
  end
275
299
 
276
- # Adds a recipe, definition, or resource provided by this cookbook.
300
+ # Adds a recipe, definition, or resource provided by this cookbook.
277
301
  #
278
302
  # Recipes are specified as normal
279
303
  # Definitions are followed by (), and can include :params for prototyping
280
304
  # Resources are the stringified version (service[apache2])
281
305
  #
282
306
  # === Parameters
283
- # recipe, definition, resource<String>:: The thing we provide
284
- # *versions<String>:: A list of versions matching << <= = >= >> followed by a version.
307
+ # recipe, definition, resource<String>:: The thing we provide
308
+ # version<String>:: A version constraint of the form "OP VERSION",
309
+ # where OP is one of < <= = > >= ~> and VERSION has
310
+ # the form x.y.z or x.y.
285
311
  #
286
312
  # === Returns
287
- # versions<Array>:: Returns the list of versions for the platform
288
- def provides(cookbook, *versions)
289
- versions.each { |v| _check_version_expression(v) }
290
- @providing[cookbook] = versions
291
- @providing[cookbook]
313
+ # versions<Array>:: Returns the list of versions for the platform
314
+ def provides(cookbook, *version_args)
315
+ version = new_args_format(:provides, cookbook, version_args)
316
+ validate_version_constraint(:provides, cookbook, version)
317
+ @providing[cookbook] = version
318
+ @providing[cookbook]
292
319
  end
293
320
 
294
321
  # Adds a cookbook that is replaced by this one, with version checking strings.
295
322
  #
296
323
  # === Parameters
297
- # cookbook<String>:: The cookbook we replace
298
- # *versions<String>:: A list of versions matching << <= = >= >> followed by a version.
324
+ # cookbook<String>:: The cookbook we replace
325
+ # version<String>:: A version constraint of the form "OP VERSION",
326
+ # where OP is one of < <= = > >= ~> and VERSION has the form x.y.z or x.y.
299
327
  #
300
328
  # === Returns
301
- # versions<Array>:: Returns the list of versions for the platform
302
- def replaces(cookbook, *versions)
303
- versions.each { |v| _check_version_expression(v) }
304
- @replacing[cookbook] = versions
305
- @replacing[cookbook]
329
+ # versions<Array>:: Returns the list of versions for the platform
330
+ def replaces(cookbook, *version_args)
331
+ version = new_args_format(:replaces, cookbook, version_args)
332
+ validate_version_constraint(:replaces, cookbook, version)
333
+ @replacing[cookbook] = version
334
+ @replacing[cookbook]
306
335
  end
307
336
 
308
- # Adds a description for a recipe.
337
+ # Adds a description for a recipe.
309
338
  #
310
339
  # === Parameters
311
340
  # recipe<String>:: The recipe
312
341
  # description<String>:: The description of the recipe
313
342
  #
314
343
  # === Returns
315
- # description<String>:: Returns the current description
344
+ # description<String>:: Returns the current description
316
345
  def recipe(name, description)
317
- @recipes[name] = description
346
+ @recipes[name] = description
318
347
  end
319
348
 
320
- # Adds an attribute that a user needs to configure for this cookbook. Takes
349
+ # Adds an attribute )hat a user needs to configure for this cookbook. Takes
321
350
  # a name (with the / notation for a nested attribute), followed by any of
322
351
  # these options
323
352
  #
@@ -331,11 +360,11 @@ class Chef
331
360
  # default<String>,<Array>,<Hash>:: The default value
332
361
  #
333
362
  # === Parameters
334
- # name<String>:: The name of the attribute ('foo', or 'apache2/log_dir')
335
- # options<Hash>:: The description of the options
363
+ # name<String>:: The name of the attribute ('foo', or 'apache2/log_dir')
364
+ # options<Hash>:: The description of the options
336
365
  #
337
366
  # === Returns
338
- # options<Hash>:: Returns the current options hash
367
+ # options<Hash>:: Returns the current options hash
339
368
  def attribute(name, options)
340
369
  validate(
341
370
  options,
@@ -355,7 +384,7 @@ class Chef
355
384
  validate_calculated_default_rule(options)
356
385
  validate_choice_default_rule(options)
357
386
 
358
- @attributes[name] = options
387
+ @attributes[name] = options
359
388
  @attributes[name]
360
389
  end
361
390
 
@@ -367,43 +396,38 @@ class Chef
367
396
  :description => { :kind_of => String }
368
397
  }
369
398
  )
370
- @groupings[name] = options
399
+ @groupings[name] = options
371
400
  @groupings[name]
372
401
  end
373
402
 
374
- def _check_version_expression(version_string)
375
- if version_string =~ /^(>>|>=|=|<=|<<) (.+)$/
376
- [ $1, $2 ]
377
- else
378
- raise ArgumentError, "Version expression #{version_string} is invalid!"
379
- end
403
+ def to_hash
404
+ {
405
+ 'name' => self.name,
406
+ 'description' => self.description,
407
+ 'long_description' => self.long_description,
408
+ 'maintainer' => self.maintainer,
409
+ 'maintainer_email' => self.maintainer_email,
410
+ 'license' => self.license,
411
+ 'platforms' => self.platforms,
412
+ 'dependencies' => self.dependencies,
413
+ 'recommendations' => self.recommendations,
414
+ 'suggestions' => self.suggestions,
415
+ 'conflicting' => self.conflicting,
416
+ 'providing' => self.providing,
417
+ 'replacing' => self.replacing,
418
+ 'attributes' => self.attributes,
419
+ 'groupings' => self.groupings,
420
+ 'recipes' => self.recipes,
421
+ 'version' => self.version
422
+ }
380
423
  end
381
424
 
382
425
  def to_json(*a)
383
- result = {
384
- :name => self.name,
385
- :description => self.description,
386
- :long_description => self.long_description,
387
- :maintainer => self.maintainer,
388
- :maintainer_email => self.maintainer_email,
389
- :license => self.license,
390
- :platforms => self.platforms,
391
- :dependencies => self.dependencies,
392
- :recommendations => self.recommendations,
393
- :suggestions => self.suggestions,
394
- :conflicting => self.conflicting,
395
- :providing => self.providing,
396
- :replacing => self.replacing,
397
- :attributes => self.attributes,
398
- :groupings => self.groupings,
399
- :recipes => self.recipes,
400
- :version => self.version
401
- }
402
- result.to_json(*a)
426
+ self.to_hash.to_json(*a)
403
427
  end
404
428
 
405
429
  def self.from_hash(o)
406
- cm = self.new()
430
+ cm = self.new()
407
431
  cm.from_hash(o)
408
432
  cm
409
433
  end
@@ -416,12 +440,12 @@ class Chef
416
440
  @maintainer_email = o['maintainer_email'] if o.has_key?('maintainer_email')
417
441
  @license = o['license'] if o.has_key?('license')
418
442
  @platforms = o['platforms'] if o.has_key?('platforms')
419
- @dependencies = o['dependencies'] if o.has_key?('dependencies')
420
- @recommendations = o['recommendations'] if o.has_key?('recommendations')
421
- @suggestions = o['suggestions'] if o.has_key?('suggestions')
422
- @conflicting = o['conflicting'] if o.has_key?('conflicting')
443
+ @dependencies = handle_deprecated_constraints(o['dependencies']) if o.has_key?('dependencies')
444
+ @recommendations = handle_deprecated_constraints(o['recommendations']) if o.has_key?('recommendations')
445
+ @suggestions = handle_deprecated_constraints(o['suggestions']) if o.has_key?('suggestions')
446
+ @conflicting = handle_deprecated_constraints(o['conflicting']) if o.has_key?('conflicting')
423
447
  @providing = o['providing'] if o.has_key?('providing')
424
- @replacing = o['replacing'] if o.has_key?('replacing')
448
+ @replacing = handle_deprecated_constraints(o['replacing']) if o.has_key?('replacing')
425
449
  @attributes = o['attributes'] if o.has_key?('attributes')
426
450
  @groupings = o['groupings'] if o.has_key?('groupings')
427
451
  @recipes = o['recipes'] if o.has_key?('recipes')
@@ -434,6 +458,19 @@ class Chef
434
458
  self.from_hash(o)
435
459
  end
436
460
 
461
+ def self.validate_json(json_str)
462
+ o = Chef::JSONCompat.from_json(json_str)
463
+ metadata = new()
464
+ VERSION_CONSTRAINTS.each do |method_name, hash_key|
465
+ if constraints = o[hash_key]
466
+ constraints.each do |cb_name, constraints|
467
+ metadata.send(method_name, cb_name, *Array(constraints))
468
+ end
469
+ end
470
+ end
471
+ true
472
+ end
473
+
437
474
  def from_json(string)
438
475
  o = Chef::JSONCompat.from_json(string)
439
476
  from_hash(o)
@@ -441,6 +478,42 @@ class Chef
441
478
 
442
479
  private
443
480
 
481
+ def new_args_format(caller_name, dep_name, version_constraints)
482
+ if version_constraints.empty?
483
+ ">= 0.0.0"
484
+ elsif version_constraints.size == 1
485
+ version_constraints.first
486
+ else
487
+ msg=<<-OBSOLETED
488
+ The dependency specification syntax you are using is no longer valid. You may not
489
+ specify more than one version constraint for a particular cookbook.
490
+ Consult http://wiki.opscode.com/display/chef/Metadata for the updated syntax.
491
+
492
+ Called by: #{caller_name} '#{dep_name}', #{version_constraints.map {|vc| vc.inspect}.join(", ")}
493
+ Called from:
494
+ #{caller[0...5].map {|line| " " + line}.join("\n")}
495
+ OBSOLETED
496
+ raise Exceptions::ObsoleteDependencySyntax, msg
497
+ end
498
+ end
499
+
500
+ def validate_version_constraint(caller_name, dep_name, constraint_str)
501
+ Chef::VersionConstraint.new(constraint_str)
502
+ rescue Chef::Exceptions::InvalidVersionConstraint => e
503
+ Log.debug(e)
504
+
505
+ msg=<<-INVALID
506
+ The version constraint syntax you are using is not valid. If you recently
507
+ upgraded to Chef 0.10.0, be aware that you no may longer use "<<" and ">>" for
508
+ 'less than' and 'greater than'; use '<' and '>' instead.
509
+ Consult http://wiki.opscode.com/display/chef/Metadata for more information.
510
+
511
+ Called by: #{caller_name} '#{dep_name}', '#{constraint_str}'
512
+ Called from:
513
+ #{caller[0...5].map {|line| " " + line}.join("\n")}
514
+ INVALID
515
+ raise Exceptions::InvalidVersionConstraint, msg
516
+ end
444
517
  # Verify that the given array is an array of strings
445
518
  #
446
519
  # Raise an exception if the members of the array are not Strings
@@ -495,6 +568,25 @@ class Chef
495
568
  end
496
569
  end
497
570
 
571
+ # This method translates version constraint strings from
572
+ # cookbooks with the old format.
573
+ #
574
+ # Before we began respecting version constraints, we allowed
575
+ # multiple constraints to be placed on cookbooks, as well as the
576
+ # << and >> operators, which are now just < and >. For
577
+ # specifications with more than one constraint, we return an
578
+ # empty array (otherwise, we're silently abiding only part of
579
+ # the contract they have specified to us). If there is only one
580
+ # constraint, we are replacing the old << and >> with the new <
581
+ # and >.
582
+ def handle_deprecated_constraints(specification)
583
+ specification.inject(Mash.new) do |acc, (cb, constraints)|
584
+ constraints = Array(constraints)
585
+ acc[cb] = (constraints.empty? || constraints.size > 1) ? [] : constraints.first.gsub(/>>/, '>').gsub(/<</, '<')
586
+ acc
587
+ end
588
+ end
589
+
498
590
  end
499
591
  end
500
592
  end