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.
Files changed (40) hide show
  1. data/lib/chef/application.rb +13 -1
  2. data/lib/chef/application/client.rb +4 -2
  3. data/lib/chef/application/knife.rb +26 -2
  4. data/lib/chef/application/solo.rb +4 -3
  5. data/lib/chef/client.rb +2 -1
  6. data/lib/chef/cookbook_version.rb +2 -1
  7. data/lib/chef/knife.rb +129 -37
  8. data/lib/chef/knife/cookbook_metadata.rb +8 -3
  9. data/lib/chef/knife/cookbook_site_install.rb +3 -127
  10. data/lib/chef/knife/cookbook_site_vendor.rb +46 -0
  11. data/lib/chef/knife/cookbook_test.rb +13 -2
  12. data/lib/chef/knife/core/cookbook_scm_repo.rb +149 -0
  13. data/lib/chef/knife/core/generic_presenter.rb +184 -0
  14. data/lib/chef/knife/core/node_editor.rb +127 -0
  15. data/lib/chef/knife/core/node_presenter.rb +103 -0
  16. data/lib/chef/knife/core/object_loader.rb +75 -0
  17. data/lib/chef/knife/{subcommand_loader.rb → core/subcommand_loader.rb} +1 -1
  18. data/lib/chef/knife/core/text_formatter.rb +100 -0
  19. data/lib/chef/knife/{ui.rb → core/ui.rb} +53 -73
  20. data/lib/chef/knife/data_bag_from_file.rb +8 -2
  21. data/lib/chef/knife/environment_from_file.rb +14 -3
  22. data/lib/chef/knife/node_edit.rb +14 -105
  23. data/lib/chef/knife/node_from_file.rb +6 -1
  24. data/lib/chef/knife/node_show.rb +6 -0
  25. data/lib/chef/knife/role_from_file.rb +6 -1
  26. data/lib/chef/knife/search.rb +34 -19
  27. data/lib/chef/knife/status.rb +15 -1
  28. data/lib/chef/mixin/recipe_definition_dsl_core.rb +1 -4
  29. data/lib/chef/mixin/shell_out.rb +1 -0
  30. data/lib/chef/node.rb +17 -5
  31. data/lib/chef/resource.rb +42 -19
  32. data/lib/chef/rest.rb +14 -6
  33. data/lib/chef/rest/auth_credentials.rb +1 -1
  34. data/lib/chef/rest/rest_request.rb +26 -1
  35. data/lib/chef/runner.rb +2 -9
  36. data/lib/chef/version.rb +1 -1
  37. metadata +11 -7
  38. data/lib/chef/knife/bootstrap/client-install.vbs +0 -80
  39. data/lib/chef/knife/bootstrap/windows-gems.erb +0 -34
  40. 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('../../knife/*.rb', __FILE__)]
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
- def color(*args)
61
- highline.color(*args)
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