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.
- data/lib/chef/application.rb +19 -3
- data/lib/chef/application/client.rb +7 -8
- data/lib/chef/application/knife.rb +9 -3
- data/lib/chef/application/solo.rb +5 -5
- data/lib/chef/cache/checksum.rb +2 -2
- data/lib/chef/client.rb +30 -64
- data/lib/chef/config.rb +52 -30
- data/lib/chef/cookbook/metadata.rb +12 -1
- data/lib/chef/cookbook_site_streaming_uploader.rb +211 -0
- data/lib/chef/cookbook_uploader.rb +102 -0
- data/lib/chef/cookbook_version.rb +47 -30
- data/lib/chef/couchdb.rb +1 -11
- data/lib/chef/exceptions.rb +1 -0
- data/lib/chef/file_access_control.rb +1 -1
- data/lib/chef/index_queue/amqp_client.rb +10 -3
- data/lib/chef/knife.rb +154 -58
- data/lib/chef/knife/bootstrap.rb +84 -48
- data/lib/chef/knife/bootstrap/centos5-gems.erb +41 -0
- data/lib/chef/knife/bootstrap/fedora13-gems.erb +38 -0
- data/lib/chef/knife/bootstrap/ubuntu10.04-gems.erb +44 -0
- data/lib/chef/knife/client_bulk_delete.rb +1 -1
- data/lib/chef/knife/client_create.rb +1 -1
- data/lib/chef/knife/client_delete.rb +1 -1
- data/lib/chef/knife/client_edit.rb +1 -1
- data/lib/chef/knife/client_list.rb +1 -1
- data/lib/chef/knife/client_reregister.rb +1 -1
- data/lib/chef/knife/client_show.rb +1 -1
- data/lib/chef/knife/configure.rb +1 -1
- data/lib/chef/knife/configure_client.rb +1 -1
- data/lib/chef/knife/cookbook_bulk_delete.rb +1 -1
- data/lib/chef/knife/cookbook_create.rb +159 -0
- data/lib/chef/knife/cookbook_delete.rb +6 -6
- data/lib/chef/knife/cookbook_download.rb +1 -1
- data/lib/chef/knife/cookbook_list.rb +1 -1
- data/lib/chef/knife/cookbook_metadata.rb +1 -1
- data/lib/chef/knife/cookbook_metadata_from_file.rb +1 -1
- data/lib/chef/knife/cookbook_show.rb +1 -1
- data/lib/chef/knife/cookbook_site_download.rb +2 -1
- data/lib/chef/knife/cookbook_site_list.rb +2 -1
- data/lib/chef/knife/cookbook_site_search.rb +2 -1
- data/lib/chef/knife/cookbook_site_share.rb +108 -0
- data/lib/chef/knife/cookbook_site_show.rb +2 -1
- data/lib/chef/knife/cookbook_site_unshare.rb +52 -0
- data/lib/chef/knife/cookbook_site_vendor.rb +3 -2
- data/lib/chef/knife/cookbook_test.rb +1 -1
- data/lib/chef/knife/cookbook_upload.rb +22 -155
- data/lib/chef/knife/data_bag_create.rb +2 -1
- data/lib/chef/knife/data_bag_delete.rb +2 -1
- data/lib/chef/knife/data_bag_edit.rb +2 -1
- data/lib/chef/knife/data_bag_from_file.rb +2 -1
- data/lib/chef/knife/data_bag_list.rb +2 -1
- data/lib/chef/knife/data_bag_show.rb +2 -1
- data/lib/chef/knife/ec2_instance_data.rb +1 -1
- data/lib/chef/knife/ec2_server_create.rb +16 -4
- data/lib/chef/knife/ec2_server_delete.rb +8 -2
- data/lib/chef/knife/ec2_server_list.rb +8 -2
- data/lib/chef/knife/index_rebuild.rb +1 -1
- data/lib/chef/knife/node_bulk_delete.rb +1 -1
- data/lib/chef/knife/node_create.rb +1 -1
- data/lib/chef/knife/node_delete.rb +1 -1
- data/lib/chef/knife/node_edit.rb +1 -1
- data/lib/chef/knife/node_from_file.rb +1 -1
- data/lib/chef/knife/node_list.rb +1 -1
- data/lib/chef/knife/node_run_list_add.rb +1 -1
- data/lib/chef/knife/node_run_list_remove.rb +1 -1
- data/lib/chef/knife/node_show.rb +1 -1
- data/lib/chef/knife/rackspace_server_create.rb +1 -1
- data/lib/chef/knife/rackspace_server_delete.rb +1 -1
- data/lib/chef/knife/rackspace_server_list.rb +1 -1
- data/lib/chef/knife/recipe_list.rb +1 -1
- data/lib/chef/knife/role_bulk_delete.rb +1 -1
- data/lib/chef/knife/role_create.rb +1 -1
- data/lib/chef/knife/role_delete.rb +1 -1
- data/lib/chef/knife/role_edit.rb +1 -1
- data/lib/chef/knife/role_from_file.rb +1 -1
- data/lib/chef/knife/role_list.rb +1 -1
- data/lib/chef/knife/role_show.rb +1 -1
- data/lib/chef/knife/search.rb +1 -1
- data/lib/chef/knife/slicehost_images_list.rb +1 -1
- data/lib/chef/knife/slicehost_server_create.rb +1 -1
- data/lib/chef/knife/slicehost_server_delete.rb +1 -1
- data/lib/chef/knife/slicehost_server_list.rb +1 -1
- data/lib/chef/knife/ssh.rb +49 -9
- data/lib/chef/knife/status.rb +2 -2
- data/lib/chef/knife/terremark_server_create.rb +1 -1
- data/lib/chef/knife/terremark_server_delete.rb +1 -1
- data/lib/chef/knife/terremark_server_list.rb +1 -1
- data/lib/chef/mixin/command.rb +17 -204
- data/lib/chef/mixin/command/unix.rb +215 -0
- data/lib/chef/mixin/command/windows.rb +72 -0
- data/lib/chef/mixin/find_preferred_file.rb +3 -3
- data/lib/chef/mixin/language.rb +64 -23
- data/lib/chef/node.rb +27 -3
- data/lib/chef/node/attribute.rb +20 -10
- data/lib/chef/platform.rb +3 -9
- data/lib/chef/provider/cron.rb +1 -1
- data/lib/chef/provider/deploy.rb +4 -1
- data/lib/chef/provider/group.rb +1 -1
- data/lib/chef/provider/group/dscl.rb +2 -2
- data/lib/chef/provider/mount/mount.rb +6 -6
- data/lib/chef/provider/package/easy_install.rb +8 -7
- data/lib/chef/provider/package/pacman.rb +1 -1
- data/lib/chef/provider/package/rpm.rb +4 -4
- data/lib/chef/provider/package/solaris.rb +127 -0
- data/lib/chef/provider/package/yum.rb +51 -28
- data/lib/chef/provider/remote_directory.rb +5 -2
- data/lib/chef/provider/remote_file.rb +1 -1
- data/lib/chef/provider/ruby_block.rb +1 -0
- data/lib/chef/provider/script.rb +2 -2
- data/lib/chef/provider/service/debian.rb +40 -4
- data/lib/chef/provider/service/freebsd.rb +1 -1
- data/lib/chef/provider/service/upstart.rb +3 -10
- data/lib/chef/provider/user/dscl.rb +2 -2
- data/lib/chef/providers.rb +1 -0
- data/lib/chef/resource.rb +7 -8
- data/lib/chef/resource/service.rb +17 -0
- data/lib/chef/resource/solaris_package.rb +36 -0
- data/lib/chef/resource/yum_package.rb +11 -4
- data/lib/chef/rest/rest_request.rb +18 -1
- data/lib/chef/run_list/run_list_expansion.rb +4 -0
- data/lib/chef/run_list/run_list_item.rb +8 -1
- data/lib/chef/shef/ext.rb +1 -1
- data/lib/chef/shef/shef_session.rb +7 -3
- data/lib/chef/tasks/chef_repo.rake +1 -0
- data/lib/chef/version.rb +2 -1
- metadata +23 -8
- 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
|
-
|
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
|
-
|
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
|