chef 0.7.16 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of chef might be problematic. Click here for more details.

Files changed (180) hide show
  1. data/README.rdoc +11 -10
  2. data/bin/chef-client +2 -2
  3. data/bin/chef-solo +1 -1
  4. data/bin/knife +27 -0
  5. data/bin/shef +49 -0
  6. data/distro/README +2 -0
  7. data/distro/{debian → common}/man/man1/chef-indexer.1 +0 -0
  8. data/distro/{debian → common}/man/man1/chef-server.1 +0 -0
  9. data/distro/{debian → common}/man/man8/chef-client.8 +0 -0
  10. data/distro/{debian → common}/man/man8/chef-solo.8 +0 -0
  11. data/distro/common/man/man8/knife.8 +375 -0
  12. data/distro/redhat/etc/init.d/chef-client +8 -4
  13. data/distro/redhat/etc/init.d/chef-server +16 -15
  14. data/distro/redhat/etc/init.d/chef-server-webui +78 -0
  15. data/distro/redhat/etc/init.d/chef-solr +76 -0
  16. data/distro/redhat/etc/init.d/chef-solr-indexer +75 -0
  17. data/distro/redhat/etc/sysconfig/chef-client +10 -0
  18. data/distro/redhat/etc/sysconfig/chef-server +10 -0
  19. data/distro/redhat/etc/sysconfig/chef-server-webui +10 -0
  20. data/distro/redhat/etc/sysconfig/chef-solr +9 -0
  21. data/distro/redhat/etc/sysconfig/chef-solr-indexer +7 -0
  22. data/distro/suse/etc/init.d/chef-client +121 -0
  23. data/lib/chef.rb +1 -1
  24. data/lib/chef/api_client.rb +263 -0
  25. data/lib/chef/application.rb +1 -1
  26. data/lib/chef/application/client.rb +21 -3
  27. data/lib/chef/application/knife.rb +144 -0
  28. data/lib/chef/application/server.rb +2 -1
  29. data/lib/chef/application/solo.rb +9 -2
  30. data/lib/chef/cache.rb +61 -0
  31. data/lib/chef/cache/checksum.rb +70 -0
  32. data/lib/chef/certificate.rb +154 -0
  33. data/lib/chef/client.rb +123 -220
  34. data/lib/chef/compile.rb +9 -21
  35. data/lib/chef/config.rb +67 -10
  36. data/lib/chef/cookbook.rb +49 -22
  37. data/lib/chef/cookbook/metadata.rb +85 -5
  38. data/lib/chef/cookbook_loader.rb +4 -4
  39. data/lib/chef/couchdb.rb +99 -30
  40. data/lib/chef/daemon.rb +1 -1
  41. data/lib/chef/data_bag.rb +215 -0
  42. data/lib/chef/data_bag_item.rb +219 -0
  43. data/lib/chef/exceptions.rb +3 -0
  44. data/lib/chef/index_queue.rb +29 -0
  45. data/lib/chef/index_queue/amqp_client.rb +106 -0
  46. data/lib/chef/index_queue/consumer.rb +76 -0
  47. data/lib/chef/index_queue/indexable.rb +74 -0
  48. data/lib/chef/knife.rb +309 -0
  49. data/lib/chef/knife/client_bulk_delete.rb +40 -0
  50. data/lib/chef/knife/client_create.rb +62 -0
  51. data/lib/chef/knife/client_delete.rb +37 -0
  52. data/lib/chef/knife/client_edit.rb +37 -0
  53. data/lib/chef/knife/client_list.rb +40 -0
  54. data/lib/chef/knife/client_reregister.rb +48 -0
  55. data/lib/chef/knife/client_show.rb +42 -0
  56. data/lib/chef/knife/configure.rb +123 -0
  57. data/lib/chef/knife/cookbook_bulk_delete.rb +46 -0
  58. data/lib/chef/knife/cookbook_delete.rb +41 -0
  59. data/lib/chef/knife/cookbook_download.rb +57 -0
  60. data/lib/chef/knife/cookbook_list.rb +41 -0
  61. data/lib/chef/knife/cookbook_metadata.rb +87 -0
  62. data/lib/chef/knife/cookbook_show.rb +75 -0
  63. data/lib/chef/knife/cookbook_upload.rb +179 -0
  64. data/lib/chef/knife/data_bag_create.rb +43 -0
  65. data/lib/chef/knife/data_bag_delete.rb +43 -0
  66. data/lib/chef/knife/data_bag_edit.rb +49 -0
  67. data/lib/chef/knife/data_bag_list.rb +42 -0
  68. data/lib/chef/knife/data_bag_show.rb +40 -0
  69. data/lib/chef/knife/ec2_instance_data.rb +46 -0
  70. data/lib/chef/knife/index_rebuild.rb +51 -0
  71. data/lib/chef/knife/node_bulk_delete.rb +43 -0
  72. data/lib/chef/knife/node_create.rb +39 -0
  73. data/lib/chef/knife/node_delete.rb +36 -0
  74. data/lib/chef/knife/node_edit.rb +36 -0
  75. data/lib/chef/knife/node_from_file.rb +42 -0
  76. data/lib/chef/knife/node_list.rb +41 -0
  77. data/lib/chef/knife/node_run_list_add.rb +64 -0
  78. data/lib/chef/knife/node_run_list_remove.rb +45 -0
  79. data/lib/chef/knife/node_show.rb +46 -0
  80. data/lib/chef/knife/role_bulk_delete.rb +44 -0
  81. data/lib/chef/knife/role_create.rb +44 -0
  82. data/lib/chef/knife/role_delete.rb +36 -0
  83. data/lib/chef/knife/role_edit.rb +37 -0
  84. data/lib/chef/knife/role_from_file.rb +46 -0
  85. data/lib/chef/knife/role_list.rb +40 -0
  86. data/lib/chef/knife/role_show.rb +43 -0
  87. data/lib/chef/knife/search.rb +94 -0
  88. data/lib/chef/knife/ssh.rb +170 -0
  89. data/lib/chef/log.rb +30 -8
  90. data/lib/chef/mixin/checksum.rb +2 -7
  91. data/lib/chef/mixin/command.rb +32 -13
  92. data/lib/chef/mixin/convert_to_class_name.rb +15 -0
  93. data/lib/chef/mixin/deep_merge.rb +199 -11
  94. data/lib/chef/mixin/generate_url.rb +18 -9
  95. data/lib/chef/mixin/language.rb +29 -1
  96. data/lib/chef/mixin/language_include_attribute.rb +56 -0
  97. data/lib/chef/mixin/language_include_recipe.rb +53 -0
  98. data/lib/chef/mixin/params_validate.rb +25 -12
  99. data/lib/chef/mixin/recipe_definition_dsl_core.rb +2 -0
  100. data/lib/chef/mixin/template.rb +11 -1
  101. data/lib/chef/mixin/xml_escape.rb +87 -0
  102. data/lib/chef/node.rb +144 -122
  103. data/lib/chef/openid_registration.rb +12 -5
  104. data/lib/chef/platform.rb +89 -47
  105. data/lib/chef/provider/breakpoint.rb +36 -0
  106. data/lib/chef/provider/cron.rb +5 -6
  107. data/lib/chef/provider/deploy.rb +43 -10
  108. data/lib/chef/provider/deploy/revision.rb +2 -3
  109. data/lib/chef/provider/erl_call.rb +72 -0
  110. data/lib/chef/provider/file.rb +8 -4
  111. data/lib/chef/provider/git.rb +10 -5
  112. data/lib/chef/provider/group/dscl.rb +128 -0
  113. data/lib/chef/provider/http_request.rb +6 -2
  114. data/lib/chef/provider/ifconfig.rb +1 -0
  115. data/lib/chef/provider/link.rb +1 -1
  116. data/lib/chef/provider/log.rb +53 -0
  117. data/lib/chef/provider/mdadm.rb +88 -0
  118. data/lib/chef/provider/mount/mount.rb +1 -1
  119. data/lib/chef/provider/package.rb +1 -1
  120. data/lib/chef/provider/package/easy_install.rb +106 -0
  121. data/lib/chef/provider/package/pacman.rb +101 -0
  122. data/lib/chef/provider/package/portage.rb +1 -1
  123. data/lib/chef/provider/package/rpm.rb +10 -8
  124. data/lib/chef/provider/package/yum-dump.py +22 -3
  125. data/lib/chef/provider/package/yum.rb +32 -8
  126. data/lib/chef/provider/package/zypper.rb +132 -0
  127. data/lib/chef/provider/remote_directory.rb +58 -49
  128. data/lib/chef/provider/remote_file.rb +1 -1
  129. data/lib/chef/provider/route.rb +136 -80
  130. data/lib/chef/provider/ruby_block.rb +18 -1
  131. data/lib/chef/provider/service/arch.rb +109 -0
  132. data/lib/chef/provider/service/freebsd.rb +0 -1
  133. data/lib/chef/provider/service/simple.rb +2 -3
  134. data/lib/chef/provider/service/upstart.rb +191 -0
  135. data/lib/chef/provider/subversion.rb +12 -4
  136. data/lib/chef/provider/template.rb +85 -53
  137. data/lib/chef/provider/user.rb +1 -1
  138. data/lib/chef/provider/user/dscl.rb +277 -0
  139. data/lib/chef/provider/user/useradd.rb +1 -0
  140. data/lib/chef/recipe.rb +2 -41
  141. data/lib/chef/resource.rb +9 -3
  142. data/lib/chef/resource/breakpoint.rb +35 -0
  143. data/lib/chef/resource/deploy.rb +16 -2
  144. data/lib/chef/resource/easy_install_package.rb +41 -0
  145. data/lib/chef/resource/erl_call.rb +83 -0
  146. data/lib/chef/resource/freebsd_package.rb +35 -0
  147. data/lib/chef/resource/log.rb +62 -0
  148. data/lib/chef/resource/mdadm.rb +82 -0
  149. data/lib/chef/resource/pacman_package.rb +33 -0
  150. data/lib/chef/resource/ruby_block.rb +21 -2
  151. data/lib/chef/resource/scm.rb +8 -0
  152. data/lib/chef/resource/subversion.rb +1 -0
  153. data/lib/chef/resource/user.rb +5 -2
  154. data/lib/chef/resource/yum_package.rb +36 -0
  155. data/lib/chef/resource_collection.rb +17 -9
  156. data/lib/chef/resource_collection/stepable_iterator.rb +124 -0
  157. data/lib/chef/rest.rb +166 -81
  158. data/lib/chef/role.rb +114 -38
  159. data/lib/chef/run_list.rb +15 -6
  160. data/lib/chef/runner.rb +13 -11
  161. data/lib/chef/search/query.rb +60 -0
  162. data/lib/chef/shef.rb +220 -0
  163. data/lib/chef/shef/ext.rb +297 -0
  164. data/lib/chef/shef/shef_session.rb +175 -0
  165. data/lib/chef/streaming_cookbook_uploader.rb +187 -0
  166. data/lib/chef/tasks/chef_repo.rake +53 -155
  167. data/lib/chef/util/file_edit.rb +94 -96
  168. data/lib/chef/webui_user.rb +233 -0
  169. metadata +219 -63
  170. data/distro/debian/etc/init.d/chef-indexer +0 -175
  171. data/distro/redhat/etc/chef/client.rb +0 -16
  172. data/distro/redhat/etc/chef/indexer.rb +0 -10
  173. data/distro/redhat/etc/chef/server.rb +0 -22
  174. data/distro/redhat/etc/init.d/chef-indexer +0 -76
  175. data/lib/chef/application/indexer.rb +0 -141
  176. data/lib/chef/queue.rb +0 -145
  177. data/lib/chef/search.rb +0 -88
  178. data/lib/chef/search/result.rb +0 -64
  179. data/lib/chef/search_index.rb +0 -77
  180. data/lib/chef/util/fileedit.rb +0 -121
@@ -22,11 +22,14 @@ require 'chef/node'
22
22
  require 'chef/role'
23
23
  require 'chef/log'
24
24
  require 'chef/mixin/deep_merge'
25
+ require 'chef/mixin/language_include_recipe'
25
26
 
26
27
  class Chef
27
28
  class Compile
28
29
 
29
30
  attr_accessor :node, :cookbook_loader, :collection, :definitions
31
+
32
+ include Chef::Mixin::LanguageIncludeRecipe
30
33
 
31
34
  # Creates a new Chef::Compile object and populates its fields. This object gets
32
35
  # used by the Chef Server to generate a fully compiled recipe list for a node.
@@ -36,11 +39,12 @@ class Chef
36
39
  def initialize(node=nil)
37
40
  @node = node
38
41
  @cookbook_loader = Chef::CookbookLoader.new
42
+ @node.cookbook_loader = @cookbook_loader
39
43
  @collection = Chef::ResourceCollection.new
40
44
  @definitions = Hash.new
41
45
  @recipes = Array.new
42
- @default_attributes = Array.new
43
- @override_attributes = Array.new
46
+ @default_attributes = Hash.new
47
+ @override_attributes = Hash.new
44
48
 
45
49
  load_libraries
46
50
  load_providers
@@ -70,8 +74,6 @@ class Chef
70
74
  # === Returns
71
75
  # true:: Always returns true
72
76
  def load_attributes()
73
- recipes, default_attrs, override_attrs = expand_node
74
-
75
77
  @cookbook_loader.each do |cookbook|
76
78
  cookbook.load_attributes(@node)
77
79
  end
@@ -140,29 +142,15 @@ class Chef
140
142
  def load_recipes
141
143
  expand_node
142
144
  @recipes.each do |recipe|
143
- if @node.run_state[:seen_recipes].has_key?(recipe)
144
- Chef::Log.debug("I am not loading #{recipe}, because I have already seen it.")
145
- next
146
- end
147
- Chef::Log.debug("Loading Recipe #{recipe}")
148
- @node.run_state[:seen_recipes][recipe] = true
149
-
150
- rmatch = recipe.match(/(.+?)::(.+)/)
151
- if rmatch
152
- cookbook = @cookbook_loader[rmatch[1]]
153
- cookbook.load_recipe(rmatch[2], @node, @collection, @definitions, @cookbook_loader)
154
- else
155
- cookbook = @cookbook_loader[recipe]
156
- cookbook.load_recipe("default", @node, @collection, @definitions, @cookbook_loader)
157
- end
145
+ include_recipe(recipe)
158
146
  end
159
147
  true
160
148
  end
161
149
 
162
150
  def expand_node
163
151
  @recipes, @default_attributes, @override_attributes = @node.run_list.expand
164
- @node.default = @default_attributes
165
- @node.override = @override_attributes
152
+ @node.default_attrs = Chef::Mixin::DeepMerge.merge(@node.default_attrs, @default_attributes)
153
+ @node.override_attrs = Chef::Mixin::DeepMerge.merge(@node.override_attrs, @override_attributes)
166
154
  return @recipes, @default_attributes, @override_attributes
167
155
  end
168
156
 
@@ -17,6 +17,7 @@
17
17
  # See the License for the specific language governing permissions and
18
18
  # limitations under the License.
19
19
 
20
+ require 'chef/log'
20
21
  require 'mixlib/config'
21
22
 
22
23
  class Chef
@@ -54,13 +55,24 @@ class Chef
54
55
  :template_url,
55
56
  :remotefile_url,
56
57
  :search_url,
58
+ :chef_server_url,
57
59
  :role_url ].each do |u|
58
60
  c[u] = url
59
61
  end
60
62
  end
61
63
  url
62
64
  end
63
-
65
+
66
+ # When you are using ActiveSupport, they monkey-patch 'daemonize' into Kernel.
67
+ # So while this is basically identical to what method_missing would do, we pull
68
+ # it up here and get a real method written so that things get dispatched
69
+ # properly.
70
+ config_attr_writer :daemonize do |v|
71
+ configure do |c|
72
+ c[:daemonize] = v
73
+ end
74
+ end
75
+
64
76
  # Override the config dispatch to set the value of log_location configuration option
65
77
  #
66
78
  # === Parameters
@@ -70,7 +82,9 @@ class Chef
70
82
  if location.respond_to? :sync=
71
83
  location
72
84
  elsif location.respond_to? :to_str
73
- File.new(location.to_str, "a")
85
+ f = File.new(location.to_str, "a")
86
+ f.sync = true
87
+ f
74
88
  end
75
89
  end
76
90
 
@@ -86,16 +100,16 @@ class Chef
86
100
 
87
101
  authorized_openid_identifiers nil
88
102
  authorized_openid_providers nil
89
- chef_server_url nil
90
- cookbook_path [ "/var/chef/site-cookbooks", "/var/chef/cookbooks" ]
103
+ client_registration_retries 5
104
+ cookbook_path [ "/var/chef/cookbooks", "/var/chef/site-cookbooks" ]
105
+ cookbook_tarball_path "/var/chef/cookbook-tarballs"
91
106
  couchdb_database "chef"
92
107
  couchdb_url "http://localhost:5984"
93
108
  couchdb_version nil
94
- daemonize nil
95
109
  delay 0
96
110
  executable_path ENV['PATH'] ? ENV['PATH'].split(File::PATH_SEPARATOR) : []
97
111
  file_cache_path "/var/chef/cache"
98
- file_store_path "/var/chef/store"
112
+ file_backup_path nil
99
113
  group nil
100
114
  http_retry_count 5
101
115
  http_retry_delay 5
@@ -103,10 +117,11 @@ class Chef
103
117
  json_attribs nil
104
118
  log_level :info
105
119
  log_location STDOUT
120
+ verbose_logging nil
106
121
  node_name nil
107
122
  node_path "/var/chef/node"
108
123
  openid_cstore_couchdb false
109
- openid_cstore_path "/var/chef/openid/cstore"
124
+ openid_cstore_path "/var/chef/openid/cstore"
110
125
  openid_providers nil
111
126
  openid_store_couchdb false
112
127
  openid_store_path "/var/chef/openid/db"
@@ -118,19 +133,21 @@ class Chef
118
133
  queue_retry_count 5
119
134
  queue_retry_delay 5
120
135
  queue_user ""
121
- queue_prefix nil
136
+ chef_server_url "http://localhost:4000"
122
137
  registration_url "http://localhost:4000"
138
+ client_url "http://localhost:4042"
123
139
  remotefile_url "http://localhost:4000"
124
- rest_timeout 60
140
+ rest_timeout 300
125
141
  run_command_stderr_timeout 120
126
142
  run_command_stdout_timeout 120
127
- search_index_path "/var/chef/search_index"
128
143
  search_url "http://localhost:4000"
129
144
  solo false
130
145
  splay nil
131
146
  ssl_client_cert ""
132
147
  ssl_client_key ""
133
148
  ssl_verify_mode :verify_none
149
+ ssl_ca_path nil
150
+ ssl_ca_file nil
134
151
  template_url "http://localhost:4000"
135
152
  umask 0022
136
153
  user nil
@@ -138,5 +155,45 @@ class Chef
138
155
  role_path "/var/chef/roles"
139
156
  role_url "http://localhost:4000"
140
157
  recipe_url nil
158
+ solr_url "http://localhost:8983"
159
+ solr_jetty_path "/var/chef/solr-jetty"
160
+ solr_data_path "/var/chef/solr/data"
161
+ solr_home_path "/var/chef/solr"
162
+ solr_heap_size "256M"
163
+ solr_java_opts nil
164
+ amqp_host '0.0.0.0'
165
+ amqp_port '5672'
166
+ amqp_user 'chef'
167
+ amqp_pass 'testing'
168
+ amqp_vhost '/chef'
169
+ # Setting this to a UUID string also makes the queue durable
170
+ # (persist across rabbitmq restarts)
171
+ amqp_consumer_id nil
172
+
173
+ client_key "/etc/chef/client.pem"
174
+ validation_key "/etc/chef/validation.pem"
175
+ validation_client_name "chef-validator"
176
+ web_ui_client_name "chef-webui"
177
+ web_ui_key "/etc/chef/webui.pem"
178
+ web_ui_admin_user_name "admin"
179
+ web_ui_admin_default_password "p@ssw0rd1"
180
+
181
+ # Server Signing CA
182
+ #
183
+ # In truth, these don't even have to change
184
+ signing_ca_cert "/var/chef/ca/cert.pem"
185
+ signing_ca_key "/var/chef/ca/key.pem"
186
+ signing_ca_country "US"
187
+ signing_ca_state "Washington"
188
+ signing_ca_location "Seattle"
189
+ signing_ca_org "Chef User"
190
+ signing_ca_domain "opensource.opscode.com"
191
+ signing_ca_email "opensource-cert@opscode.com"
192
+
193
+ # Checksum Cache
194
+ # Uses Moneta on the back-end
195
+ cache_type "BasicFile"
196
+ cache_options({ :path => "/var/chef/cache/checksums", :skip_expires => true })
197
+
141
198
  end
142
199
  end
@@ -1,5 +1,6 @@
1
1
  #
2
2
  # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Nuo Yan (<nuo@opscode.com>)
3
4
  # Author:: Christopher Walters (<cw@opscode.com>)
4
5
  # Copyright:: Copyright (c) 2008 Opscode, Inc.
5
6
  # License:: Apache License, Version 2.0
@@ -26,9 +27,9 @@ class Chef
26
27
  class Cookbook
27
28
  include Chef::Mixin::ConvertToClassName
28
29
 
29
- attr_accessor :attribute_files, :definition_files, :template_files, :remote_files,
30
+ attr_accessor :definition_files, :template_files, :remote_files,
30
31
  :lib_files, :resource_files, :provider_files, :name
31
- attr_reader :recipe_files
32
+ attr_reader :recipe_files, :attribute_files
32
33
 
33
34
  # Creates a new Chef::Cookbook object.
34
35
  #
@@ -37,6 +38,7 @@ class Chef
37
38
  def initialize(name)
38
39
  @name = name
39
40
  @attribute_files = Array.new
41
+ @attribute_names = Hash.new
40
42
  @definition_files = Array.new
41
43
  @template_files = Array.new
42
44
  @remote_files = Array.new
@@ -70,15 +72,23 @@ class Chef
70
72
  # === Raises
71
73
  # <ArgumentError>:: If the argument is not a kind_of? <Chef::Node>
72
74
  def load_attributes(node)
73
- unless node.kind_of?(Chef::Node)
74
- raise ArgumentError, "You must pass a Chef::Node to load_attributes!"
75
- end
76
75
  @attribute_files.each do |file|
77
- Chef::Log.debug("Loading attributes from #{file}")
78
- node.from_file(file)
76
+ load_attribute_file(file, node)
79
77
  end
80
78
  node
81
79
  end
80
+
81
+ def load_attribute_file(file, node)
82
+ Chef::Log.debug("Loading attributes from #{file}")
83
+ node.from_file(file)
84
+ end
85
+
86
+ def load_attribute(name, node)
87
+ attr_name = shorten_name(name)
88
+ file = @attribute_files[@attribute_names[attr_name]]
89
+ load_attribute_file(file, node)
90
+ node
91
+ end
82
92
 
83
93
  # Loads all the resource definitions in this cookbook.
84
94
  #
@@ -118,20 +128,14 @@ class Chef
118
128
  end
119
129
 
120
130
  def recipe_files=(*args)
121
- @recipe_files = args.flatten
122
- @recipe_files.each_index do |i|
123
- file = @recipe_files[i]
124
- case file
125
- when /(.+\/)(.+).rb$/
126
- @recipe_names[$2] = i
127
- when /(.+).rb$/
128
- @recipe_names[$1] = i
129
- else
130
- @recipe_names[file] = i
131
- end
132
- end
131
+ @recipe_files, @recipe_names = set_with_names(args.flatten)
133
132
  @recipe_files
134
133
  end
134
+
135
+ def attribute_files=(*args)
136
+ @attribute_files, @attribute_names = set_with_names(args.flatten)
137
+ @attribute_files
138
+ end
135
139
 
136
140
  def recipe?(name)
137
141
  lookup_name = name
@@ -153,9 +157,7 @@ class Chef
153
157
 
154
158
  def load_recipe(name, node, collection=nil, definitions=nil, cookbook_loader=nil)
155
159
  cookbook_name = @name
156
- recipe_name = nil
157
- nmatch = name.match(/^(.+?)::(.+)$/)
158
- recipe_name = nmatch ? nmatch[2] : name
160
+ recipe_name = shorten_name(name)
159
161
 
160
162
  unless @recipe_names.has_key?(recipe_name)
161
163
  raise ArgumentError, "Cannot find a recipe matching #{recipe_name} in cookbook #{@name}"
@@ -166,6 +168,31 @@ class Chef
166
168
  recipe.from_file(@recipe_files[@recipe_names[recipe_name]])
167
169
  recipe
168
170
  end
171
+
172
+ private
173
+
174
+ def shorten_name(name)
175
+ short_name = nil
176
+ nmatch = name.match(/^(.+?)::(.+)$/)
177
+ short_name = nmatch ? nmatch[2] : name
178
+ end
179
+
180
+ def set_with_names(file_list)
181
+ files = file_list
182
+ names = Hash.new
183
+ files.each_index do |i|
184
+ file = files[i]
185
+ case file
186
+ when /(.+\/)(.+).rb$/
187
+ names[$2] = i
188
+ when /(.+).rb$/
189
+ names[$1] = i
190
+ else
191
+ names[file] = i
192
+ end
193
+ end
194
+ [ files, names ]
195
+ end
169
196
 
170
197
  end
171
198
  end
@@ -40,6 +40,7 @@ class Chef
40
40
  :providing,
41
41
  :replacing,
42
42
  :attributes,
43
+ :groupings,
43
44
  :recipes,
44
45
  :version
45
46
 
@@ -69,6 +70,7 @@ class Chef
69
70
  @providing = Mash.new
70
71
  @replacing = Mash.new
71
72
  @attributes = Mash.new
73
+ @groupings = Mash.new
72
74
  @recipes = Mash.new
73
75
  @version = Version.new "0.0.0"
74
76
  if cookbook
@@ -307,9 +309,10 @@ class Chef
307
309
  #
308
310
  # display_name<String>:: What a UI should show for this attribute
309
311
  # description<String>:: A hint as to what this attr is for
310
- # multiple_values<True>,<False>:: Whether it supports multiple values
311
- # type<String>:: "string", "hash" or "array" - default is "string"
312
- # required<True>,<False>:: Whether this attr is required - default false
312
+ # choice<Array>:: An array of choices to present to the user.
313
+ # calculated<Boolean>:: If true, the default value is calculated by the recipe and cannot be displayed.
314
+ # type<String>:: "string" or "array" - default is "string" ("hash" is supported for backwards compatibility)
315
+ # required<String>:: Whether this attr is 'required', 'recommended' or 'optional' - default 'optional' (true/false values also supported for backwards compatibility)
313
316
  # recipes<Array>:: An array of recipes which need this attr set.
314
317
  # default<String>,<Array>,<Hash>:: The default value
315
318
  #
@@ -325,17 +328,35 @@ class Chef
325
328
  {
326
329
  :display_name => { :kind_of => String },
327
330
  :description => { :kind_of => String },
328
- :multiple_values => { :equal_to => [ true, false ], :default => false },
331
+ :choice => { :kind_of => [ Array ], :default => [] },
332
+ :calculated => { :equal_to => [ true, false ], :default => false },
329
333
  :type => { :equal_to => [ "string", "array", "hash" ], :default => "string" },
330
- :required => { :equal_to => [ true, false ], :default => false },
334
+ :required => { :equal_to => [ "required", "recommended", "optional", true, false ], :default => "optional" },
331
335
  :recipes => { :kind_of => [ Array ], :default => [] },
332
336
  :default => { :kind_of => [ String, Array, Hash ] }
333
337
  }
334
338
  )
339
+ options[:required] = remap_required_attribute(options[:required]) unless options[:required].nil?
340
+ validate_string_array(options[:choice])
341
+ validate_calculated_default_rule(options)
342
+ validate_choice_default_rule(options)
343
+
335
344
  @attributes[name] = options
336
345
  @attributes[name]
337
346
  end
338
347
 
348
+ def grouping(name, options)
349
+ validate(
350
+ options,
351
+ {
352
+ :title => { :kind_of => String },
353
+ :description => { :kind_of => String }
354
+ }
355
+ )
356
+ @groupings[name] = options
357
+ @groupings[name]
358
+ end
359
+
339
360
  def _check_version_expression(version_string)
340
361
  if version_string =~ /^(>>|>=|=|<=|<<) (.+)$/
341
362
  [ $1, $2 ]
@@ -360,6 +381,7 @@ class Chef
360
381
  :providing => self.providing,
361
382
  :replacing => self.replacing,
362
383
  :attributes => self.attributes,
384
+ :groupings => self.groupings,
363
385
  :recipes => self.recipes,
364
386
  :version => self.version
365
387
  }
@@ -379,6 +401,7 @@ class Chef
379
401
  self.maintainer o['maintainer'] if o.has_key?('maintainer')
380
402
  self.maintainer_email o['maintainer_email'] if o.has_key?('maintainer_email')
381
403
  self.license o['license'] if o.has_key?('license')
404
+ self.version o['version'] if o.has_key?('version')
382
405
  self.platforms = o['platforms'] if o.has_key?('platforms')
383
406
  self.dependencies = o['dependencies'] if o.has_key?('dependencies')
384
407
  self.recommendations = o['recommendations'] if o.has_key?('recommendations')
@@ -387,6 +410,7 @@ class Chef
387
410
  self.providing = o['providing'] if o.has_key?('providing')
388
411
  self.replacing = o['replacing'] if o.has_key?('replacing')
389
412
  self.attributes = o['attributes'] if o.has_key?('attributes')
413
+ self.groupings = o['groupings'] if o.has_key?('groupings')
390
414
  self.recipes = o['recipes'] if o.has_key?('recipes')
391
415
  self.version = o['version'] if o.has_key?('version')
392
416
  self
@@ -402,6 +426,62 @@ class Chef
402
426
  from_hash(o)
403
427
  end
404
428
 
429
+ private
430
+
431
+ # Verify that the given array is an array of strings
432
+ #
433
+ # Raise an exception if the members of the array are not Strings
434
+ #
435
+ # === Parameters
436
+ # arry<Array>:: An array to be validated
437
+ def validate_string_array(arry)
438
+ if arry.kind_of?(Array)
439
+ arry.each do |choice|
440
+ validate( {:choice => choice}, {:choice => {:kind_of => String}} )
441
+ end
442
+ end
443
+ end
444
+
445
+ # For backwards compatibility, remap Boolean values to String
446
+ # true is mapped to "required"
447
+ # false is mapped to "optional"
448
+ #
449
+ # === Parameters
450
+ # required_attr<String><Boolean>:: The value of options[:required]
451
+ #
452
+ # === Returns
453
+ # required_attr<String>:: "required", "recommended", or "optional"
454
+ def remap_required_attribute(value)
455
+ case value
456
+ when true
457
+ value = "required"
458
+ when false
459
+ value = "optional"
460
+ end
461
+ value
462
+ end
463
+
464
+ def validate_calculated_default_rule(options)
465
+ calculated_conflict = ((options[:default].is_a?(Array) && !options[:default].empty?) ||
466
+ (options[:default].is_a?(String) && !options[:default] != "")) &&
467
+ options[:calculated] == true
468
+ raise ArgumentError, "Default cannot be specified if calculated is true!" if calculated_conflict
469
+ end
470
+
471
+ def validate_choice_default_rule(options)
472
+ return if !options[:choice].is_a?(Array) || options[:choice].empty?
473
+
474
+ if options[:default].is_a?(String) && options[:default] != ""
475
+ raise ArgumentError, "Default must be one of your choice values!" if options[:choice].index(options[:default]) == nil
476
+ end
477
+
478
+ if options[:default].is_a?(Array) && !options[:default].empty?
479
+ options[:default].each do |val|
480
+ raise ArgumentError, "Default values must be a subset of your choice values!" if options[:choice].index(val) == nil
481
+ end
482
+ end
483
+ end
484
+
405
485
  end
406
486
  end
407
487
  end