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
@@ -30,19 +30,28 @@ class Chef
30
30
  else
31
31
  new_url = "cookbooks/#{cookbook}/#{type}?"
32
32
  new_url += "id=#{url}"
33
- platform, version = Chef::Platform.find_platform_and_version(node)
34
- if type == "files" || type == "templates"
35
- new_url += "&platform=#{platform}&version=#{version}&fqdn=#{node[:fqdn]}&node_name=#{node.name}"
36
- end
37
- if args
38
- args.each do |key, value|
39
- new_url += "&#{key}=#{value}"
40
- end
41
- end
33
+ new_url = generate_cookbook_url_from_uri(new_url, node, args)
42
34
  end
43
35
  Chef::Log.debug("generated cookbook url: #{new_url}")
44
36
  return new_url
45
37
  end
38
+
39
+ def generate_cookbook_url_from_uri(uri, node, args=nil)
40
+ platform, version = Chef::Platform.find_platform_and_version(node)
41
+ uri =~ /cookbooks\/(.+?)\/(.+)\?/
42
+ cookbook = $1
43
+ type = $2
44
+ if type == "files" || type == "templates"
45
+ uri += "&platform=#{platform}&version=#{version}&fqdn=#{node[:fqdn]}&node_name=#{node.name}"
46
+ end
47
+ if args
48
+ args.each do |key, value|
49
+ uri += "&#{key}=#{value}"
50
+ end
51
+ end
52
+
53
+ uri
54
+ end
46
55
 
47
56
  end
48
57
  end
@@ -16,6 +16,10 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
+ require 'chef/search/query'
20
+ require 'chef/data_bag'
21
+ require 'chef/data_bag_item'
22
+
19
23
  class Chef
20
24
  module Mixin
21
25
  module Language
@@ -73,7 +77,31 @@ class Chef
73
77
 
74
78
  has_platform
75
79
  end
76
-
80
+
81
+ def search(*args, &block)
82
+ # If you pass a block, or have at least the start argument, do raw result parsing
83
+ #
84
+ # Otherwise, do the iteration for the end user
85
+ if Kernel.block_given? || args.length >= 4
86
+ Chef::Search::Query.new.search(*args, &block)
87
+ else
88
+ results = Array.new
89
+ Chef::Search::Query.new.search(*args) do |o|
90
+ results << o
91
+ end
92
+ results
93
+ end
94
+ end
95
+
96
+ def data_bag(bag)
97
+ rbag = Chef::DataBag.load(bag)
98
+ rbag.keys
99
+ end
100
+
101
+ def data_bag_item(bag, item)
102
+ Chef::DataBagItem.load(bag, item)
103
+ end
104
+
77
105
  end
78
106
  end
79
107
  end
@@ -0,0 +1,56 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008, 2009 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
+
19
+ require 'chef/log'
20
+
21
+ class Chef
22
+ module Mixin
23
+ module LanguageIncludeAttribute
24
+
25
+ def include_attribute(*args)
26
+ if self.kind_of?(Chef::Node)
27
+ node = self
28
+ else
29
+ node = @node
30
+ end
31
+
32
+ args.flatten.each do |attrib|
33
+ if node.run_state[:seen_attributes].has_key?(attrib)
34
+ Chef::Log.debug("I am not loading attribute file #{attrib}, because I have already seen it.")
35
+ next
36
+ end
37
+
38
+ Chef::Log.debug("Loading Attribute #{attrib}")
39
+ node.run_state[:seen_attributes][attrib] = true
40
+
41
+ if amatch = attrib.match(/(.+?)::(.+)/)
42
+ cookbook = @cookbook_loader[amatch[1].to_sym]
43
+ cookbook.load_attribute(amatch[2], node)
44
+ else
45
+ cookbook = @cookbook_loader[amatch[1].to_sym]
46
+ cookbook.load_attribute("default", node)
47
+ end
48
+ end
49
+ true
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+
56
+
@@ -0,0 +1,53 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Copyright:: Copyright (c) 2008, 2009 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
+
19
+ require 'chef/log'
20
+
21
+ class Chef
22
+ module Mixin
23
+ module LanguageIncludeRecipe
24
+
25
+ def include_recipe(*args)
26
+ args.flatten.each do |recipe|
27
+ if @node.run_state[:seen_recipes].has_key?(recipe)
28
+ Chef::Log.debug("I am not loading #{recipe}, because I have already seen it.")
29
+ next
30
+ end
31
+
32
+ Chef::Log.debug("Loading Recipe #{recipe} via include_recipe")
33
+ @node.run_state[:seen_recipes][recipe] = true
34
+
35
+ rmatch = recipe.match(/(.+?)::(.+)/)
36
+ if rmatch
37
+ cookbook = @cookbook_loader[rmatch[1]]
38
+ cookbook.load_recipe(rmatch[2], @node, @collection, @definitions, @cookbook_loader)
39
+ else
40
+ cookbook = @cookbook_loader[recipe]
41
+ cookbook.load_recipe("default", @node, @collection, @definitions, @cookbook_loader)
42
+ end
43
+ end
44
+ end
45
+
46
+ def require_recipe(*args)
47
+ include_recipe(*args)
48
+ end
49
+
50
+ end
51
+ end
52
+ end
53
+
@@ -80,11 +80,12 @@ class Chef
80
80
  map = {
81
81
  symbol => validation
82
82
  }
83
- if arg == nil
83
+
84
+ if arg == nil && self.instance_variable_defined?(iv_symbol) == true
84
85
  self.instance_variable_get(iv_symbol)
85
86
  else
86
- validate({ symbol => arg }, { symbol => validation })
87
- self.instance_variable_set(iv_symbol, arg)
87
+ opts = validate({ symbol => arg }, { symbol => validation })
88
+ self.instance_variable_set(iv_symbol, opts[symbol])
88
89
  end
89
90
  end
90
91
 
@@ -104,7 +105,8 @@ class Chef
104
105
  # Raise an exception if the parameter is not found.
105
106
  def _pv_required(opts, key, is_required=true)
106
107
  if is_required
107
- if opts.has_key?(key.to_s) || opts.has_key?(key.to_sym)
108
+ if (opts.has_key?(key.to_s) && opts[key.to_s] != nil) ||
109
+ (opts.has_key?(key.to_sym) && opts[key.to_sym] != nil)
108
110
  true
109
111
  else
110
112
  raise ArgumentError, "Required argument #{key} is missing!"
@@ -166,16 +168,18 @@ class Chef
166
168
  # Check a parameter against a regular expression.
167
169
  def _pv_regex(opts, key, regex)
168
170
  value = _pv_opts_lookup(opts, key)
169
- passes = false
170
- [ regex ].flatten.each do |r|
171
- if value != nil
172
- if r.match(value.to_s)
173
- passes = true
171
+ if value != nil
172
+ passes = false
173
+ [ regex ].flatten.each do |r|
174
+ if value != nil
175
+ if r.match(value.to_s)
176
+ passes = true
177
+ end
174
178
  end
175
179
  end
176
- end
177
- unless passes
178
- raise ArgumentError, "Option #{key}'s value #{value} does not match regular expression #{regex.to_s}"
180
+ unless passes
181
+ raise ArgumentError, "Option #{key}'s value #{value} does not match regular expression #{regex.to_s}"
182
+ end
179
183
  end
180
184
  end
181
185
 
@@ -191,6 +195,15 @@ class Chef
191
195
  end
192
196
  end
193
197
  end
198
+
199
+ # Allow a parameter to default to @name
200
+ def _pv_name_attribute(opts, key, is_name_attribute=true)
201
+ if is_name_attribute
202
+ if opts[key] == nil
203
+ opts[key] = self.instance_variable_get("@name")
204
+ end
205
+ end
206
+ end
194
207
  end
195
208
  end
196
209
  end
@@ -20,12 +20,14 @@
20
20
  require 'chef/recipe'
21
21
  require 'chef/resource'
22
22
  require 'chef/mixin/convert_to_class_name'
23
+ require 'chef/mixin/language'
23
24
 
24
25
  class Chef
25
26
  module Mixin
26
27
  module RecipeDefinitionDSLCore
27
28
 
28
29
  include Chef::Mixin::ConvertToClassName
30
+ include Chef::Mixin::Language
29
31
 
30
32
  def method_missing(method_symbol, *args, &block)
31
33
  # If we have a definition that matches, we want to use that instead. This should
@@ -22,7 +22,17 @@ require 'erubis'
22
22
  class Chef
23
23
  module Mixin
24
24
  module Template
25
-
25
+
26
+ module ChefContext
27
+ def node
28
+ return @node if @node
29
+ raise "Could not find a value for node. If you are explicitly setting variables in a template, " +
30
+ "include a node variable if you plan to use it."
31
+ end
32
+ end
33
+
34
+ ::Erubis::Context.send(:include, ChefContext)
35
+
26
36
  # Render a template with Erubis. Takes a template as a string, and a
27
37
  # context hash.
28
38
  def render_template(template, context)
@@ -0,0 +1,87 @@
1
+ #
2
+ # Author:: Sam Ruby
3
+ # Author:: Daniel DeLeo (<dan@opscode.com>)
4
+ # Copyright:: Copyright (c) 2009 Opscode, Inc.
5
+ # Copyright:: Copyright (c) 2005 Sam Ruby
6
+ # License:: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+
20
+ # Code adapted from Sam Ruby's xchar.rb http://intertwingly.net/stories/2005/09/28/xchar.rb
21
+ # Thanks, Sam!
22
+
23
+ class Chef
24
+ module Mixin
25
+ module XMLEscape
26
+ extend self
27
+
28
+ CP1252 = {
29
+ 128 => 8364, # euro sign
30
+ 130 => 8218, # single low-9 quotation mark
31
+ 131 => 402, # latin small letter f with hook
32
+ 132 => 8222, # double low-9 quotation mark
33
+ 133 => 8230, # horizontal ellipsis
34
+ 134 => 8224, # dagger
35
+ 135 => 8225, # double dagger
36
+ 136 => 710, # modifier letter circumflex accent
37
+ 137 => 8240, # per mille sign
38
+ 138 => 352, # latin capital letter s with caron
39
+ 139 => 8249, # single left-pointing angle quotation mark
40
+ 140 => 338, # latin capital ligature oe
41
+ 142 => 381, # latin capital letter z with caron
42
+ 145 => 8216, # left single quotation mark
43
+ 146 => 8217, # right single quotation mark
44
+ 147 => 8220, # left double quotation mark
45
+ 148 => 8221, # right double quotation mark
46
+ 149 => 8226, # bullet
47
+ 150 => 8211, # en dash
48
+ 151 => 8212, # em dash
49
+ 152 => 732, # small tilde
50
+ 153 => 8482, # trade mark sign
51
+ 154 => 353, # latin small letter s with caron
52
+ 155 => 8250, # single right-pointing angle quotation mark
53
+ 156 => 339, # latin small ligature oe
54
+ 158 => 382, # latin small letter z with caron
55
+ 159 => 376 # latin capital letter y with diaeresis
56
+ } unless defined?(CP1252)
57
+
58
+ # http://www.w3.org/TR/REC-xml/#dt-chardata
59
+ PREDEFINED = {
60
+ 38 => '&amp;', # ampersand
61
+ 60 => '&lt;', # left angle bracket
62
+ 62 => '&gt;' # right angle bracket
63
+ } unless defined?(PREDEFINED)
64
+
65
+ # http://www.w3.org/TR/REC-xml/#charsets
66
+ VALID = [[0x9, 0xA, 0xD], (0x20..0xD7FF),
67
+ (0xE000..0xFFFD), (0x10000..0x10FFFF)] unless defined?(VALID)
68
+
69
+ def xml_escape(unescaped_str)
70
+ begin
71
+ unescaped_str.unpack("U*").map {|char| xml_escape_char!(char)}.join
72
+ rescue
73
+ unescaped_str.unpack("C*").map {|char| xml_escape_char!(char)}.join
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def xml_escape_char!(char)
80
+ char = CP1252[char] || char
81
+ char = 42 unless VALID.detect {|range| range.include? char}
82
+ char = PREDEFINED[char] || (char<128 ? char.chr : "&##{char};")
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -1,5 +1,6 @@
1
1
  #
2
2
  # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Christopher Brown (<cb@opscode.com>)
3
4
  # Copyright:: Copyright (c) 2008 Opscode, Inc.
4
5
  # License:: Apache License, Version 2.0
5
6
  #
@@ -20,24 +21,30 @@ require 'chef/config'
20
21
  require 'chef/mixin/check_helper'
21
22
  require 'chef/mixin/params_validate'
22
23
  require 'chef/mixin/from_file'
24
+ require 'chef/mixin/language_include_attribute'
23
25
  require 'chef/couchdb'
24
- require 'chef/queue'
26
+ require 'chef/rest'
25
27
  require 'chef/run_list'
26
28
  require 'chef/node/attribute'
29
+ require 'chef/index_queue'
27
30
  require 'extlib'
28
31
  require 'json'
29
32
 
30
33
  class Chef
31
34
  class Node
32
35
 
33
- attr_accessor :attribute, :recipe_list, :couchdb_rev, :run_state, :run_list, :override, :default
36
+ attr_accessor :attribute, :recipe_list, :couchdb, :couchdb_rev, :run_state, :run_list, :override_attrs, :default_attrs, :cookbook_loader
37
+ attr_reader :node
38
+ attr_reader :couchdb_id
34
39
 
35
40
  include Chef::Mixin::CheckHelper
36
41
  include Chef::Mixin::FromFile
37
42
  include Chef::Mixin::ParamsValidate
43
+ include Chef::Mixin::LanguageIncludeAttribute
44
+ include Chef::IndexQueue::Indexable
38
45
 
39
46
  DESIGN_DOCUMENT = {
40
- "version" => 8,
47
+ "version" => 9,
41
48
  "language" => "javascript",
42
49
  "views" => {
43
50
  "all" => {
@@ -120,22 +127,39 @@ class Chef
120
127
  }
121
128
 
122
129
  # Create a new Chef::Node object.
123
- def initialize()
130
+ def initialize(couchdb=nil)
124
131
  @name = nil
132
+ @node = self
125
133
 
126
134
  @attribute = Mash.new
127
- @override = Mash.new
128
- @default = Mash.new
129
- @run_list = Chef::RunList.new
135
+ @override_attrs = Mash.new
136
+ @default_attrs = Mash.new
137
+ @run_list = Chef::RunList.new
130
138
 
131
139
  @couchdb_rev = nil
132
- @couchdb = Chef::CouchDB.new
140
+ @couchdb_id = nil
141
+ @couchdb = couchdb || Chef::CouchDB.new
142
+
133
143
  @run_state = {
134
144
  :template_cache => Hash.new,
135
- :seen_recipes => Hash.new
145
+ :seen_recipes => Hash.new,
146
+ :seen_attributes => Hash.new
136
147
  }
137
148
  end
138
-
149
+
150
+ def couchdb_id=(value)
151
+ @couchdb_id = value
152
+ self.index_id = value
153
+ end
154
+
155
+ def chef_server_rest
156
+ Chef::REST.new(Chef::Config[:chef_server_url])
157
+ end
158
+
159
+ def self.chef_server_rest
160
+ Chef::REST.new(Chef::Config[:chef_server_url])
161
+ end
162
+
139
163
  # Find a recipe for this Chef::Node by fqdn. Will search first for
140
164
  # Chef::Config["node_path"]/fqdn.rb, then hostname.rb, then default.rb.
141
165
  #
@@ -143,21 +167,15 @@ class Chef
143
167
  #
144
168
  # Raises an ArgumentError if it cannot find the node.
145
169
  def find_file(fqdn)
146
- node_file = nil
147
170
  host_parts = fqdn.split(".")
148
171
  hostname = host_parts[0]
149
-
150
- if File.exists?(File.join(Chef::Config[:node_path], "#{fqdn}.rb"))
151
- node_file = File.join(Chef::Config[:node_path], "#{fqdn}.rb")
152
- elsif File.exists?(File.join(Chef::Config[:node_path], "#{hostname}.rb"))
153
- node_file = File.join(Chef::Config[:node_path], "#{hostname}.rb")
154
- elsif File.exists?(File.join(Chef::Config[:node_path], "default.rb"))
155
- node_file = File.join(Chef::Config[:node_path], "default.rb")
156
- end
157
- unless node_file
158
- raise ArgumentError, "Cannot find a node matching #{fqdn}, not even with default.rb!"
159
- end
160
- self.from_file(node_file)
172
+
173
+ [fqdn, hostname, "default"].each { |fname|
174
+ node_file = File.join(Chef::Config[:node_path], "#{fname.to_s}.rb")
175
+ return self.from_file(node_file) if File.exists?(node_file)
176
+ }
177
+
178
+ raise ArgumentError, "Cannot find a node matching #{fqdn}, not even with default.rb!"
161
179
  end
162
180
 
163
181
  # Set the name of this Node, or return the current name.
@@ -179,20 +197,22 @@ class Chef
179
197
 
180
198
  # Return an attribute of this node. Returns nil if the attribute is not found.
181
199
  def [](attrib)
182
- attrs = Chef::Node::Attribute.new(@attribute, @default, @override)
183
- attrs[attrib]
200
+ Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs)[attrib]
184
201
  end
185
202
 
186
203
  # Set an attribute of this node
187
204
  def []=(attrib, value)
188
- attrs = Chef::Node::Attribute.new(@attribute, @default, @override)
189
- attrs[attrib] = value
205
+ Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs)[attrib] = value
206
+ end
207
+
208
+ def store(attrib, value)
209
+ self[attrib] = value
190
210
  end
191
211
 
192
212
  # Set an attribute of this node, but auto-vivifiy any Mashes that might
193
213
  # be missing
194
214
  def set
195
- attrs = Chef::Node::Attribute.new(@attribute, @default, @override)
215
+ attrs = Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs)
196
216
  attrs.auto_vivifiy_on_read = true
197
217
  attrs
198
218
  end
@@ -200,7 +220,7 @@ class Chef
200
220
  # Set an attribute of this node, auto-vivifiying any mashes that are
201
221
  # missing, but if the final value already exists, don't set it
202
222
  def set_unless
203
- attrs = Chef::Node::Attribute.new(@attribute, @default, @override)
223
+ attrs = Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs)
204
224
  attrs.auto_vivifiy_on_read = true
205
225
  attrs.set_unless_value_present = true
206
226
  attrs
@@ -214,53 +234,31 @@ class Chef
214
234
  # Only works on the top level. Preferred way is to use the normal [] style
215
235
  # lookup and call attribute?()
216
236
  def attribute?(attrib)
217
- attrs = Chef::Node::Attribute.new(@attribute, @default, @override)
218
- attrs.attribute?(attrib)
237
+ Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs).attribute?(attrib)
219
238
  end
220
239
 
221
240
  # Yield each key of the top level to the block.
222
241
  def each(&block)
223
- attrs = Chef::Node::Attribute.new(@attribute, @default, @override)
224
- attrs.each(&block)
242
+ Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs).each(&block)
225
243
  end
226
244
 
227
245
  # Iterates over each attribute, passing the attribute and value to the block.
228
246
  def each_attribute(&block)
229
- attrs = Chef::Node::Attribute.new(@attribute, @default, @override)
230
- attrs.each_attribute(&block)
247
+ Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs).each_attribute(&block)
231
248
  end
232
249
 
233
250
  # Set an attribute based on the missing method. If you pass an argument, we'll use that
234
251
  # to set the attribute values. Otherwise, we'll wind up just returning the attributes
235
252
  # value.
236
253
  def method_missing(symbol, *args)
237
- attrs = Chef::Node::Attribute.new(@attribute, @default, @override)
238
- attrs.send(symbol, *args)
254
+ Chef::Node::Attribute.new(@attribute, @default_attrs, @override_attrs).send(symbol, *args)
239
255
  end
240
256
 
241
257
  # Returns true if this Node expects a given recipe, false if not.
242
258
  def recipe?(recipe_name)
243
- if @run_list.include?(recipe_name)
244
- true
245
- else
246
- if @run_state[:seen_recipes].include?(recipe_name)
247
- true
248
- else
249
- false
250
- end
251
- end
259
+ @run_list.include?(recipe_name) || @run_state[:seen_recipes].include?(recipe_name)
252
260
  end
253
261
 
254
- # Returns an Array of recipes. If you call it with arguments, they will become the new
255
- # list of recipes.
256
- def recipes(*args)
257
- if args.length > 0
258
- @run_list.reset(args)
259
- else
260
- @run_list
261
- end
262
- end
263
-
264
262
  # Returns true if this Node expects a given role, false if not.
265
263
  def role?(role_name)
266
264
  @run_list.include?("role[#{role_name}]")
@@ -269,11 +267,12 @@ class Chef
269
267
  # Returns an Array of roles and recipes, in the order they will be applied.
270
268
  # If you call it with arguments, they will become the new list of roles and recipes.
271
269
  def run_list(*args)
272
- if args.length > 0
273
- @run_list.reset(args)
274
- else
275
- @run_list
276
- end
270
+ args.length > 0 ? @run_list.reset!(args) : @run_list
271
+ end
272
+
273
+ def recipes(*args)
274
+ Chef::Log.warn "Chef::Node#recipes method is deprecated. Please use Chef::Node#run_list"
275
+ run_list(*args)
277
276
  end
278
277
 
279
278
  # Returns true if this Node expects a given role, false if not.
@@ -281,51 +280,33 @@ class Chef
281
280
  @run_list.detect { |r| r == item } ? true : false
282
281
  end
283
282
 
284
- # Turns the node into an object that we can index. I apologize up front for the
285
- # super confusion that is the recursive index_flatten hash, which comes up next.
286
- # Faith, young one, faith.
287
- #
288
- # === Returns
289
- # index_hash<Hash>:: A flattened hash of all the nodes attributes, suitable for indexing.
290
- def to_index
291
- index_hash = {
292
- "index_name" => "node",
293
- "id" => "node_#{@name}",
294
- "name" => @name,
295
- }
296
- @attribute.each do |key, value|
297
- if value.kind_of?(Hash) || value.kind_of?(Mash)
298
- index_flatten_hash(key, value).each do |to_index|
299
- to_index.each do |nk, nv|
300
- index_hash[nk] = nv
301
- end
302
- end
283
+ def consume_attributes(attrs)
284
+ attrs ||= {}
285
+ Chef::Log.debug("Adding JSON Attributes")
286
+ attrs.each do |key, value|
287
+ if ["recipes", "run_list"].include?(key)
288
+ run_list(value)
303
289
  else
304
- index_hash[key] = value
290
+ Chef::Log.debug("JSON Attribute: #{key} - #{value.inspect}")
291
+ store(key, value)
305
292
  end
306
293
  end
307
- index_hash["recipe"] = @run_list.recipes if @run_list.recipes.length > 0
308
- index_hash["roles"] = @run_list.roles if @run_list.roles.length > 0
309
- index_hash["run_list"] = @run_list.run_list if @run_list.run_list.length > 0
310
- index_hash
294
+ self[:tags] = Array.new unless attribute?(:tags)
295
+
311
296
  end
312
297
 
313
- # Ah, song of my heart, index_flatten_hash. This method flattens a hash in preparation
314
- # for indexing, by appending the name of it's parent to a current key with an _. Hence,
315
- # node[:bar][:baz] = 'monkey' becomes bar_baz:monkey.
316
- #
317
- # === Returns
318
- # results<Array>:: An array of hashes with one element.
319
- def index_flatten_hash(parent_name, hash)
320
- results = Array.new
321
- hash.each do |k, v|
322
- if v.kind_of?(Hash) || v.kind_of?(Mash)
323
- results << index_flatten_hash("#{parent_name}_#{k}", v)
324
- else
325
- results << { "#{parent_name}_#{k}", v }
326
- end
298
+ # Transform the node to a Hash
299
+ def to_hash
300
+ index_hash = Hash.new
301
+ self.each do |k, v|
302
+ index_hash[k] = v
327
303
  end
328
- results.flatten
304
+ index_hash["chef_type"] = "node"
305
+ index_hash["name"] = @name
306
+ index_hash["recipe"] = @run_list.recipes if @run_list.recipes.length > 0
307
+ index_hash["role"] = @run_list.roles if @run_list.roles.length > 0
308
+ index_hash["run_list"] = @run_list.run_list if @run_list.run_list.length > 0
309
+ index_hash
329
310
  end
330
311
 
331
312
  # Serialize this object as a hash
@@ -335,6 +316,8 @@ class Chef
335
316
  'json_class' => self.class.name,
336
317
  "attributes" => @attribute,
337
318
  "chef_type" => "node",
319
+ "defaults" => @default_attrs,
320
+ "overrides" => @override_attrs,
338
321
  "run_list" => @run_list.run_list,
339
322
  }
340
323
  result["_rev"] = @couchdb_rev if @couchdb_rev
@@ -345,62 +328,101 @@ class Chef
345
328
  def self.json_create(o)
346
329
  node = new
347
330
  node.name(o["name"])
348
- o["attributes"].each do |k,v|
349
- node[k] = v
350
- end
351
- if o.has_key?("defaults")
352
- node.default = o["defaults"]
353
- end
354
- if o.has_key?("overrides")
355
- node.override = o["overrides"]
356
- end
331
+ o["attributes"].each { |k,v| node[k] = v }
332
+
333
+ node.default_attrs = Mash.new(o["defaults"]) if o.has_key?("defaults")
334
+ node.override_attrs = Mash.new(o["overrides"]) if o.has_key?("overrides")
335
+
357
336
  if o.has_key?("run_list")
358
- node.run_list.reset(o["run_list"])
337
+ node.run_list.reset!(o["run_list"])
359
338
  else
360
339
  o["recipes"].each { |r| node.recipes << r }
361
340
  end
362
341
  node.couchdb_rev = o["_rev"] if o.has_key?("_rev")
342
+ node.couchdb_id = o["_id"] if o.has_key?("_id")
343
+ node.index_id = node.couchdb_id
363
344
  node
364
345
  end
365
346
 
366
347
  # List all the Chef::Node objects in the CouchDB. If inflate is set to true, you will get
367
348
  # the full list of all Nodes, fully inflated.
349
+ def self.cdb_list(inflate=false, couchdb=nil)
350
+ rs =(couchdb || Chef::CouchDB.new).list("nodes", inflate)
351
+ lookup = (inflate ? "value" : "key")
352
+ rs["rows"].collect { |r| r[lookup] }
353
+ end
354
+
368
355
  def self.list(inflate=false)
369
- rs = Chef::CouchDB.new.list("nodes", inflate)
370
356
  if inflate
371
- rs["rows"].collect { |r| r["value"] }
357
+ response = Hash.new
358
+ Chef::Search::Query.new.search(:node) do |n|
359
+ response[n.name] = n unless n.nil?
360
+ end
361
+ response
372
362
  else
373
- rs["rows"].collect { |r| r["key"] }
363
+ Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("nodes")
374
364
  end
375
365
  end
376
366
 
377
367
  # Load a node by name from CouchDB
368
+ def self.cdb_load(name, couchdb=nil)
369
+ (couchdb || Chef::CouchDB.new).load("node", name)
370
+ end
371
+
372
+ def self.exists?(nodename, couchdb)
373
+ begin
374
+ self.cdb_load(nodename, couchdb)
375
+ rescue Chef::Exceptions::CouchDBNotFound
376
+ nil
377
+ end
378
+ end
379
+
380
+ # Load a node by name
378
381
  def self.load(name)
379
- Chef::CouchDB.new.load("node", name)
382
+ Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("nodes/#{name}")
380
383
  end
381
384
 
382
385
  # Remove this node from the CouchDB
383
- def destroy
384
- Chef::Queue.send_msg(:queue, :remove, self)
386
+ def cdb_destroy
385
387
  @couchdb.delete("node", @name, @couchdb_rev)
386
388
  end
389
+
390
+ # Remove this node via the REST API
391
+ def destroy
392
+ chef_server_rest.delete_rest("nodes/#{@name}")
393
+ end
387
394
 
388
395
  # Save this node to the CouchDB
396
+ def cdb_save
397
+ @couchdb_rev = @couchdb.store("node", @name, self)["rev"]
398
+ end
399
+
400
+ # Save this node via the REST API
389
401
  def save
390
- Chef::Queue.send_msg(:queue, :index, self)
391
- results = @couchdb.store("node", @name, self)
392
- @couchdb_rev = results["rev"]
402
+ begin
403
+ chef_server_rest.put_rest("nodes/#{@name}", self)
404
+ rescue Net::HTTPServerException => e
405
+ raise e unless e.response.code == "404"
406
+ chef_server_rest.post_rest("nodes", self)
407
+ end
408
+ self
393
409
  end
394
410
 
411
+ # Create the node via the REST API
412
+ def create
413
+ chef_server_rest.post_rest("nodes", self)
414
+ self
415
+ end
416
+
395
417
  # Set up our CouchDB design document
396
- def self.create_design_document
397
- Chef::CouchDB.new.create_design_document("nodes", DESIGN_DOCUMENT)
418
+ def self.create_design_document(couchdb=nil)
419
+ (couchdb || Chef::CouchDB.new).create_design_document("nodes", DESIGN_DOCUMENT)
398
420
  end
399
421
 
400
422
  # As a string
401
423
  def to_s
402
424
  "node[#{@name}]"
403
425
  end
404
-
426
+
405
427
  end
406
428
  end