chef 0.9.0.b01 → 0.9.0.b02
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.
- data/lib/chef/cookbook/metadata/version.rb +2 -2
- data/lib/chef/cookbook/syntax_check.rb +128 -0
- data/lib/chef/cookbook_version.rb +17 -0
- data/lib/chef/file_access_control.rb +8 -3
- data/lib/chef/knife.rb +20 -7
- data/lib/chef/knife/cookbook_bulk_delete.rb +21 -6
- data/lib/chef/knife/cookbook_delete.rb +97 -5
- data/lib/chef/knife/cookbook_download.rb +55 -13
- data/lib/chef/knife/cookbook_upload.rb +14 -40
- data/lib/chef/knife/recipe_list.rb +32 -0
- data/lib/chef/knife/ssh.rb +33 -0
- data/lib/chef/shef/shef_session.rb +1 -1
- data/lib/chef/version.rb +1 -1
- metadata +5 -3
@@ -32,13 +32,13 @@ class Chef::Cookbook::Metadata
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def _parse(str="")
|
35
|
-
@major, @minor, @patch = case str
|
35
|
+
@major, @minor, @patch = case str.to_s
|
36
36
|
when /^(\d+)\.(\d+)\.(\d+)$/
|
37
37
|
[ $1.to_i, $2.to_i, $3.to_i ]
|
38
38
|
when /^(\d+)\.(\d+)$/
|
39
39
|
[ $1.to_i, $2.to_i, 0 ]
|
40
40
|
else
|
41
|
-
raise "Metadata version does not match 'x.y.z' or 'x.y'"
|
41
|
+
raise "Metadata version '#{str.to_s}' does not match 'x.y.z' or 'x.y'"
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -0,0 +1,128 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Daniel DeLeo (<dan@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2010 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/cache/checksum'
|
20
|
+
require 'chef/mixin/shell_out'
|
21
|
+
|
22
|
+
class Chef
|
23
|
+
class Cookbook
|
24
|
+
class SyntaxCheck
|
25
|
+
include Chef::Mixin::ShellOut
|
26
|
+
|
27
|
+
attr_reader :cookbook_path
|
28
|
+
|
29
|
+
def self.for_cookbook(cookbook_name, cookbook_path=nil)
|
30
|
+
cookbook_path ||= Chef::Config.cookbook_path
|
31
|
+
unless cookbook_path
|
32
|
+
raise ArgumentError, "Cannot find cookbook #{cookbook_name} unless Chef::Config.cookbook_path is set or an explicit cookbook path is given"
|
33
|
+
end
|
34
|
+
new(File.join(cookbook_path, cookbook_name.to_s))
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(cookbook_path)
|
38
|
+
@cookbook_path = cookbook_path
|
39
|
+
end
|
40
|
+
|
41
|
+
def cache
|
42
|
+
Chef::Cache::Checksum.instance
|
43
|
+
end
|
44
|
+
|
45
|
+
def ruby_files
|
46
|
+
Dir[File.join(cookbook_path, '**', '*.rb')]
|
47
|
+
end
|
48
|
+
|
49
|
+
def untested_ruby_files
|
50
|
+
ruby_files.reject do |file|
|
51
|
+
if validated?(file)
|
52
|
+
Chef::Log.debug("ruby file #{file} is unchanged, skipping syntax check")
|
53
|
+
true
|
54
|
+
else
|
55
|
+
false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def template_files
|
61
|
+
Dir[File.join(cookbook_path, '**', '*.erb')]
|
62
|
+
end
|
63
|
+
|
64
|
+
def untested_template_files
|
65
|
+
template_files.reject do |file|
|
66
|
+
if validated?(file)
|
67
|
+
Chef::Log.debug("template #{file} is unchanged, skipping syntax check")
|
68
|
+
true
|
69
|
+
else
|
70
|
+
false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def validated?(file)
|
76
|
+
!!cache.lookup_checksum(cache_key(file), File.stat(file))
|
77
|
+
end
|
78
|
+
|
79
|
+
def validated(file)
|
80
|
+
cache.generate_checksum(cache_key(file), file, File.stat(file))
|
81
|
+
end
|
82
|
+
|
83
|
+
def cache_key(file)
|
84
|
+
@cache_keys ||= {}
|
85
|
+
@cache_keys[file] ||= cache.generate_key(file, "chef-test")
|
86
|
+
end
|
87
|
+
|
88
|
+
def validate_ruby_files
|
89
|
+
untested_ruby_files.each do |ruby_file|
|
90
|
+
return false unless validate_ruby_file(ruby_file)
|
91
|
+
validated(ruby_file)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def validate_templates
|
96
|
+
untested_template_files.each do |template|
|
97
|
+
return false unless validate_template(template)
|
98
|
+
validated(template)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def validate_template(erb_file)
|
103
|
+
Chef::Log.debug("Testing template #{erb_file} for syntax errors...")
|
104
|
+
result = shell_out("sh -c 'erubis -x #{erb_file} | ruby -c'")
|
105
|
+
result.error!
|
106
|
+
true
|
107
|
+
rescue Chef::Exceptions::ShellCommandFailed
|
108
|
+
file_relative_path = erb_file[/^#{Regexp.escape(cookbook_path+File::Separator)}(.*)/, 1]
|
109
|
+
Chef::Log.fatal("Erb template #{file_relative_path} has a syntax error:")
|
110
|
+
result.stderr.each_line { |l| Chef::Log.fatal(l.chomp) }
|
111
|
+
false
|
112
|
+
end
|
113
|
+
|
114
|
+
def validate_ruby_file(ruby_file)
|
115
|
+
Chef::Log.debug("Testing #{ruby_file} for syntax errors...")
|
116
|
+
result = shell_out("ruby -c #{ruby_file}")
|
117
|
+
result.error!
|
118
|
+
true
|
119
|
+
rescue Chef::Exceptions::ShellCommandFailed
|
120
|
+
file_relative_path = ruby_file[/^#{Regexp.escape(cookbook_path+File::Separator)}(.*)/, 1]
|
121
|
+
Chef::Log.fatal("Cookbook file #{file_relative_path} has a ruby syntax error:")
|
122
|
+
result.stderr.each_line { |l| Chef::Log.fatal(l.chomp) }
|
123
|
+
false
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -664,6 +664,23 @@ class Chef
|
|
664
664
|
chef_server_rest.get_rest('cookbooks')
|
665
665
|
end
|
666
666
|
|
667
|
+
##
|
668
|
+
# Given a +cookbook_name+, get a list of all versions that exist on the
|
669
|
+
# server.
|
670
|
+
# ===Returns
|
671
|
+
# [String]:: Array of cookbook versions, which are strings like 'x.y.z'
|
672
|
+
# nil:: if the cookbook doesn't exist. an error will also be logged.
|
673
|
+
def self.available_versions(cookbook_name)
|
674
|
+
chef_server_rest.get_rest("cookbooks/#{cookbook_name}").values.flatten
|
675
|
+
rescue Net::HTTPServerException => e
|
676
|
+
if e.to_s =~ /^404/
|
677
|
+
Chef::Log.error("Cannot find a cookbook named #{cookbook_name}")
|
678
|
+
nil
|
679
|
+
else
|
680
|
+
raise
|
681
|
+
end
|
682
|
+
end
|
683
|
+
|
667
684
|
# Get the newest version of all cookbooks
|
668
685
|
def self.latest_cookbooks
|
669
686
|
chef_server_rest.get_rest('cookbooks/_latest')
|
@@ -16,6 +16,9 @@
|
|
16
16
|
# See the License for the specific language governing permissions and
|
17
17
|
# limitations under the License.
|
18
18
|
#
|
19
|
+
|
20
|
+
require 'chef/log'
|
21
|
+
|
19
22
|
class Chef
|
20
23
|
|
21
24
|
# FileAccessControl objects set the owner, group and mode of +file+ to
|
@@ -55,7 +58,7 @@ class Chef
|
|
55
58
|
|
56
59
|
# Workaround the fact that Ruby's Etc module doesn't believe in negative
|
57
60
|
# uids, so negative uids show up as the diminished radix complement of
|
58
|
-
#
|
61
|
+
# a uint. For example, a uid of -2 is reported as 4294967294
|
59
62
|
def dimished_radix_complement(int)
|
60
63
|
if int > UID_MAX
|
61
64
|
int - UINT
|
@@ -71,10 +74,11 @@ class Chef
|
|
71
74
|
elsif resource.owner.kind_of?(Integer)
|
72
75
|
resource.owner
|
73
76
|
else
|
77
|
+
Chef::Log.error("The `owner` parameter of the #@resource resource is set to an invalid value (#{resource.owner.inspect})")
|
74
78
|
raise ArgumentError, "cannot resolve #{resource.owner.inspect} to uid, owner must be a string or integer"
|
75
79
|
end
|
76
80
|
rescue ArgumentError
|
77
|
-
raise Chef::Exceptions::UserIDNotFound, "cannot
|
81
|
+
raise Chef::Exceptions::UserIDNotFound, "cannot determine user id for '#{resource.owner}', does the user exist on this system?"
|
78
82
|
end
|
79
83
|
|
80
84
|
def set_owner
|
@@ -92,10 +96,11 @@ class Chef
|
|
92
96
|
elsif resource.group.kind_of?(Integer)
|
93
97
|
resource.group
|
94
98
|
else
|
99
|
+
Chef::Log.error("The `group` parameter of the #@resource resource is set to an invalid value (#{resource.owner.inspect})")
|
95
100
|
raise ArgumentError, "cannot resolve #{resource.group.inspect} to gid, group must be a string or integer"
|
96
101
|
end
|
97
102
|
rescue ArgumentError
|
98
|
-
raise Chef::Exceptions::GroupIDNotFound, "cannot
|
103
|
+
raise Chef::Exceptions::GroupIDNotFound, "cannot determine group id for '#{resource.group}', does the group exist on this system?"
|
99
104
|
end
|
100
105
|
|
101
106
|
def set_group
|
data/lib/chef/knife.rb
CHANGED
@@ -46,7 +46,7 @@ class Chef
|
|
46
46
|
load_commands
|
47
47
|
@sub_classes.keys.sort.each do |snake_case|
|
48
48
|
klass_instance = build_sub_class(snake_case)
|
49
|
-
klass_instance.parse_options
|
49
|
+
klass_instance.parse_options([])
|
50
50
|
puts klass_instance.opt_parser
|
51
51
|
puts
|
52
52
|
end
|
@@ -98,6 +98,14 @@ class Chef
|
|
98
98
|
klass_instance
|
99
99
|
end
|
100
100
|
|
101
|
+
def parse_options(args)
|
102
|
+
super
|
103
|
+
rescue OptionParser::InvalidOption => e
|
104
|
+
puts "Error: " + e.to_s
|
105
|
+
show_usage
|
106
|
+
exit(1)
|
107
|
+
end
|
108
|
+
|
101
109
|
def ask_question(question, opts={})
|
102
110
|
question = question + "[#{opts[:default]}] " if opts[:default]
|
103
111
|
|
@@ -159,17 +167,17 @@ class Chef
|
|
159
167
|
def output(data)
|
160
168
|
case config[:format]
|
161
169
|
when "json", nil
|
162
|
-
puts JSON.pretty_generate(data)
|
170
|
+
stdout.puts JSON.pretty_generate(data)
|
163
171
|
when "yaml"
|
164
172
|
require 'yaml'
|
165
|
-
puts YAML::dump(data)
|
173
|
+
stdout.puts YAML::dump(data)
|
166
174
|
when "text"
|
167
175
|
# If you were looking for some attribute and there is only one match
|
168
176
|
# just dump the attribute value
|
169
177
|
if data.length == 1 and config[:attribute]
|
170
|
-
puts data.values[0]
|
178
|
+
stdout.puts data.values[0]
|
171
179
|
else
|
172
|
-
pp
|
180
|
+
PP.pp(data, stdout)
|
173
181
|
end
|
174
182
|
else
|
175
183
|
raise ArgumentError, "Unknown output format #{config[:format]}"
|
@@ -224,10 +232,11 @@ class Chef
|
|
224
232
|
parse_output ? JSON.parse(output) : output
|
225
233
|
end
|
226
234
|
|
227
|
-
def confirm(question)
|
235
|
+
def confirm(question, append_instructions=true)
|
228
236
|
return true if config[:yes]
|
229
237
|
|
230
|
-
print
|
238
|
+
print question
|
239
|
+
print "? (Y/N) " if append_instructions
|
231
240
|
answer = stdin.readline
|
232
241
|
answer.chomp!
|
233
242
|
case answer
|
@@ -243,6 +252,10 @@ class Chef
|
|
243
252
|
end
|
244
253
|
end
|
245
254
|
|
255
|
+
def show_usage
|
256
|
+
stdout.puts("USAGE: " + self.opt_parser.to_s)
|
257
|
+
end
|
258
|
+
|
246
259
|
def load_from_file(klass, from_file, bag=nil)
|
247
260
|
relative_path = ""
|
248
261
|
if klass == Chef::Role
|
@@ -1,6 +1,7 @@
|
|
1
1
|
#
|
2
2
|
# Author:: Adam Jacob (<adam@opscode.com>)
|
3
|
-
#
|
3
|
+
# Author:: Daniel DeLeo (<dan@opscode.com>)
|
4
|
+
# Copyright:: Copyright (c) 2009, 2010 Opscode, Inc.
|
4
5
|
# License:: Apache License, Version 2.0
|
5
6
|
#
|
6
7
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -17,6 +18,7 @@
|
|
17
18
|
#
|
18
19
|
|
19
20
|
require 'chef/knife'
|
21
|
+
require 'chef/knife/cookbook_delete'
|
20
22
|
|
21
23
|
class Chef
|
22
24
|
class Knife
|
@@ -24,13 +26,26 @@ class Chef
|
|
24
26
|
|
25
27
|
banner "Sub-Command: cookbook bulk delete REGEX (options)"
|
26
28
|
|
27
|
-
def run
|
28
|
-
|
29
|
+
def run
|
30
|
+
unless regex_str = @name_args.first
|
29
31
|
Chef::Log.fatal("You must supply a regular expression to match the results against")
|
30
32
|
exit 42
|
31
|
-
|
32
|
-
|
33
|
-
|
33
|
+
end
|
34
|
+
|
35
|
+
regex = Regexp.new(regex_str)
|
36
|
+
|
37
|
+
all_cookbooks = Chef::CookbookVersion.list
|
38
|
+
cookbooks_names = all_cookbooks.keys.grep(regex)
|
39
|
+
cookbooks_to_delete = cookbooks_names.inject({}) { |hash, name| hash[name] = all_cookbooks[name];hash }
|
40
|
+
output(format_list_for_display(cookbooks_to_delete))
|
41
|
+
|
42
|
+
confirm("Do you really want to delete these cookbooks? All versions will be deleted. (Y/N) ", false)
|
43
|
+
|
44
|
+
cookbooks_names.each do |cookbook_name|
|
45
|
+
versions = rest.get_rest("cookbooks/#{cookbook_name}").values.flatten
|
46
|
+
versions.each do |version|
|
47
|
+
object = rest.delete_rest("cookbooks/#{cookbook_name}/#{version}")
|
48
|
+
Chef::Log.info("Deleted cookbook #{cookbook_name.ljust(25)} [#{version}]")
|
34
49
|
end
|
35
50
|
end
|
36
51
|
end
|
@@ -21,21 +21,113 @@ require 'chef/knife'
|
|
21
21
|
class Chef
|
22
22
|
class Knife
|
23
23
|
class CookbookDelete < Knife
|
24
|
+
|
25
|
+
option :all, :short => '-a', :long => '--all', :boolean => true, :description => 'delete all versions'
|
24
26
|
|
25
27
|
banner "Sub-Command: cookbook delete COOKBOOK VERSION (options)"
|
26
28
|
|
27
29
|
def run
|
28
|
-
|
29
|
-
|
30
|
+
@cookbook_name, @version = name_args
|
31
|
+
if @cookbook_name && @version
|
32
|
+
delete_explicit_version
|
33
|
+
elsif @cookbook_name && config[:all]
|
34
|
+
delete_all_versions
|
35
|
+
elsif @cookbook_name && @version.nil?
|
36
|
+
delete_without_explicit_version
|
37
|
+
elsif @cookbook_name.nil?
|
38
|
+
show_usage
|
39
|
+
Chef::Log.fatal("You must provide the name of the cookbook to delete")
|
40
|
+
exit(1)
|
30
41
|
end
|
31
42
|
end
|
32
43
|
|
33
|
-
|
34
|
-
|
35
|
-
|
44
|
+
def delete_explicit_version
|
45
|
+
delete_object(Chef::CookbookVersion, "#{@cookbook_name} version #{@version}", "cookbook") do
|
46
|
+
rest.delete_rest("cookbooks/#{@cookbook_name}/#{@version}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def delete_all_versions
|
51
|
+
confirm("Do you really want to delete all versions of #{@cookbook_name}")
|
52
|
+
delete_all_without_confirmation
|
53
|
+
end
|
54
|
+
|
55
|
+
def delete_all_without_confirmation
|
56
|
+
# look up the available versions again just in case the user
|
57
|
+
# got to the list of versions to delete and selected 'all'
|
58
|
+
# and also a specific version
|
59
|
+
@available_versions = nil
|
60
|
+
Array(available_versions).each do |version|
|
61
|
+
delete_version_without_confirmation(version)
|
62
|
+
end
|
63
|
+
end
|
36
64
|
|
65
|
+
def delete_without_explicit_version
|
66
|
+
if available_versions.nil?
|
67
|
+
# we already logged an error or 2 about it, so just bail
|
68
|
+
exit(1)
|
69
|
+
elsif available_versions.size == 1
|
70
|
+
@version = available_versions.first
|
71
|
+
delete_explicit_version
|
72
|
+
else
|
73
|
+
versions_to_delete = ask_which_versions_to_delete
|
74
|
+
delete_versions_without_confirmation(versions_to_delete)
|
75
|
+
end
|
76
|
+
end
|
37
77
|
|
78
|
+
def available_versions
|
79
|
+
@available_versions ||= rest.get_rest("cookbooks/#{@cookbook_name}").values.flatten
|
80
|
+
rescue Net::HTTPServerException => e
|
81
|
+
if e.to_s =~ /^404/
|
82
|
+
Chef::Log.error("Cannot find a cookbook named #{@cookbook_name} to delete")
|
83
|
+
nil
|
84
|
+
else
|
85
|
+
raise
|
86
|
+
end
|
87
|
+
end
|
38
88
|
|
89
|
+
def ask_which_versions_to_delete
|
90
|
+
question = "Which version(s) do you want to delete?\n"
|
91
|
+
valid_responses = {}
|
92
|
+
available_versions.each_with_index do |version, index|
|
93
|
+
valid_responses[(index + 1).to_s] = version
|
94
|
+
question << "#{index + 1}. #{@cookbook_name} #{version}\n"
|
95
|
+
end
|
96
|
+
valid_responses[(available_versions.size + 1).to_s] = :all
|
97
|
+
question << "#{available_versions.size + 1}. All versions\n\n"
|
98
|
+
responses = ask_question(question).split(',').map { |response| response.strip }
|
99
|
+
|
100
|
+
if responses.empty?
|
101
|
+
Chef::Log.error("No versions specified, exiting")
|
102
|
+
exit(1)
|
103
|
+
end
|
104
|
+
versions = responses.map do |response|
|
105
|
+
if version = valid_responses[response]
|
106
|
+
version
|
107
|
+
else
|
108
|
+
Chef::Log.error("#{response} is not a valid choice, skipping it")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
versions.compact
|
112
|
+
end
|
39
113
|
|
114
|
+
def delete_version_without_confirmation(version)
|
115
|
+
object = rest.delete_rest("cookbooks/#{@cookbook_name}/#{version}")
|
116
|
+
output(format_for_display(object)) if config[:print_after]
|
117
|
+
Chef::Log.info("Deleted cookbook[#{@cookbook_name}][#{version}]")
|
118
|
+
end
|
40
119
|
|
120
|
+
def delete_versions_without_confirmation(versions)
|
121
|
+
versions.each do |version|
|
122
|
+
if version == :all
|
123
|
+
delete_all_without_confirmation
|
124
|
+
break
|
125
|
+
else
|
126
|
+
delete_version_without_confirmation(version)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
41
130
|
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -23,12 +23,13 @@ class Chef
|
|
23
23
|
class Knife
|
24
24
|
class CookbookDownload < Knife
|
25
25
|
|
26
|
-
banner "Sub-Command: cookbook download COOKBOOK VERSION (options)"
|
26
|
+
banner "Sub-Command: cookbook download COOKBOOK [VERSION] (options)"
|
27
27
|
|
28
|
-
option :
|
29
|
-
:short => "-
|
30
|
-
:long => "--
|
31
|
-
:description => "The version of the cookbook to download"
|
28
|
+
option :latest,
|
29
|
+
:short => "-N",
|
30
|
+
:long => "--latest",
|
31
|
+
:description => "The version of the cookbook to download",
|
32
|
+
:boolean => true
|
32
33
|
|
33
34
|
option :download_directory,
|
34
35
|
:short => "-d DOWNLOAD_DIRECTORY",
|
@@ -45,19 +46,22 @@ class Chef
|
|
45
46
|
# specificity for downloads - need to implement --platform and
|
46
47
|
# --fqdn here
|
47
48
|
def run
|
48
|
-
|
49
|
-
|
50
|
-
|
49
|
+
@cookbook_name, @version = @name_args
|
50
|
+
|
51
|
+
if @cookbook_name.nil?
|
52
|
+
show_usage
|
53
|
+
Chef::Log.fatal("You must specify a cookbook name")
|
54
|
+
exit 1
|
55
|
+
elsif @version.nil?
|
56
|
+
determine_version
|
51
57
|
end
|
52
58
|
|
53
|
-
cookbook_name
|
54
|
-
cookbook_version = @name_args[1] == 'latest' ? '_latest' : @name_args[1]
|
55
|
-
Chef::Log.info("Downloading #{cookbook_name} cookbook version #{cookbook_version}")
|
59
|
+
Chef::Log.info("Downloading #{@cookbook_name} cookbook version #{@version}")
|
56
60
|
|
57
|
-
cookbook = rest.get_rest("cookbooks/#{cookbook_name}/#{
|
61
|
+
cookbook = rest.get_rest("cookbooks/#{@cookbook_name}/#{@version}")
|
58
62
|
manifest = cookbook.manifest
|
59
63
|
|
60
|
-
basedir = File.join(config[:download_directory], "#{cookbook_name}-#{cookbook.version}")
|
64
|
+
basedir = File.join(config[:download_directory], "#{@cookbook_name}-#{cookbook.version}")
|
61
65
|
if File.exists?(basedir)
|
62
66
|
if config[:force]
|
63
67
|
Chef::Log.debug("Deleting #{basedir}")
|
@@ -83,6 +87,44 @@ class Chef
|
|
83
87
|
Chef::Log.info("Cookbook downloaded to #{basedir}")
|
84
88
|
end
|
85
89
|
|
90
|
+
def determine_version
|
91
|
+
if available_versions.size == 1
|
92
|
+
@version = available_versions.first
|
93
|
+
elsif config[:latest]
|
94
|
+
@version = available_versions.map { |v| Chef::Cookbook::Metadata::Version.new(v) }.sort.last
|
95
|
+
else
|
96
|
+
ask_which_version
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def available_versions
|
101
|
+
@available_versions ||= begin
|
102
|
+
versions = Chef::CookbookVersion.available_versions(@cookbook_name).map do |version|
|
103
|
+
Chef::Cookbook::Metadata::Version.new(version)
|
104
|
+
end
|
105
|
+
versions.sort!
|
106
|
+
versions
|
107
|
+
end
|
108
|
+
#pp :available_versions => @available_versions
|
109
|
+
@available_versions
|
110
|
+
end
|
111
|
+
|
112
|
+
def ask_which_version
|
113
|
+
question = "Which version do you want to download?\n"
|
114
|
+
valid_responses = {}
|
115
|
+
available_versions.each_with_index do |version, index|
|
116
|
+
valid_responses[(index + 1).to_s] = version
|
117
|
+
question << "#{index + 1}. #{@cookbook_name} #{version}\n"
|
118
|
+
end
|
119
|
+
question += "\n"
|
120
|
+
response = ask_question(question).strip
|
121
|
+
|
122
|
+
unless @version = valid_responses[response]
|
123
|
+
Chef::Log.error("'#{response}' is not a valid value.")
|
124
|
+
exit(1)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
86
128
|
end
|
87
129
|
end
|
88
130
|
end
|
@@ -24,6 +24,7 @@ require 'chef/streaming_cookbook_uploader'
|
|
24
24
|
require 'chef/cache/checksum'
|
25
25
|
require 'chef/sandbox'
|
26
26
|
require 'chef/cookbook_version'
|
27
|
+
require 'chef/cookbook/syntax_check'
|
27
28
|
require 'chef/cookbook/file_system_file_vendor'
|
28
29
|
|
29
30
|
class Chef
|
@@ -50,6 +51,8 @@ class Chef
|
|
50
51
|
else
|
51
52
|
config[:cookbook_path] = Chef::Config[:cookbook_path]
|
52
53
|
end
|
54
|
+
# Ugh, manipulating globals causes bugs.
|
55
|
+
@user_cookbook_path = config[:cookbook_path]
|
53
56
|
|
54
57
|
Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest) }
|
55
58
|
|
@@ -70,23 +73,12 @@ class Chef
|
|
70
73
|
end
|
71
74
|
end
|
72
75
|
|
73
|
-
def test_ruby(cookbook_dir)
|
74
|
-
Chef::Log.info("Validating ruby files")
|
75
|
-
Dir[File.join(cookbook_dir, '**', '*.rb')].each do |ruby_file|
|
76
|
-
test_ruby_file(cookbook_dir, ruby_file)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def test_templates(cookbook_dir)
|
81
|
-
Chef::Log.info("Validating templates")
|
82
|
-
Dir[File.join(cookbook_dir, '**', '*.erb')].each do |erb_file|
|
83
|
-
test_template_file(cookbook_dir, erb_file)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
76
|
def upload_cookbook(cookbook)
|
88
77
|
Chef::Log.info("Saving #{cookbook.name}")
|
89
78
|
|
79
|
+
# Validate the cookbook before staging it or else the syntax checker's
|
80
|
+
# cache will not be helpful.
|
81
|
+
validate_cookbook(cookbook)
|
90
82
|
# create build directory
|
91
83
|
tmp_cookbook_dir = create_build_dir(cookbook)
|
92
84
|
|
@@ -173,10 +165,6 @@ class Chef
|
|
173
165
|
end
|
174
166
|
end
|
175
167
|
|
176
|
-
# Validate ruby files and templates
|
177
|
-
test_ruby(tmp_cookbook_dir)
|
178
|
-
test_templates(tmp_cookbook_dir)
|
179
|
-
|
180
168
|
# First, generate metadata
|
181
169
|
Chef::Log.debug("Generating metadata")
|
182
170
|
kcm = Chef::Knife::CookbookMetadata.new
|
@@ -201,30 +189,16 @@ class Chef
|
|
201
189
|
end
|
202
190
|
end
|
203
191
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
Chef::Log.fatal("Erb template #{file_relative_path} has a syntax error:")
|
213
|
-
result.stderr.each_line { |l| Chef::Log.fatal(l.chomp) }
|
214
|
-
exit(1)
|
192
|
+
def validate_cookbook(cookbook)
|
193
|
+
syntax_checker = Chef::Cookbook::SyntaxCheck.for_cookbook(cookbook.name, @user_cookbook_path)
|
194
|
+
Chef::Log.info("Validating ruby files")
|
195
|
+
exit(1) unless syntax_checker.validate_ruby_files
|
196
|
+
Chef::Log.info("Validating templates")
|
197
|
+
exit(1) unless syntax_checker.validate_templates
|
198
|
+
Chef::Log.info("Syntax OK")
|
199
|
+
true
|
215
200
|
end
|
216
201
|
|
217
|
-
def test_ruby_file(cookbook_dir, ruby_file)
|
218
|
-
Chef::Log.debug("Testing #{ruby_file} for syntax errors...")
|
219
|
-
result = shell_out("ruby -c #{ruby_file}")
|
220
|
-
result.error!
|
221
|
-
rescue Chef::Exceptions::ShellCommandFailed
|
222
|
-
file_relative_path = ruby_file[/^#{Regexp.escape(cookbook_dir+File::Separator)}(.*)/, 1]
|
223
|
-
Chef::Log.fatal("Cookbook file #{file_relative_path} has a syntax error:")
|
224
|
-
result.stderr.each_line { |l| Chef::Log.fatal(l.chomp) }
|
225
|
-
exit(1)
|
226
|
-
end
|
227
|
-
|
228
202
|
end
|
229
203
|
end
|
230
204
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Daniel DeLeo (<dan@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2010 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/knife'
|
20
|
+
class Chef::Knife::RecipeList < Chef::Knife
|
21
|
+
|
22
|
+
banner "Sub-Command: recipe list [PATTERN]"
|
23
|
+
|
24
|
+
def run
|
25
|
+
recipes = rest.get_rest('cookbooks/_recipes')
|
26
|
+
if pattern = @name_args.first
|
27
|
+
recipes = recipes.grep(Regexp.new(pattern))
|
28
|
+
end
|
29
|
+
output(recipes)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/lib/chef/knife/ssh.rb
CHANGED
@@ -76,6 +76,7 @@ class Chef
|
|
76
76
|
end
|
77
77
|
r
|
78
78
|
end
|
79
|
+
(Chef::Log.fatal("No nodes returned from search!"); exit 10) if list.length == 0
|
79
80
|
session_from_list(list)
|
80
81
|
end
|
81
82
|
|
@@ -201,6 +202,34 @@ class Chef
|
|
201
202
|
exec("screen -c #{tf.path}")
|
202
203
|
end
|
203
204
|
|
205
|
+
def tmux
|
206
|
+
begin
|
207
|
+
Chef::Mixin::Command.run_command(:command => "tmux new-session -d -s 'knife'")
|
208
|
+
rescue Chef::Exceptions::Exec
|
209
|
+
end
|
210
|
+
session.servers_for.each do |server|
|
211
|
+
begin
|
212
|
+
Chef::Mixin::Command.run_command(:command => "tmux new-window -d -n '#{server.host}' -t 'knife' 'ssh #{server.user ? "#{server.user}@#{server.host}" : server.host}'")
|
213
|
+
rescue Chef::Exceptions::Exec
|
214
|
+
end
|
215
|
+
end
|
216
|
+
exec("tmux attach-session -t knife")
|
217
|
+
end
|
218
|
+
|
219
|
+
def macterm
|
220
|
+
require 'appscript'
|
221
|
+
Appscript.app("/Applications/Utilities/Terminal.app").windows.first.activate
|
222
|
+
Appscript.app("System Events").application_processes["Terminal.app"].keystroke("n", :using=>:command_down)
|
223
|
+
term = Appscript.app('Terminal')
|
224
|
+
window = term.windows.first.get
|
225
|
+
session.servers_for.each do |server|
|
226
|
+
Appscript.app("System Events").application_processes["Terminal.app"].keystroke("t", :using=>:command_down)
|
227
|
+
cmd = "unset PROMPT_COMMAND; echo -e \"\\033]0;#{server.host}\\007\"; ssh #{server.user ? "#{server.user}@#{server.host}" : server.host}"
|
228
|
+
Appscript.app('Terminal').do_script(cmd, :in => window.tabs.last.get)
|
229
|
+
sleep 1
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
204
233
|
def run
|
205
234
|
@longest = 0
|
206
235
|
|
@@ -215,6 +244,10 @@ class Chef
|
|
215
244
|
interactive
|
216
245
|
when "screen"
|
217
246
|
screen
|
247
|
+
when "tmux"
|
248
|
+
tmux
|
249
|
+
when "macterm"
|
250
|
+
macterm
|
218
251
|
else
|
219
252
|
ssh_command(@name_args[1..-1].join(" "))
|
220
253
|
end
|
data/lib/chef/version.rb
CHANGED
metadata
CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
|
|
6
6
|
- 0
|
7
7
|
- 9
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.9.0.
|
9
|
+
- b02
|
10
|
+
version: 0.9.0.b02
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Adam Jacob
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-06-
|
18
|
+
date: 2010-06-13 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -283,6 +283,7 @@ files:
|
|
283
283
|
- lib/chef/cookbook/metadata/version.rb
|
284
284
|
- lib/chef/cookbook/metadata.rb
|
285
285
|
- lib/chef/cookbook/remote_file_vendor.rb
|
286
|
+
- lib/chef/cookbook/syntax_check.rb
|
286
287
|
- lib/chef/cookbook_loader.rb
|
287
288
|
- lib/chef/cookbook_version.rb
|
288
289
|
- lib/chef/couchdb.rb
|
@@ -344,6 +345,7 @@ files:
|
|
344
345
|
- lib/chef/knife/rackspace_server_create.rb
|
345
346
|
- lib/chef/knife/rackspace_server_delete.rb
|
346
347
|
- lib/chef/knife/rackspace_server_list.rb
|
348
|
+
- lib/chef/knife/recipe_list.rb
|
347
349
|
- lib/chef/knife/role_bulk_delete.rb
|
348
350
|
- lib/chef/knife/role_create.rb
|
349
351
|
- lib/chef/knife/role_delete.rb
|