chef 0.10.0.beta.5 → 0.10.0.beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/chef/application.rb +13 -1
- data/lib/chef/application/client.rb +4 -2
- data/lib/chef/application/knife.rb +26 -2
- data/lib/chef/application/solo.rb +4 -3
- data/lib/chef/client.rb +2 -1
- data/lib/chef/cookbook_version.rb +2 -1
- data/lib/chef/knife.rb +129 -37
- data/lib/chef/knife/cookbook_metadata.rb +8 -3
- data/lib/chef/knife/cookbook_site_install.rb +3 -127
- data/lib/chef/knife/cookbook_site_vendor.rb +46 -0
- data/lib/chef/knife/cookbook_test.rb +13 -2
- data/lib/chef/knife/core/cookbook_scm_repo.rb +149 -0
- data/lib/chef/knife/core/generic_presenter.rb +184 -0
- data/lib/chef/knife/core/node_editor.rb +127 -0
- data/lib/chef/knife/core/node_presenter.rb +103 -0
- data/lib/chef/knife/core/object_loader.rb +75 -0
- data/lib/chef/knife/{subcommand_loader.rb → core/subcommand_loader.rb} +1 -1
- data/lib/chef/knife/core/text_formatter.rb +100 -0
- data/lib/chef/knife/{ui.rb → core/ui.rb} +53 -73
- data/lib/chef/knife/data_bag_from_file.rb +8 -2
- data/lib/chef/knife/environment_from_file.rb +14 -3
- data/lib/chef/knife/node_edit.rb +14 -105
- data/lib/chef/knife/node_from_file.rb +6 -1
- data/lib/chef/knife/node_show.rb +6 -0
- data/lib/chef/knife/role_from_file.rb +6 -1
- data/lib/chef/knife/search.rb +34 -19
- data/lib/chef/knife/status.rb +15 -1
- data/lib/chef/mixin/recipe_definition_dsl_core.rb +1 -4
- data/lib/chef/mixin/shell_out.rb +1 -0
- data/lib/chef/node.rb +17 -5
- data/lib/chef/resource.rb +42 -19
- data/lib/chef/rest.rb +14 -6
- data/lib/chef/rest/auth_credentials.rb +1 -1
- data/lib/chef/rest/rest_request.rb +26 -1
- data/lib/chef/runner.rb +2 -9
- data/lib/chef/version.rb +1 -1
- metadata +11 -7
- data/lib/chef/knife/bootstrap/client-install.vbs +0 -80
- data/lib/chef/knife/bootstrap/windows-gems.erb +0 -34
- data/lib/chef/knife/windows_bootstrap.rb +0 -157
@@ -0,0 +1,103 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Daniel DeLeo (<dan@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2011 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/core/text_formatter'
|
20
|
+
require 'chef/knife/core/generic_presenter'
|
21
|
+
|
22
|
+
class Chef
|
23
|
+
class Knife
|
24
|
+
module Core
|
25
|
+
|
26
|
+
# This module may be included into a knife subcommand class to automatically
|
27
|
+
# add configuration options used by the NodePresenter
|
28
|
+
module NodeFormattingOptions
|
29
|
+
# :nodoc:
|
30
|
+
# Would prefer to do this in a rational way, but can't be done b/c of
|
31
|
+
# Mixlib::CLI's design :(
|
32
|
+
def self.included(includer)
|
33
|
+
includer.class_eval do
|
34
|
+
option :medium_output,
|
35
|
+
:short => '-m',
|
36
|
+
:long => '--medium',
|
37
|
+
:boolean => true,
|
38
|
+
:default => false,
|
39
|
+
:description => 'Include normal attributes in the output'
|
40
|
+
|
41
|
+
option :long_output,
|
42
|
+
:short => '-l',
|
43
|
+
:long => '--long',
|
44
|
+
:boolean => true,
|
45
|
+
:default => false,
|
46
|
+
:description => 'Include all attributes in the output'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#==Chef::Knife::Core::NodePresenter
|
52
|
+
# A customized presenter for Chef::Node objects. Supports variable-length
|
53
|
+
# output formats for displaying node data
|
54
|
+
class NodePresenter < GenericPresenter
|
55
|
+
|
56
|
+
# Converts a Chef::Node object to a string suitable for output to a
|
57
|
+
# terminal. If config[:medium_output] or config[:long_output] are set
|
58
|
+
# the volume of output is adjusted accordingly. Uses colors if enabled
|
59
|
+
# in the the ui object.
|
60
|
+
def summarize(data)
|
61
|
+
if data.kind_of?(Chef::Node)
|
62
|
+
node = data
|
63
|
+
summarized=<<-SUMMARY
|
64
|
+
#{ui.color('Node Name:', :bold)} #{ui.color(node.name, :bold)}
|
65
|
+
#{key('Environment:')} #{node.chef_environment}
|
66
|
+
#{key('FQDN:')} #{node[:fqdn]}
|
67
|
+
#{key('IP:')} #{node[:ipaddress]}
|
68
|
+
#{key('Run List:')} #{node.run_list}
|
69
|
+
#{key('Roles:')} #{Array(node[:roles]).join(', ')}
|
70
|
+
#{key('Recipes')} #{Array(node[:recipes]).join(', ')}
|
71
|
+
#{key('Platform:')} #{node[:platform]} #{node[:platform_version]}
|
72
|
+
SUMMARY
|
73
|
+
if config[:medium_output] || config[:long_output]
|
74
|
+
summarized +=<<-MORE
|
75
|
+
#{key('Attributes:')}
|
76
|
+
#{text_format(node.normal_attrs)}
|
77
|
+
MORE
|
78
|
+
end
|
79
|
+
if config[:long_output]
|
80
|
+
summarized +=<<-MOST
|
81
|
+
#{key('Default Attributes:')}
|
82
|
+
#{text_format(node.default_attrs)}
|
83
|
+
#{key('Override Attributes:')}
|
84
|
+
#{text_format(node.override_attrs)}
|
85
|
+
#{key('Automatic Attributes (Ohai Data):')}
|
86
|
+
#{text_format(node.automatic_attrs)}
|
87
|
+
MOST
|
88
|
+
end
|
89
|
+
summarized
|
90
|
+
else
|
91
|
+
super
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def key(key_text)
|
96
|
+
ui.color(key_text, :cyan)
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
@@ -0,0 +1,75 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Daniel DeLeo (<dan@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2011 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
|
+
class Chef
|
20
|
+
class Knife
|
21
|
+
module Core
|
22
|
+
class ObjectLoader
|
23
|
+
|
24
|
+
attr_reader :ui
|
25
|
+
attr_reader :klass
|
26
|
+
|
27
|
+
def initialize(klass, ui)
|
28
|
+
@klass = klass
|
29
|
+
@ui = ui
|
30
|
+
end
|
31
|
+
|
32
|
+
def load_from(repo_location, *components)
|
33
|
+
unless object_file = find_file(repo_location, *components)
|
34
|
+
ui.error "Could not find or open file for #{components.join(' ')}"
|
35
|
+
exit 1
|
36
|
+
end
|
37
|
+
object_from_file(object_file)
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_file(repo_location, *components)
|
41
|
+
if file_exists_and_is_readable?(File.expand_path( components.last ))
|
42
|
+
File.expand_path( components.last )
|
43
|
+
else
|
44
|
+
relative_path = File.join(Dir.pwd, repo_location, *components)
|
45
|
+
if file_exists_and_is_readable?(relative_path)
|
46
|
+
relative_path
|
47
|
+
else
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def object_from_file(filename)
|
54
|
+
case filename
|
55
|
+
when /\.(js|json)$/
|
56
|
+
Chef::JSONCompat.from_json(IO.read(filename))
|
57
|
+
when /\.rb$/
|
58
|
+
r = klass.new
|
59
|
+
r.from_file(filename)
|
60
|
+
r
|
61
|
+
else
|
62
|
+
ui.fatal("File must end in .js, .json, or .rb")
|
63
|
+
exit 30
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def file_exists_and_is_readable?(file)
|
68
|
+
File.exists?(file) && File.readable?(file)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
@@ -72,7 +72,7 @@ class Chef
|
|
72
72
|
|
73
73
|
def find_subcommands_via_dirglob
|
74
74
|
# The "require paths" of the core knife subcommands bundled with chef
|
75
|
-
files = Dir[File.expand_path('
|
75
|
+
files = Dir[File.expand_path('../../../knife/*.rb', __FILE__)]
|
76
76
|
files.map! { |knife_file| knife_file[/#{CHEF_ROOT}#{Regexp.escape(File::SEPARATOR)}(.*)\.rb/,1] }
|
77
77
|
files
|
78
78
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Daniel DeLeo (<dan@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2011 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
|
+
class Chef
|
20
|
+
class Knife
|
21
|
+
module Core
|
22
|
+
class TextFormatter
|
23
|
+
|
24
|
+
attr_reader :data
|
25
|
+
attr_reader :ui
|
26
|
+
|
27
|
+
def initialize(data, ui)
|
28
|
+
@ui = ui
|
29
|
+
@data = if data.respond_to?(:display_hash)
|
30
|
+
data.display_hash
|
31
|
+
elsif data.kind_of?(Array)
|
32
|
+
data
|
33
|
+
elsif data.respond_to?(:to_hash)
|
34
|
+
data.to_hash
|
35
|
+
else
|
36
|
+
data
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def formatted_data
|
41
|
+
@formatted_data ||= text_format(data)
|
42
|
+
end
|
43
|
+
|
44
|
+
def text_format(data, indent=0)
|
45
|
+
buffer = ''
|
46
|
+
|
47
|
+
if data.respond_to?(:keys)
|
48
|
+
justify_width = data.keys.map {|k| k.to_s.size }.max.to_i + 2
|
49
|
+
data.sort.each do |key, value|
|
50
|
+
justified_key = ui.color("#{key}:".ljust(justify_width), :cyan)
|
51
|
+
if should_enumerate?(value)
|
52
|
+
buffer << indent_line(justified_key, indent)
|
53
|
+
buffer << text_format(value, indent + 1)
|
54
|
+
else
|
55
|
+
buffer << indent_key_value(justified_key, value, justify_width, indent)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
elsif data.kind_of?(Array)
|
59
|
+
data.each do |item|
|
60
|
+
if should_enumerate?(data)
|
61
|
+
buffer << text_format(item, indent + 1)
|
62
|
+
else
|
63
|
+
buffer << indent_line(item, indent)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
else
|
67
|
+
buffer << indent_line(stringify_value(data), indent)
|
68
|
+
end
|
69
|
+
buffer
|
70
|
+
end
|
71
|
+
|
72
|
+
# Ruby 1.8 Strings include enumberable, which is not what we want. So
|
73
|
+
# we have this heuristic to detect hashes and arrays instead.
|
74
|
+
def should_enumerate?(value)
|
75
|
+
((value.respond_to?(:keys) && !value.empty? ) || ( value.kind_of?(Array) && value.size > 1 ))
|
76
|
+
end
|
77
|
+
|
78
|
+
def indent_line(string, indent)
|
79
|
+
(" " * indent) << "#{string}\n"
|
80
|
+
end
|
81
|
+
|
82
|
+
def indent_key_value(key, value, justify_width, indent)
|
83
|
+
lines = value.to_s.split("\n")
|
84
|
+
if lines.size > 1
|
85
|
+
total_indent = (2 * indent) + justify_width + 1
|
86
|
+
indent_line("#{key} #{lines.shift}", indent) << lines.map {|l| (" " * total_indent) + l << "\n" }.join("")
|
87
|
+
else
|
88
|
+
indent_line("#{key} #{stringify_value(value)}", indent)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def stringify_value(data)
|
93
|
+
data.kind_of?(String) ? data : data.inspect
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
@@ -18,18 +18,37 @@
|
|
18
18
|
# limitations under the License.
|
19
19
|
#
|
20
20
|
|
21
|
+
require 'forwardable'
|
22
|
+
require 'chef/knife/core/generic_presenter'
|
21
23
|
|
22
24
|
class Chef
|
23
25
|
class Knife
|
26
|
+
|
27
|
+
#==Chef::Knife::UI
|
28
|
+
# The User Interaction class used by knife.
|
24
29
|
class UI
|
25
30
|
|
31
|
+
extend Forwardable
|
32
|
+
|
26
33
|
attr_reader :stdout
|
27
34
|
attr_reader :stderr
|
28
35
|
attr_reader :stdin
|
29
36
|
attr_reader :config
|
30
37
|
|
38
|
+
def_delegator :@presenter, :format_list_for_display
|
39
|
+
def_delegator :@presenter, :format_for_display
|
40
|
+
def_delegator :@presenter, :format_cookbook_list_for_display
|
41
|
+
|
31
42
|
def initialize(stdout, stderr, stdin, config)
|
32
43
|
@stdout, @stderr, @stdin, @config = stdout, stderr, stdin, config
|
44
|
+
@presenter = Chef::Knife::Core::GenericPresenter.new(self, config)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Creates a new +presenter_class+ object and uses it to format structured
|
48
|
+
# data for display. By default, a Chef::Knife::Core::GenericPresenter
|
49
|
+
# object is used.
|
50
|
+
def use_presenter(presenter_class)
|
51
|
+
@presenter = presenter_class.new(self, config)
|
33
52
|
end
|
34
53
|
|
35
54
|
def highline
|
@@ -39,26 +58,42 @@ class Chef
|
|
39
58
|
end
|
40
59
|
end
|
41
60
|
|
61
|
+
# Prints a message to stdout. Aliased as +info+ for compatibility with
|
62
|
+
# the logger API.
|
42
63
|
def msg(message)
|
43
64
|
stdout.puts message
|
44
65
|
end
|
45
66
|
|
46
67
|
alias :info :msg
|
47
68
|
|
69
|
+
# Print a warning message
|
48
70
|
def warn(message)
|
49
|
-
msg("WARNING: #{message}")
|
71
|
+
msg("#{color('WARNING:', :yellow, :bold)} #{message}")
|
50
72
|
end
|
51
73
|
|
74
|
+
# Print an error message
|
52
75
|
def error(message)
|
53
|
-
msg("ERROR: #{message}")
|
76
|
+
msg("#{color('ERROR:', :red, :bold)} #{message}")
|
54
77
|
end
|
55
78
|
|
79
|
+
# Print a message describing a fatal error.
|
56
80
|
def fatal(message)
|
57
|
-
msg("FATAL: #{message}")
|
81
|
+
msg("#{color('FATAL:', :red, :bold)} #{message}")
|
82
|
+
end
|
83
|
+
|
84
|
+
def color(string, *colors)
|
85
|
+
if color?
|
86
|
+
highline.color(string, *colors)
|
87
|
+
else
|
88
|
+
string
|
89
|
+
end
|
58
90
|
end
|
59
91
|
|
60
|
-
|
61
|
-
|
92
|
+
# Should colored output be used? For output to a terminal, this is
|
93
|
+
# determined by the value of `config[:color]`. When output is not to a
|
94
|
+
# terminal, colored output is never used
|
95
|
+
def color?
|
96
|
+
config[:color] && stdout.tty?
|
62
97
|
end
|
63
98
|
|
64
99
|
def ask(*args, &block)
|
@@ -69,6 +104,19 @@ class Chef
|
|
69
104
|
highline.list(*args)
|
70
105
|
end
|
71
106
|
|
107
|
+
# Formats +data+ using the configured presenter and outputs the result
|
108
|
+
# via +msg+. Formatting can be customized by configuring a different
|
109
|
+
# presenter. See +use_presenter+
|
110
|
+
def output(data)
|
111
|
+
msg @presenter.format(data)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Determines if the output format is a data interchange format, i.e.,
|
115
|
+
# JSON or YAML
|
116
|
+
def interchange?
|
117
|
+
@presenter.interchange?
|
118
|
+
end
|
119
|
+
|
72
120
|
def ask_question(question, opts={})
|
73
121
|
question = question + "[#{opts[:default]}] " if opts[:default]
|
74
122
|
|
@@ -90,74 +138,6 @@ class Chef
|
|
90
138
|
stdout.puts data
|
91
139
|
end
|
92
140
|
|
93
|
-
def output(data)
|
94
|
-
case config[:format]
|
95
|
-
when "json", nil
|
96
|
-
stdout.puts Chef::JSONCompat.to_json_pretty(data)
|
97
|
-
when "yaml"
|
98
|
-
require 'yaml'
|
99
|
-
stdout.puts YAML::dump(data)
|
100
|
-
when "text"
|
101
|
-
# If you were looking for some attribute and there is only one match
|
102
|
-
# just dump the attribute value
|
103
|
-
if data.length == 1 and config[:attribute]
|
104
|
-
stdout.puts data.values[0]
|
105
|
-
else
|
106
|
-
PP.pp(data, stdout)
|
107
|
-
end
|
108
|
-
else
|
109
|
-
raise ArgumentError, "Unknown output format #{config[:format]}"
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def format_list_for_display(list)
|
114
|
-
config[:with_uri] ? list : list.keys.sort { |a,b| a <=> b }
|
115
|
-
end
|
116
|
-
|
117
|
-
def format_for_display(data)
|
118
|
-
if config[:attribute]
|
119
|
-
config[:attribute].split(".").each do |attr|
|
120
|
-
if data.respond_to?(:[])
|
121
|
-
data = data[attr]
|
122
|
-
elsif data.nil?
|
123
|
-
nil # don't get no method error on nil
|
124
|
-
else data.respond_to?(attr.to_sym)
|
125
|
-
data = data.send(attr.to_sym)
|
126
|
-
end
|
127
|
-
end
|
128
|
-
{ config[:attribute] => data.respond_to?(:to_hash) ? data.to_hash : data }
|
129
|
-
elsif config[:run_list]
|
130
|
-
data = data.run_list.run_list
|
131
|
-
{ "run_list" => data }
|
132
|
-
elsif config[:environment]
|
133
|
-
if data.respond_to?(:chef_environment)
|
134
|
-
{"chef_environment" => data.chef_environment}
|
135
|
-
else
|
136
|
-
# this is a place holder for now. Feel free to modify (i.e. add other cases). [nuo]
|
137
|
-
data
|
138
|
-
end
|
139
|
-
elsif config[:id_only]
|
140
|
-
data.respond_to?(:name) ? data.name : data["id"]
|
141
|
-
else
|
142
|
-
data
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
def format_cookbook_list_for_display(item)
|
147
|
-
if config[:with_uri]
|
148
|
-
item
|
149
|
-
else
|
150
|
-
versions_by_cookbook = item.inject({}) do |collected, ( cookbook, versions )|
|
151
|
-
collected[cookbook] = versions["versions"].map {|v| v['version']}
|
152
|
-
collected
|
153
|
-
end
|
154
|
-
key_length = versions_by_cookbook.keys.map {|name| name.size }.max + 2
|
155
|
-
versions_by_cookbook.sort.map do |cookbook, versions|
|
156
|
-
"#{cookbook.ljust(key_length)} #{versions.join(',')}"
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
141
|
def edit_data(data, parse_output=true)
|
162
142
|
output = Chef::JSONCompat.to_json_pretty(data)
|
163
143
|
|