chef 0.9.6 → 0.9.8.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. data/lib/chef/application.rb +19 -3
  2. data/lib/chef/application/client.rb +7 -8
  3. data/lib/chef/application/knife.rb +9 -3
  4. data/lib/chef/application/solo.rb +5 -5
  5. data/lib/chef/cache/checksum.rb +2 -2
  6. data/lib/chef/client.rb +30 -64
  7. data/lib/chef/config.rb +52 -30
  8. data/lib/chef/cookbook/metadata.rb +12 -1
  9. data/lib/chef/cookbook_site_streaming_uploader.rb +211 -0
  10. data/lib/chef/cookbook_uploader.rb +102 -0
  11. data/lib/chef/cookbook_version.rb +47 -30
  12. data/lib/chef/couchdb.rb +1 -11
  13. data/lib/chef/exceptions.rb +1 -0
  14. data/lib/chef/file_access_control.rb +1 -1
  15. data/lib/chef/index_queue/amqp_client.rb +10 -3
  16. data/lib/chef/knife.rb +154 -58
  17. data/lib/chef/knife/bootstrap.rb +84 -48
  18. data/lib/chef/knife/bootstrap/centos5-gems.erb +41 -0
  19. data/lib/chef/knife/bootstrap/fedora13-gems.erb +38 -0
  20. data/lib/chef/knife/bootstrap/ubuntu10.04-gems.erb +44 -0
  21. data/lib/chef/knife/client_bulk_delete.rb +1 -1
  22. data/lib/chef/knife/client_create.rb +1 -1
  23. data/lib/chef/knife/client_delete.rb +1 -1
  24. data/lib/chef/knife/client_edit.rb +1 -1
  25. data/lib/chef/knife/client_list.rb +1 -1
  26. data/lib/chef/knife/client_reregister.rb +1 -1
  27. data/lib/chef/knife/client_show.rb +1 -1
  28. data/lib/chef/knife/configure.rb +1 -1
  29. data/lib/chef/knife/configure_client.rb +1 -1
  30. data/lib/chef/knife/cookbook_bulk_delete.rb +1 -1
  31. data/lib/chef/knife/cookbook_create.rb +159 -0
  32. data/lib/chef/knife/cookbook_delete.rb +6 -6
  33. data/lib/chef/knife/cookbook_download.rb +1 -1
  34. data/lib/chef/knife/cookbook_list.rb +1 -1
  35. data/lib/chef/knife/cookbook_metadata.rb +1 -1
  36. data/lib/chef/knife/cookbook_metadata_from_file.rb +1 -1
  37. data/lib/chef/knife/cookbook_show.rb +1 -1
  38. data/lib/chef/knife/cookbook_site_download.rb +2 -1
  39. data/lib/chef/knife/cookbook_site_list.rb +2 -1
  40. data/lib/chef/knife/cookbook_site_search.rb +2 -1
  41. data/lib/chef/knife/cookbook_site_share.rb +108 -0
  42. data/lib/chef/knife/cookbook_site_show.rb +2 -1
  43. data/lib/chef/knife/cookbook_site_unshare.rb +52 -0
  44. data/lib/chef/knife/cookbook_site_vendor.rb +3 -2
  45. data/lib/chef/knife/cookbook_test.rb +1 -1
  46. data/lib/chef/knife/cookbook_upload.rb +22 -155
  47. data/lib/chef/knife/data_bag_create.rb +2 -1
  48. data/lib/chef/knife/data_bag_delete.rb +2 -1
  49. data/lib/chef/knife/data_bag_edit.rb +2 -1
  50. data/lib/chef/knife/data_bag_from_file.rb +2 -1
  51. data/lib/chef/knife/data_bag_list.rb +2 -1
  52. data/lib/chef/knife/data_bag_show.rb +2 -1
  53. data/lib/chef/knife/ec2_instance_data.rb +1 -1
  54. data/lib/chef/knife/ec2_server_create.rb +16 -4
  55. data/lib/chef/knife/ec2_server_delete.rb +8 -2
  56. data/lib/chef/knife/ec2_server_list.rb +8 -2
  57. data/lib/chef/knife/index_rebuild.rb +1 -1
  58. data/lib/chef/knife/node_bulk_delete.rb +1 -1
  59. data/lib/chef/knife/node_create.rb +1 -1
  60. data/lib/chef/knife/node_delete.rb +1 -1
  61. data/lib/chef/knife/node_edit.rb +1 -1
  62. data/lib/chef/knife/node_from_file.rb +1 -1
  63. data/lib/chef/knife/node_list.rb +1 -1
  64. data/lib/chef/knife/node_run_list_add.rb +1 -1
  65. data/lib/chef/knife/node_run_list_remove.rb +1 -1
  66. data/lib/chef/knife/node_show.rb +1 -1
  67. data/lib/chef/knife/rackspace_server_create.rb +1 -1
  68. data/lib/chef/knife/rackspace_server_delete.rb +1 -1
  69. data/lib/chef/knife/rackspace_server_list.rb +1 -1
  70. data/lib/chef/knife/recipe_list.rb +1 -1
  71. data/lib/chef/knife/role_bulk_delete.rb +1 -1
  72. data/lib/chef/knife/role_create.rb +1 -1
  73. data/lib/chef/knife/role_delete.rb +1 -1
  74. data/lib/chef/knife/role_edit.rb +1 -1
  75. data/lib/chef/knife/role_from_file.rb +1 -1
  76. data/lib/chef/knife/role_list.rb +1 -1
  77. data/lib/chef/knife/role_show.rb +1 -1
  78. data/lib/chef/knife/search.rb +1 -1
  79. data/lib/chef/knife/slicehost_images_list.rb +1 -1
  80. data/lib/chef/knife/slicehost_server_create.rb +1 -1
  81. data/lib/chef/knife/slicehost_server_delete.rb +1 -1
  82. data/lib/chef/knife/slicehost_server_list.rb +1 -1
  83. data/lib/chef/knife/ssh.rb +49 -9
  84. data/lib/chef/knife/status.rb +2 -2
  85. data/lib/chef/knife/terremark_server_create.rb +1 -1
  86. data/lib/chef/knife/terremark_server_delete.rb +1 -1
  87. data/lib/chef/knife/terremark_server_list.rb +1 -1
  88. data/lib/chef/mixin/command.rb +17 -204
  89. data/lib/chef/mixin/command/unix.rb +215 -0
  90. data/lib/chef/mixin/command/windows.rb +72 -0
  91. data/lib/chef/mixin/find_preferred_file.rb +3 -3
  92. data/lib/chef/mixin/language.rb +64 -23
  93. data/lib/chef/node.rb +27 -3
  94. data/lib/chef/node/attribute.rb +20 -10
  95. data/lib/chef/platform.rb +3 -9
  96. data/lib/chef/provider/cron.rb +1 -1
  97. data/lib/chef/provider/deploy.rb +4 -1
  98. data/lib/chef/provider/group.rb +1 -1
  99. data/lib/chef/provider/group/dscl.rb +2 -2
  100. data/lib/chef/provider/mount/mount.rb +6 -6
  101. data/lib/chef/provider/package/easy_install.rb +8 -7
  102. data/lib/chef/provider/package/pacman.rb +1 -1
  103. data/lib/chef/provider/package/rpm.rb +4 -4
  104. data/lib/chef/provider/package/solaris.rb +127 -0
  105. data/lib/chef/provider/package/yum.rb +51 -28
  106. data/lib/chef/provider/remote_directory.rb +5 -2
  107. data/lib/chef/provider/remote_file.rb +1 -1
  108. data/lib/chef/provider/ruby_block.rb +1 -0
  109. data/lib/chef/provider/script.rb +2 -2
  110. data/lib/chef/provider/service/debian.rb +40 -4
  111. data/lib/chef/provider/service/freebsd.rb +1 -1
  112. data/lib/chef/provider/service/upstart.rb +3 -10
  113. data/lib/chef/provider/user/dscl.rb +2 -2
  114. data/lib/chef/providers.rb +1 -0
  115. data/lib/chef/resource.rb +7 -8
  116. data/lib/chef/resource/service.rb +17 -0
  117. data/lib/chef/resource/solaris_package.rb +36 -0
  118. data/lib/chef/resource/yum_package.rb +11 -4
  119. data/lib/chef/rest/rest_request.rb +18 -1
  120. data/lib/chef/run_list/run_list_expansion.rb +4 -0
  121. data/lib/chef/run_list/run_list_item.rb +8 -1
  122. data/lib/chef/shef/ext.rb +1 -1
  123. data/lib/chef/shef/shef_session.rb +7 -3
  124. data/lib/chef/tasks/chef_repo.rake +1 -0
  125. data/lib/chef/version.rb +2 -1
  126. metadata +23 -8
  127. data/lib/chef/cache/file_cache_by_checksum.rb +0 -52
@@ -26,6 +26,11 @@ require 'chef/cookbook/metadata/version'
26
26
  class Chef
27
27
  class Cookbook
28
28
  class Metadata
29
+
30
+ COMPARISON_FIELDS = [ :name, :description, :long_description, :maintainer,
31
+ :maintainer_email, :license, :platforms, :dependencies,
32
+ :recommendations, :suggestions, :conflicting, :providing,
33
+ :replacing, :attributes, :groupings, :recipes, :version]
29
34
 
30
35
  include Chef::Mixin::CheckHelper
31
36
  include Chef::Mixin::ParamsValidate
@@ -83,6 +88,12 @@ class Chef
83
88
  end
84
89
  end
85
90
 
91
+ def ==(other)
92
+ COMPARISON_FIELDS.inject(true) do |equal_so_far, field|
93
+ equal_so_far && other.respond_to?(field) && (other.send(field) == send(field))
94
+ end
95
+ end
96
+
86
97
  # Sets the cookbooks maintainer, or returns it.
87
98
  #
88
99
  # === Parameters
@@ -330,7 +341,7 @@ class Chef
330
341
  :description => { :kind_of => String },
331
342
  :choice => { :kind_of => [ Array ], :default => [] },
332
343
  :calculated => { :equal_to => [ true, false ], :default => false },
333
- :type => { :equal_to => [ "string", "array", "hash" ], :default => "string" },
344
+ :type => { :equal_to => [ "string", "array", "hash", "symbol" ], :default => "string" },
334
345
  :required => { :equal_to => [ "required", "recommended", "optional", true, false ], :default => "optional" },
335
346
  :recipes => { :kind_of => [ Array ], :default => [] },
336
347
  :default => { :kind_of => [ String, Array, Hash ] }
@@ -0,0 +1,211 @@
1
+ #
2
+ # Author:: Stanislav Vitvitskiy
3
+ # Author:: Nuo Yan (nuo@opscode.com)
4
+ # Author:: Christopher Walters (<cw@opscode.com>)
5
+ # Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
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
+
21
+ require 'net/http'
22
+ require 'mixlib/authentication/signedheaderauth'
23
+ require 'openssl'
24
+
25
+ # inspired by from http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html
26
+ class Chef
27
+ class CookbookSiteStreamingUploader
28
+
29
+ DefaultHeaders = { 'accept' => 'application/json', 'x-chef-version' => ::Chef::VERSION }
30
+
31
+ class << self
32
+
33
+ def post(to_url, user_id, secret_key_filename, params = {}, headers = {})
34
+ make_request(:post, to_url, user_id, secret_key_filename, params, headers)
35
+ end
36
+
37
+ def put(to_url, user_id, secret_key_filename, params = {}, headers = {})
38
+ make_request(:put, to_url, user_id, secret_key_filename, params, headers)
39
+ end
40
+
41
+ def make_request(http_verb, to_url, user_id, secret_key_filename, params = {}, headers = {})
42
+ boundary = '----RubyMultipartClient' + rand(1000000).to_s + 'ZZZZZ'
43
+ parts = []
44
+ content_file = nil
45
+
46
+ timestamp = Time.now.utc.iso8601
47
+ secret_key = OpenSSL::PKey::RSA.new(File.read(secret_key_filename))
48
+
49
+ unless params.nil? || params.empty?
50
+ params.each do |key, value|
51
+ if value.kind_of?(File)
52
+ content_file = value
53
+ filepath = value.path
54
+ filename = File.basename(filepath)
55
+ parts << StringPart.new( "--" + boundary + "\r\n" +
56
+ "Content-Disposition: form-data; name=\"" + key.to_s + "\"; filename=\"" + filename + "\"\r\n" +
57
+ "Content-Type: application/octet-stream\r\n\r\n")
58
+ parts << StreamPart.new(value, File.size(filepath))
59
+ parts << StringPart.new("\r\n")
60
+ else
61
+ parts << StringPart.new( "--" + boundary + "\r\n" +
62
+ "Content-Disposition: form-data; name=\"" + key.to_s + "\"\r\n\r\n")
63
+ parts << StringPart.new(value.to_s + "\r\n")
64
+ end
65
+ end
66
+ parts << StringPart.new("--" + boundary + "--\r\n")
67
+ end
68
+
69
+ body_stream = MultipartStream.new(parts)
70
+
71
+ timestamp = Time.now.utc.iso8601
72
+
73
+ url = URI.parse(to_url)
74
+
75
+ Chef::Log.logger.debug("Signing: method: #{http_verb}, path: #{url.path}, file: #{content_file}, User-id: #{user_id}, Timestamp: #{timestamp}")
76
+
77
+ # We use the body for signing the request if the file parameter
78
+ # wasn't a valid file or wasn't included. Extract the body (with
79
+ # multi-part delimiters intact) to sign the request.
80
+ # TODO: tim: 2009-12-28: It'd be nice to remove this special case, and
81
+ # always hash the entire request body. In the file case it would just be
82
+ # expanded multipart text - the entire body of the POST.
83
+ content_body = parts.inject("") { |result,part| result + part.read(0, part.size) }
84
+ content_file.rewind if content_file # we consumed the file for the above operation, so rewind it.
85
+
86
+ signing_options = {
87
+ :http_method=>http_verb,
88
+ :path=>url.path,
89
+ :user_id=>user_id,
90
+ :timestamp=>timestamp}
91
+ (content_file && signing_options[:file] = content_file) || (signing_options[:body] = (content_body || ""))
92
+
93
+ headers.merge!(Mixlib::Authentication::SignedHeaderAuth.signing_object(signing_options).sign(secret_key))
94
+
95
+ content_file.rewind if content_file
96
+
97
+ # net/http doesn't like symbols for header keys, so we'll to_s each one just in case
98
+ headers = DefaultHeaders.merge(Hash[*headers.map{ |k,v| [k.to_s, v] }.flatten])
99
+
100
+ req = case http_verb
101
+ when :put
102
+ Net::HTTP::Put.new(url.path, headers)
103
+ when :post
104
+ Net::HTTP::Post.new(url.path, headers)
105
+ end
106
+ req.content_length = body_stream.size
107
+ req.content_type = 'multipart/form-data; boundary=' + boundary unless parts.empty?
108
+ req.body_stream = body_stream
109
+
110
+ http = Net::HTTP.new(url.host, url.port)
111
+ if url.scheme == "https"
112
+ http.use_ssl = true
113
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
114
+ end
115
+ res = http.request(req)
116
+ #res = http.start {|http_proc| http_proc.request(req) }
117
+
118
+ # alias status to code and to_s to body for test purposes
119
+ # TODO: stop the following madness!
120
+ class << res
121
+ alias :to_s :body
122
+
123
+ # BUGBUG this makes the response compatible with what respsonse_steps expects to test headers (response.headers[] -> response[])
124
+ def headers
125
+ self
126
+ end
127
+
128
+ def status
129
+ code.to_i
130
+ end
131
+ end
132
+ res
133
+ end
134
+
135
+ end
136
+
137
+ class StreamPart
138
+ def initialize(stream, size)
139
+ @stream, @size = stream, size
140
+ end
141
+
142
+ def size
143
+ @size
144
+ end
145
+
146
+ # read the specified amount from the stream
147
+ def read(offset, how_much)
148
+ @stream.read(how_much)
149
+ end
150
+ end
151
+
152
+ class StringPart
153
+ def initialize(str)
154
+ @str = str
155
+ end
156
+
157
+ def size
158
+ @str.length
159
+ end
160
+
161
+ # read the specified amount from the string startiung at the offset
162
+ def read(offset, how_much)
163
+ @str[offset, how_much]
164
+ end
165
+ end
166
+
167
+ class MultipartStream
168
+ def initialize(parts)
169
+ @parts = parts
170
+ @part_no = 0
171
+ @part_offset = 0
172
+ end
173
+
174
+ def size
175
+ @parts.inject(0) {|size, part| size + part.size}
176
+ end
177
+
178
+ def read(how_much)
179
+ return nil if @part_no >= @parts.size
180
+
181
+ how_much_current_part = @parts[@part_no].size - @part_offset
182
+
183
+ how_much_current_part = if how_much_current_part > how_much
184
+ how_much
185
+ else
186
+ how_much_current_part
187
+ end
188
+
189
+ how_much_next_part = how_much - how_much_current_part
190
+
191
+ current_part = @parts[@part_no].read(@part_offset, how_much_current_part)
192
+
193
+ # recurse into the next part if the current one was not large enough
194
+ if how_much_next_part > 0
195
+ @part_no += 1
196
+ @part_offset = 0
197
+ next_part = read(how_much_next_part)
198
+ current_part + if next_part
199
+ next_part
200
+ else
201
+ ''
202
+ end
203
+ else
204
+ @part_offset += how_much_current_part
205
+ current_part
206
+ end
207
+ end
208
+ end
209
+
210
+ end
211
+ end
@@ -0,0 +1,102 @@
1
+ require 'rest_client'
2
+ require 'chef/cookbook_loader'
3
+ require 'chef/cache/checksum'
4
+ require 'chef/sandbox'
5
+ require 'chef/cookbook_version'
6
+ require 'chef/cookbook/syntax_check'
7
+ require 'chef/cookbook/file_system_file_vendor'
8
+
9
+ class Chef
10
+ class CookbookUploader
11
+ class << self
12
+
13
+ def upload_cookbook(cookbook)
14
+ Chef::Log.info("Saving #{cookbook.name}")
15
+
16
+ rest = Chef::REST.new(Chef::Config[:chef_server_url])
17
+
18
+ # Syntax Check
19
+ validate_cookbook(cookbook)
20
+ # Generate metadata.json from metadata.rb
21
+ build_metadata(cookbook)
22
+
23
+ # generate checksums of cookbook files and create a sandbox
24
+ checksum_files = cookbook.checksums
25
+ checksums = checksum_files.inject({}){|memo,elt| memo[elt.first]=nil ; memo}
26
+ new_sandbox = rest.post_rest("/sandboxes", { :checksums => checksums })
27
+
28
+ Chef::Log.info("Uploading files")
29
+ # upload the new checksums and commit the sandbox
30
+ new_sandbox['checksums'].each do |checksum, info|
31
+ if info['needs_upload'] == true
32
+ Chef::Log.info("Uploading #{checksum_files[checksum]} (checksum hex = #{checksum}) to #{info['url']}")
33
+
34
+ # Checksum is the hexadecimal representation of the md5,
35
+ # but we need the base64 encoding for the content-md5
36
+ # header
37
+ checksum64 = Base64.encode64([checksum].pack("H*")).strip
38
+ timestamp = Time.now.utc.iso8601
39
+ file_contents = File.read(checksum_files[checksum])
40
+ # TODO - 5/28/2010, cw: make signing and sending the request streaming
41
+ sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object(
42
+ :http_method => :put,
43
+ :path => URI.parse(info['url']).path,
44
+ :body => file_contents,
45
+ :timestamp => timestamp,
46
+ :user_id => rest.client_name
47
+ )
48
+ headers = { 'content-type' => 'application/x-binary', 'content-md5' => checksum64, :accept => 'application/json' }
49
+ headers.merge!(sign_obj.sign(OpenSSL::PKey::RSA.new(rest.signing_key)))
50
+ begin
51
+ RestClient::Request.execute(:method => :put, :url => info['url'], :headers => headers, :payload => file_contents)
52
+ rescue RestClient::RequestFailed => e
53
+ Chef::Log.error("Upload failed: #{e.message}\n#{e.response.body}")
54
+ raise
55
+ end
56
+ else
57
+ Chef::Log.debug("#{checksum_files[checksum]} has not changed")
58
+ end
59
+ end
60
+ sandbox_url = new_sandbox['uri']
61
+ Chef::Log.debug("Committing sandbox")
62
+ # Retry if S3 is claims a checksum doesn't exist (the eventual
63
+ # in eventual consistency)
64
+ retries = 0
65
+ begin
66
+ rest.put_rest(sandbox_url, {:is_completed => true})
67
+ rescue Net::HTTPServerException => e
68
+ if e.message =~ /^400/ && (retries += 1) <= 5
69
+ sleep 2
70
+ retry
71
+ else
72
+ raise
73
+ end
74
+ end
75
+
76
+ # files are uploaded, so save the manifest
77
+ cookbook.save
78
+
79
+ Chef::Log.info("Upload complete!")
80
+ end
81
+
82
+ def build_metadata(cookbook)
83
+ Chef::Log.debug("Generating metadata")
84
+ kcm = Chef::Knife::CookbookMetadata.new
85
+ kcm.config[:cookbook_path] = Chef::Config[:cookbook_path]
86
+ kcm.name_args = [ cookbook.name.to_s ]
87
+ kcm.run
88
+ end
89
+
90
+ def validate_cookbook(cookbook)
91
+ syntax_checker = Chef::Cookbook::SyntaxCheck.for_cookbook(cookbook.name, @user_cookbook_path)
92
+ Chef::Log.info("Validating ruby files")
93
+ exit(1) unless syntax_checker.validate_ruby_files
94
+ Chef::Log.info("Validating templates")
95
+ exit(1) unless syntax_checker.validate_templates
96
+ Chef::Log.info("Syntax OK")
97
+ true
98
+ end
99
+
100
+ end
101
+ end
102
+ end
@@ -30,22 +30,6 @@ class Chef
30
30
  class CookbookVersion
31
31
  include Chef::IndexQueue::Indexable
32
32
 
33
- attr_accessor :definition_filenames, :template_filenames, :file_filenames,
34
- :library_filenames, :resource_filenames, :provider_filenames, :root_filenames, :name,
35
- :metadata, :metadata_filenames, :status, :couchdb_rev, :couchdb
36
- attr_reader :couchdb_id
37
-
38
- # attribute_filenames also has a setter that has non-default
39
- # functionality.
40
- attr_reader :attribute_filenames
41
-
42
- # recipe_filenames also has a setter that has non-default
43
- # functionality.
44
- attr_reader :recipe_filenames
45
-
46
- attr_reader :recipe_filenames_by_name
47
- attr_reader :attribute_filenames_by_short_filename
48
-
49
33
  COOKBOOK_SEGMENTS = [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates, :root_files ]
50
34
 
51
35
  DESIGN_DOCUMENT = {
@@ -167,6 +151,33 @@ class Chef
167
151
  },
168
152
  }
169
153
  }
154
+
155
+ attr_accessor :definition_filenames
156
+ attr_accessor :template_filenames
157
+ attr_accessor :file_filenames
158
+ attr_accessor :library_filenames
159
+ attr_accessor :resource_filenames
160
+ attr_accessor :provider_filenames
161
+ attr_accessor :root_filenames
162
+ attr_accessor :name
163
+ attr_accessor :metadata
164
+ attr_accessor :metadata_filenames
165
+ attr_accessor :status
166
+ attr_accessor :couchdb_rev
167
+ attr_accessor :couchdb
168
+
169
+ attr_reader :couchdb_id
170
+
171
+ # attribute_filenames also has a setter that has non-default
172
+ # functionality.
173
+ attr_reader :attribute_filenames
174
+
175
+ # recipe_filenames also has a setter that has non-default
176
+ # functionality.
177
+ attr_reader :recipe_filenames
178
+
179
+ attr_reader :recipe_filenames_by_name
180
+ attr_reader :attribute_filenames_by_short_filename
170
181
 
171
182
  # This is the one and only method that knows how cookbook files'
172
183
  # checksums are generated.
@@ -200,7 +211,7 @@ class Chef
200
211
  @status = :ready
201
212
  @manifest = nil
202
213
  @file_vendor = nil
203
- @metadata = {}
214
+ @metadata = Chef::Cookbook::Metadata.new
204
215
  end
205
216
 
206
217
  def version
@@ -308,17 +319,23 @@ class Chef
308
319
  unless recipe_filenames_by_name.has_key?(recipe_name)
309
320
  raise ArgumentError, "Cannot find a recipe matching #{recipe_name} in cookbook #{name}"
310
321
  end
322
+
311
323
  Chef::Log.debug("Found recipe #{recipe_name} in cookbook #{name}")
312
324
  recipe = Chef::Recipe.new(name, recipe_name, run_context)
313
325
  recipe_filename = recipe_filenames_by_name[recipe_name]
314
- raise Chef::Exceptions::RecipeNotFound, "could not find recipe #{recipe_name} for cookbook #{name}" unless recipe_filename
326
+
327
+ unless recipe_filename
328
+ raise Chef::Exceptions::RecipeNotFound, "could not find recipe #{recipe_name} for cookbook #{name}"
329
+ end
315
330
 
316
331
  recipe.from_file(recipe_filename)
317
332
  recipe
318
333
  end
319
334
 
320
335
  def segment_filenames(segment)
321
- raise ArgumentError, "invalid segment #{segment}: must be one of #{COOKBOOK_SEGMENTS.join(', ')}" unless COOKBOOK_SEGMENTS.include?(segment)
336
+ unless COOKBOOK_SEGMENTS.include?(segment)
337
+ raise ArgumentError, "invalid segment #{segment}: must be one of #{COOKBOOK_SEGMENTS.join(', ')}"
338
+ end
322
339
 
323
340
  case segment.to_sym
324
341
  when :resources
@@ -398,9 +415,9 @@ class Chef
398
415
  # we're just going to make cookbook_files out of these and make the
399
416
  # cookbook find them by filespecificity again. but it's the shortest
400
417
  # path to "success" for now.
401
- if manifest_record_path =~ /(#{segment}\/[^\/]+\/#{dirname})\/.+$/
418
+ if manifest_record_path =~ /(#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)})\/.+$/
402
419
  specificity_dirname = $1
403
- non_specific_path = manifest_record_path[/#{segment}\/[^\/]+\/#{dirname}\/(.+)$/, 1]
420
+ non_specific_path = manifest_record_path[/#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)}\/(.+)$/, 1]
404
421
  # Record the specificity_dirname only if it's in the list of
405
422
  # valid preferences
406
423
  if filenames_by_pref[specificity_dirname]
@@ -429,7 +446,7 @@ class Chef
429
446
  manifest_record_path = manifest_record[:path]
430
447
 
431
448
  # extract the preference part from the path.
432
- if manifest_record_path =~ /(#{segment}\/[^\/]+\/#{dirname})\/.+$/
449
+ if manifest_record_path =~ /(#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)})\/.+$/
433
450
  # Note the specificy_dirname includes the segment and
434
451
  # dirname argument as above, which is what
435
452
  # preferences_for_path returns. It could be
@@ -461,7 +478,7 @@ class Chef
461
478
  platform, version = Chef::Platform.find_platform_and_version(node)
462
479
  rescue ArgumentError => e
463
480
  # Skip platform/version if they were not found by find_platform_and_version
464
- if e.message =~ /Cannot find a (platform|version)/
481
+ if e.message =~ /Cannot find a (?:platform|version)/
465
482
  platform = "/unknown_platform/"
466
483
  version = "/unknown_platform_version/"
467
484
  else
@@ -504,9 +521,9 @@ class Chef
504
521
  # we're just going to make cookbook_files out of these and make the
505
522
  # cookbook find them by filespecificity again. but it's the shortest
506
523
  # path to "success" for now.
507
- if manifest_record_path =~ /(#{segment}\/[^\/]+\/#{dirname})\/.+$/
524
+ if manifest_record_path =~ /(#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)})\/.+$/
508
525
  specificity_dirname = $1
509
- non_specific_path = manifest_record_path[/#{segment}\/[^\/]+\/#{dirname}\/(.+)$/, 1]
526
+ non_specific_path = manifest_record_path[/#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)}\/(.+)$/, 1]
510
527
  # Record the specificity_dirname only if it's in the list of
511
528
  # valid preferences
512
529
  if filenames_by_pref[specificity_dirname]
@@ -535,7 +552,7 @@ class Chef
535
552
  manifest_record_path = manifest_record[:path]
536
553
 
537
554
  # extract the preference part from the path.
538
- if manifest_record_path =~ /(#{segment}\/[^\/]+\/#{dirname})\/.+$/
555
+ if manifest_record_path =~ /(#{Regexp.escape(segment.to_s)}\/[^\/]+\/#{Regexp.escape(dirname)})\/.+$/
539
556
  # Note the specificy_dirname includes the segment and
540
557
  # dirname argument as above, which is what
541
558
  # preferences_for_path returns. It could be
@@ -567,7 +584,7 @@ class Chef
567
584
  platform, version = Chef::Platform.find_platform_and_version(node)
568
585
  rescue ArgumentError => e
569
586
  # Skip platform/version if they were not found by find_platform_and_version
570
- if e.message =~ /Cannot find a (platform|version)/
587
+ if e.message =~ /Cannot find a (?:platform|version)/
571
588
  platform = "/unknown_platform/"
572
589
  version = "/unknown_platform_version/"
573
590
  else
@@ -780,11 +797,11 @@ class Chef
780
797
  specificity = "default"
781
798
 
782
799
  if segment == :root_files
783
- matcher = segment_file.match(".+/#{name}/(.+)")
800
+ matcher = segment_file.match(".+/#{Regexp.escape(name.to_s)}/(.+)")
784
801
  file_name = matcher[1]
785
802
  path = file_name
786
803
  elsif segment == :templates || segment == :files
787
- matcher = segment_file.match("/#{name}/(#{segment}/(.+?)/(.+))")
804
+ matcher = segment_file.match("/#{Regexp.escape(name.to_s)}/(#{Regexp.escape(segment.to_s)}/(.+?)/(.+))")
788
805
  unless matcher
789
806
  Chef::Log.debug("Skipping file #{segment_file}, as it doesn't have a proper segment.")
790
807
  next
@@ -793,7 +810,7 @@ class Chef
793
810
  specificity = matcher[2]
794
811
  file_name = matcher[3]
795
812
  else
796
- matcher = segment_file.match("/#{name}/(#{segment}/(.+))")
813
+ matcher = segment_file.match("/#{Regexp.escape(name.to_s)}/(#{Regexp.escape(segment.to_s)}/(.+))")
797
814
  path = matcher[1]
798
815
  file_name = matcher[2]
799
816
  end