chef 0.9.8.beta.1 → 0.9.8.beta.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -200,15 +200,24 @@ class Chef
200
200
 
201
201
  def ask_question(question, opts={})
202
202
  question = question + "[#{opts[:default]}] " if opts[:default]
203
-
204
- stdout.print question
205
- a = stdin.readline.strip
206
203
 
207
- if opts[:default]
208
- a.empty? ? opts[:default] : a
204
+ if opts[:default] and config[:defaults]
205
+
206
+ opts[:default]
207
+
209
208
  else
210
- a
209
+
210
+ stdout.print question
211
+ a = stdin.readline.strip
212
+
213
+ if opts[:default]
214
+ a.empty? ? opts[:default] : a
215
+ else
216
+ a
217
+ end
218
+
211
219
  end
220
+
212
221
  end
213
222
 
214
223
  def configure_chef
@@ -58,13 +58,7 @@ class Chef
58
58
  :description => "Bootstrap a distro using a template",
59
59
  :default => "ubuntu10.04-gems"
60
60
 
61
- option :identity_file,
62
- :short => "-i IDENTITY_FILE",
63
- :long => "--identity-file IDENTITY_FILE",
64
- :description => "The SSH identity file used for authentication"
65
-
66
61
  option :use_sudo,
67
- :short => "-s",
68
62
  :long => "--sudo",
69
63
  :description => "Execute the bootstrap via sudo",
70
64
  :boolean => true
@@ -79,7 +73,7 @@ class Chef
79
73
  :long => "--run-list RUN_LIST",
80
74
  :description => "Comma separated list of roles/recipes to apply",
81
75
  :proc => lambda { |o| o.split(",") },
82
- :default => nil
76
+ :default => []
83
77
 
84
78
  def h
85
79
  @highline ||= HighLine.new
@@ -0,0 +1,32 @@
1
+ bash -c '
2
+ if [ ! -f /usr/bin/chef-client ]; then
3
+ echo "chef chef/chef_server_url string <%= Chef::Config[:chef_server_url] %>" | debconf-set-selections
4
+ [ -f /etc/apt/sources.list.d/opscode.list ] || echo "deb http://apt.opscode.com lucid main" > /etc/apt/sources.list.d/opscode.list
5
+ wget -O- http://apt.opscode.com/packages@opscode.com.gpg.key | apt-key add -
6
+ apt-get update
7
+ apt-get install -y chef
8
+ fi
9
+
10
+ <% unless Chef::Config[:validation_client_name] == "chef-validator" -%>
11
+ [ `grep -qx "validation_client_name \"<%= Chef::Config[:validation_client_name] %>\"" /etc/chef/client.rb` ] || echo "validation_client_name \"<%= Chef::Config[:validation_client_name] %>\"" >> /etc/chef/client.rb
12
+ <% end -%>
13
+
14
+ (
15
+ cat <<'EOP'
16
+ <%= IO.read(Chef::Config[:validation_key]) %>
17
+ EOP
18
+ ) > /tmp/validation.pem
19
+ awk NF /tmp/validation.pem > /etc/chef/validation.pem
20
+ rm /tmp/validation.pem
21
+
22
+ <% if @config[:chef_node_name] %>
23
+ [ `grep -qx "node_name \"<%= @config[:chef_node_name] %>\"" /etc/chef/client.rb` ] || echo "node_name \"<%= @config[:chef_node_name] %>\"" >> /etc/chef/client.rb
24
+ <% end -%>
25
+
26
+ (
27
+ cat <<'EOP'
28
+ <%= { "run_list" => @run_list }.to_json %>
29
+ EOP
30
+ ) > /etc/chef/first-boot.json
31
+
32
+ /usr/bin/chef-client -j /etc/chef/first-boot.json'
@@ -26,35 +26,57 @@ class Chef
26
26
  class CookbookCreate < Knife
27
27
  include Chef::Mixin::ShellOut
28
28
 
29
- banner "knife cookbook create COOKBOOK [COMPANY_NAME_FOR_COPYRIGHT] [EMAIL] [APACHE_LICENSE=false] (options)"
29
+ banner "knife cookbook create COOKBOOK (options)"
30
30
 
31
31
  option :cookbook_path,
32
32
  :short => "-o PATH",
33
33
  :long => "--cookbook-path PATH",
34
34
  :description => "The directory where the cookbook will be created"
35
35
 
36
+ option :readme_format,
37
+ :short => "-r FORMAT",
38
+ :long => "--readme-format FORMAT",
39
+ :description => "Format of the README file, supported formats are 'md' (markdown) and 'rdoc' (rdoc)",
40
+ :default => "rdoc"
41
+
42
+ option :cookbook_license,
43
+ :short => "-I LICENSE",
44
+ :long => "--license LICENSE",
45
+ :description => "License for cookbook, apachev2 or none"
46
+
47
+ option :cookbook_copyright,
48
+ :short => "-C COPYRIGHT",
49
+ :long => "--copyright COPYRIGHT",
50
+ :description => "Name of Copyright holder"
51
+
52
+ option :cookbook_email,
53
+ :short => "-E EMAIL",
54
+ :long => "--email EMAIL",
55
+ :description => "Email address of cookbook maintainer"
56
+
36
57
  def run
37
- if @name_args.length < 1
38
- show_usage
39
- Chef::Log.fatal("You must specify a cookbook name")
40
- exit 1
58
+ self.config = Chef::Config.merge!(config)
59
+ if @name_args.length < 1
60
+ show_usage
61
+ Chef::Log.fatal("You must specify a cookbook name")
62
+ exit 1
41
63
  end
42
64
 
43
- if default_cookbook_path_empty? && given_cookbook_path_empty?
65
+ if default_cookbook_path_empty? && parameter_empty?(config[:cookbook_path])
44
66
  raise ArgumentError, "Default cookbook_path is not specified in the knife.rb config file, and a value to -o is not provided. Nowhere to write the new cookbook to."
45
67
  end
46
68
 
47
- cookbook_path = given_cookbook_path_empty? ? Chef::Config[:cookbook_path].first : config[:cookbook_path]
69
+ cookbook_path = Array(config[:cookbook_path]).first
48
70
  cookbook_name = @name_args.first
49
- company_name = @name_args[1].nil? || @name_args[1].empty? ? "YOUR_COMPANY_NAME" : @name_args[1]
50
- email = @name_args[2].nil? || @name_args[2].empty? ? "YOUR_EMAIL" : @name_args[2]
51
- license = (@name_args[3].nil? || @name_args[3].to_s.empty? || @name_args[3] == false || @name_args[3] == 'false') ? :none : :apachev2
52
- create_cookbook(cookbook_path,cookbook_name, company_name, license)
71
+ copyright = config[:cookbook_copyright] || "YOUR_COMPANY_NAME"
72
+ email = config[:cookbook_email] || "YOUR_EMAIL"
73
+ license = ((config[:cookbook_license] != "false") && config[:cookbook_license]) || "none"
74
+ create_cookbook(cookbook_path,cookbook_name, copyright, license)
53
75
  create_readme(cookbook_path,cookbook_name)
54
- create_metadata(cookbook_path,cookbook_name, company_name, email, license)
76
+ create_metadata(cookbook_path,cookbook_name, copyright, email, license)
55
77
  end
56
78
 
57
- def create_cookbook(dir, cookbook_name, company_name, license)
79
+ def create_cookbook(dir, cookbook_name, copyright, license)
58
80
  msg("** Creating cookbook #{cookbook_name}")
59
81
  shell_out "mkdir -p #{File.join(dir, cookbook_name, "attributes")}"
60
82
  shell_out "mkdir -p #{File.join(dir, cookbook_name, "recipes")}"
@@ -71,11 +93,11 @@ class Chef
71
93
  # Cookbook Name:: #{cookbook_name}
72
94
  # Recipe:: default
73
95
  #
74
- # Copyright #{Time.now.year}, #{company_name}
96
+ # Copyright #{Time.now.year}, #{copyright}
75
97
  #
76
98
  EOH
77
99
  case license
78
- when :apachev2
100
+ when "apachev2"
79
101
  file.puts <<-EOH
80
102
  # Licensed under the Apache License, Version 2.0 (the "License");
81
103
  # you may not use this file except in compliance with the License.
@@ -90,7 +112,7 @@ EOH
90
112
  # limitations under the License.
91
113
  #
92
114
  EOH
93
- when :none
115
+ when "none"
94
116
  file.puts <<-EOH
95
117
  # All rights reserved - Do Not Redistribute
96
118
  #
@@ -102,9 +124,11 @@ EOH
102
124
 
103
125
  def create_readme(dir, cookbook_name)
104
126
  msg("** Creating README for cookbook: #{cookbook_name}")
105
- unless File.exists?(File.join(dir, cookbook_name, "README.rdoc"))
106
- open(File.join(dir, cookbook_name, "README.rdoc"), "w") do |file|
107
- file.puts <<-EOH
127
+ unless File.exists?(File.join(dir, cookbook_name, "README.#{config[:readme_format]}"))
128
+ open(File.join(dir, cookbook_name, "README.#{config[:readme_format]}"), "w") do |file|
129
+ case config[:readme_format]
130
+ when "rdoc"
131
+ file.puts <<-EOH
108
132
  = DESCRIPTION:
109
133
 
110
134
  = REQUIREMENTS:
@@ -114,27 +138,54 @@ EOH
114
138
  = USAGE:
115
139
 
116
140
  EOH
141
+ when "md","mkd","txt"
142
+ file.puts <<-EOH
143
+ Description
144
+ ===========
145
+
146
+ Requirements
147
+ ============
148
+
149
+ Attributes
150
+ ==========
151
+
152
+ Usage
153
+ =====
154
+
155
+ EOH
156
+ else
157
+ file.puts <<-EOH
158
+ Description
159
+
160
+ Requirements
161
+
162
+ Attributes
163
+
164
+ Usage
165
+
166
+ EOH
167
+ end
117
168
  end
118
169
  end
119
170
  end
120
171
 
121
- def create_metadata(dir, cookbook_name, company_name, email, license)
172
+ def create_metadata(dir, cookbook_name, copyright, email, license)
122
173
  msg("** Creating metadata for cookbook: #{cookbook_name}")
123
174
 
124
175
  license_name = case license
125
- when :apachev2
176
+ when "apachev2"
126
177
  "Apache 2.0"
127
- when :none
178
+ when "none"
128
179
  "All rights reserved"
129
180
  end
130
181
 
131
182
  unless File.exists?(File.join(dir, cookbook_name, "metadata.rb"))
132
183
  open(File.join(dir, cookbook_name, "metadata.rb"), "w") do |file|
133
- if File.exists?(File.join(dir, cookbook_name, 'README.rdoc'))
134
- long_description = "long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc'))"
184
+ if File.exists?(File.join(dir, cookbook_name, "README.#{config[:readme_format]}"))
185
+ long_description = "long_description IO.read(File.join(File.dirname(__FILE__), 'README.#{config[:readme_format]}'))"
135
186
  end
136
187
  file.puts <<-EOH
137
- maintainer "#{company_name}"
188
+ maintainer "#{copyright}"
138
189
  maintainer_email "#{email}"
139
190
  license "#{license_name}"
140
191
  description "Installs/Configures #{cookbook_name}"
@@ -146,14 +197,13 @@ EOH
146
197
  end
147
198
 
148
199
  private
149
- def default_cookbook_path_empty?
150
- Chef::Config[:cookbook_path].nil? || Chef::Config[:cookbook_path].empty?
151
- end
152
-
153
- def given_cookbook_path_empty?
154
- config[:cookbook_path].nil? || config[:cookbook_path].empty?
155
- end
200
+ def default_cookbook_path_empty?
201
+ Chef::Config[:cookbook_path].nil? || Chef::Config[:cookbook_path].empty?
202
+ end
156
203
 
204
+ def parameter_empty?(parameter)
205
+ parameter.nil? || parameter.empty?
206
+ end
157
207
  end
158
208
  end
159
- end
209
+ end
@@ -61,8 +61,13 @@ class Chef
61
61
  :description => "The ssh username",
62
62
  :default => "root"
63
63
 
64
+ option :ssh_password,
65
+ :short => "-P PASSWORD",
66
+ :long => "--ssh-password PASSWORD",
67
+ :description => "The ssh password"
68
+
64
69
  option :identity_file,
65
- :short => "-i IDENTITY_FILE",
70
+ :short => "-I IDENTITY_FILE",
66
71
  :long => "--identity-file IDENTITY_FILE",
67
72
  :description => "The SSH identity file used for authentication"
68
73
 
@@ -87,6 +92,17 @@ class Chef
87
92
  :description => "Your AWS region",
88
93
  :default => "us-east-1"
89
94
 
95
+ option :distro,
96
+ :short => "-d DISTRO",
97
+ :long => "--distro DISTRO",
98
+ :description => "Bootstrap a distro using a template",
99
+ :default => "ubuntu10.04-gems"
100
+
101
+ option :template_file,
102
+ :long => "--template-file TEMPLATE",
103
+ :description => "Full path to location of template to use",
104
+ :default => false
105
+
90
106
  def h
91
107
  @highline ||= HighLine.new
92
108
  end
@@ -124,8 +140,10 @@ class Chef
124
140
 
125
141
  # wait for it to be ready to do stuff
126
142
  server.wait_for { print "."; ready? }
143
+ puts "#{h.color("\nWaiting 10 seconds for SSH Host Key generation on", :magenta)}: #{server.dns_name}"
144
+ sleep 10
127
145
 
128
- print "\n\n"
146
+ print "\n"
129
147
 
130
148
  puts "#{h.color("Public DNS Name", :cyan)}: #{server.dns_name}"
131
149
  puts "#{h.color("Public IP Address", :cyan)}: #{server.ip_address}"
@@ -134,11 +152,15 @@ class Chef
134
152
 
135
153
  begin
136
154
  bootstrap = Chef::Knife::Bootstrap.new
137
- bootstrap.name_args = [ server.ip_address, @name_args ].flatten
155
+ bootstrap.name_args = server.dns_name
156
+ bootstrap.config[:run_list] = @name_args
138
157
  bootstrap.config[:ssh_user] = config[:ssh_user]
139
158
  bootstrap.config[:identity_file] = config[:identity_file]
140
159
  bootstrap.config[:chef_node_name] = server.id
141
160
  bootstrap.config[:prerelease] = config[:prerelease]
161
+ bootstrap.config[:distro] = config[:distro]
162
+ bootstrap.config[:use_sudo] = true
163
+ bootstrap.config[:template_file] = config[:template_file]
142
164
  bootstrap.run
143
165
  rescue Errno::ECONNREFUSED
144
166
  puts h.color("Connection refused on SSH, retrying - CTRL-C to abort")
@@ -74,10 +74,18 @@ class Chef
74
74
  end
75
75
 
76
76
  def format_values(hash)
77
- formatted_array = hash.map { |key, value| [key.to_s, value]}.flatten
77
+ formatted_array = flatten_one_level(hash.map { |key, value| [key.to_s, value]})
78
78
  Hash[*formatted_array]
79
79
  end
80
80
 
81
+ def flatten_one_level(array)
82
+ array.inject([]) do |flatter_array, values|
83
+ Array(values).each {|value| flatter_array << value }
84
+ flatter_array
85
+ end
86
+ end
87
+
88
+
81
89
  def assert_valid_platform_values!(platforms, value)
82
90
  unless value.kind_of?(Hash)
83
91
  msg = "platform dependent values must be specified in the format :platform => {:version => value} "
@@ -179,6 +179,7 @@ class Chef
179
179
  time = Time.now
180
180
  savetime = time.strftime("%Y%m%d%H%M%S")
181
181
  backup_filename = "#{@new_resource.path}.chef-#{savetime}"
182
+ backup_filename = backup_filename.sub(/^([A-Za-z]:)/, "") #strip drive letter on Windows
182
183
  # if :file_backup_path is nil, we fallback to the old behavior of
183
184
  # keeping the backup in the same directory. We also need to to_s it
184
185
  # so we don't get a type error around implicit to_str conversions.
@@ -100,11 +100,11 @@ class Chef
100
100
  def upgrade_package(name, version)
101
101
  if version
102
102
  run_command(
103
- :command => "zypper -n --no-gpg-checks update -l #{name}=#{version}"
103
+ :command => "zypper -n --no-gpg-checks install -l #{name}=#{version}"
104
104
  )
105
105
  else
106
106
  run_command(
107
- :command => "zypper -n --no-gpg-checks update -l #{name}"
107
+ :command => "zypper -n --no-gpg-checks install -l #{name}"
108
108
  )
109
109
  end
110
110
  end
@@ -64,8 +64,8 @@ class Chef
64
64
  if absolute_uri?(source)
65
65
  Chef::Provider::RemoteFile
66
66
  else
67
- # contentious...
68
67
  Chef::Log.warn("remote_file is deprecated for fetching files from cookbooks. Use cookbook_file instead")
68
+ Chef::Log.warn("From #{self.to_s} on #{source_line}")
69
69
  Chef::Provider::CookbookFile
70
70
  end
71
71
  end
@@ -5,9 +5,9 @@
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
6
6
  # you may not use this file except in compliance with the License.
7
7
  # You may obtain a copy of the License at
8
- #
8
+ #
9
9
  # http://www.apache.org/licenses/LICENSE-2.0
10
- #
10
+ #
11
11
  # Unless required by applicable law or agreed to in writing, software
12
12
  # distributed under the License is distributed on an "AS IS" BASIS,
13
13
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -20,6 +20,7 @@ require "pp"
20
20
  require "etc"
21
21
  require "mixlib/cli"
22
22
 
23
+ require 'chef/version'
23
24
  require "chef/client"
24
25
  require "chef/config"
25
26
 
@@ -31,74 +32,121 @@ module Shef
31
32
  LEADERS = Hash.new("")
32
33
  LEADERS[Chef::Recipe] = ":recipe"
33
34
  LEADERS[Chef::Node] = ":attributes"
34
-
35
+
35
36
  class << self
36
- attr_accessor :client_type, :options
37
+ attr_accessor :client_type
38
+ attr_accessor :options
39
+ attr_accessor :env
40
+ attr_writer :editor
41
+ end
42
+
43
+ # Start the irb REPL with shef's customizations
44
+ def self.start
45
+ setup_logger
46
+ # FUGLY HACK: irb gives us no other choice.
47
+ irb_help = [:help, :irb_help, IRB::ExtendCommandBundle::NO_OVERRIDE]
48
+ IRB::ExtendCommandBundle.instance_variable_get(:@ALIASES).delete(irb_help)
49
+
50
+ parse_opts
51
+
52
+ # HACK: this duplicates the functions of IRB.start, but we have to do it
53
+ # to get access to the main object before irb starts.
54
+ ::IRB.setup(nil)
55
+
56
+ irb = IRB::Irb.new
57
+
58
+ init(irb.context.main)
59
+
60
+
61
+ irb_conf[:IRB_RC].call(irb.context) if irb_conf[:IRB_RC]
62
+ irb_conf[:MAIN_CONTEXT] = irb.context
63
+
64
+ trap("SIGINT") do
65
+ irb.signal_handle
66
+ end
67
+
68
+ catch(:IRB_EXIT) do
69
+ irb.eval_input
70
+ end
71
+ end
72
+
73
+ def self.setup_logger
74
+ Chef::Config[:log_level] ||= :warn
75
+ Chef::Log.init(STDERR)
76
+ Mixlib::Authentication::Log.logger = Ohai::Log.logger = Chef::Log.logger
77
+ Chef::Log.level = Chef::Config[:log_level] || :warn
37
78
  end
38
-
79
+
39
80
  # Shef assumes it's running whenever it is defined
40
81
  def self.running?
41
82
  true
42
83
  end
43
-
84
+
44
85
  # Set the irb_conf object to something other than IRB.conf
45
86
  # usful for testing.
46
87
  def self.irb_conf=(conf_hash)
47
88
  @irb_conf = conf_hash
48
89
  end
49
-
90
+
50
91
  def self.irb_conf
51
92
  @irb_conf || IRB.conf
52
93
  end
53
-
94
+
54
95
  def self.configure_irb
55
96
  irb_conf[:HISTORY_FILE] = "~/.shef_history"
56
97
  irb_conf[:SAVE_HISTORY] = 1000
57
-
98
+
58
99
  irb_conf[:IRB_RC] = lambda do |conf|
59
100
  m = conf.main
60
- leader = LEADERS[m.class]
61
-
62
- def m.help
63
- shef_help
64
- end
65
101
 
66
- conf.prompt_c = "chef#{leader} > "
102
+ conf.prompt_c = "chef#{leader(m)} > "
67
103
  conf.return_format = " => %s \n"
68
- conf.prompt_i = "chef#{leader} > "
69
- conf.prompt_n = "chef#{leader} ?> "
70
- conf.prompt_s = "chef#{leader}%l> "
104
+ conf.prompt_i = "chef#{leader(m)} > "
105
+ conf.prompt_n = "chef#{leader(m)} ?> "
106
+ conf.prompt_s = "chef#{leader(m)}%l> "
71
107
  end
72
108
  end
73
-
109
+
110
+ def self.leader(main_object)
111
+ env_string = Shef.env ? " (#{Shef.env})" : ""
112
+ LEADERS[main_object.class] + env_string
113
+ end
114
+
74
115
  def self.session
75
- client_type.instance.reset! unless client_type.instance.node_built?
116
+ unless client_type.instance.node_built?
117
+ puts "Session type: #{client_type.session_type}"
118
+ client_type.instance.reset!
119
+ end
76
120
  client_type.instance
77
121
  end
78
-
79
- def self.init
122
+
123
+ def self.init(main)
80
124
  parse_json
81
125
  configure_irb
82
126
 
83
127
  session # trigger ohai run + session load
84
-
128
+
85
129
  session.node.consume_attributes(@json_attribs)
86
130
 
87
- greeting = begin
88
- " #{Etc.getlogin}@#{Shef.session.node.name}"
89
- rescue NameError
90
- ""
91
- end
131
+ Extensions.extend_context_object(main)
92
132
 
93
- version
133
+ main.version
94
134
  puts
95
135
 
96
136
  puts "run `help' for help, `exit' or ^D to quit."
97
137
  puts
98
138
  puts "Ohai2u#{greeting}!"
99
139
  end
100
-
140
+
141
+ def self.greeting
142
+ " #{Etc.getlogin}@#{Shef.session.node.fqdn}"
143
+ rescue NameError
144
+ ""
145
+ end
146
+
101
147
  def self.parse_json
148
+ # HACK: copied verbatim from chef/application/client, because it's not
149
+ # reusable as written there :(
102
150
  if Chef::Config[:json_attribs]
103
151
  begin
104
152
  json_io = open(Chef::Config[:json_attribs])
@@ -119,28 +167,48 @@ module Shef
119
167
  end
120
168
  end
121
169
  end
122
-
170
+
123
171
  def self.fatal!(message, exit_status)
124
172
  Chef::Log.fatal(message)
125
173
  exit exit_status
126
174
  end
127
-
175
+
128
176
  def self.client_type
129
177
  type = Shef::StandAloneSession
130
178
  type = Shef::SoloSession if Chef::Config[:shef_solo]
131
179
  type = Shef::ClientSession if Chef::Config[:client]
180
+ type = Shef::DoppelGangerSession if Chef::Config[:doppelganger]
132
181
  type
133
182
  end
134
-
183
+
135
184
  def self.parse_opts
136
185
  @options = Options.new
137
186
  @options.parse_opts
138
187
  end
139
-
188
+
189
+ def self.editor
190
+ @editor || Chef::Config[:editor] || ENV['EDITOR']
191
+ end
192
+
140
193
  class Options
141
194
  include Mixlib::CLI
142
195
 
143
- option :config_file,
196
+ def self.footer(text=nil)
197
+ @footer = text if text
198
+ @footer
199
+ end
200
+
201
+ banner("shef #{Chef::VERSION}\n\nUsage: shef [NAMED_CONF] (OPTIONS)")
202
+
203
+ footer(<<-FOOTER)
204
+ When no CONFIG is specified, shef attempts to load a default configuration file:
205
+ * If a NAMED_CONF is given, shef will load ~/.chef/NAMED_CONF/shef.rb
206
+ * If no NAMED_CONF is given shef will load ~/.chef/shef.rb if it exists
207
+ * Shef falls back to loading /etc/chef/client.rb or /etc/chef/solo.rb if -z or
208
+ -s options are given and no shef.rb can be found.
209
+ FOOTER
210
+
211
+ option :config_file,
144
212
  :short => "-c CONFIG",
145
213
  :long => "--config CONFIG",
146
214
  :description => "The configuration file to use"
@@ -151,8 +219,13 @@ module Shef
151
219
  :description => "Show this message",
152
220
  :on => :tail,
153
221
  :boolean => true,
154
- :show_options => true,
155
- :exit => 0
222
+ :proc => proc { print_help }
223
+
224
+ option :log_level,
225
+ :short => "-l LOG_LEVEL",
226
+ :long => '--log-level LOG_LEVEL',
227
+ :description => "Set the logging level",
228
+ :proc => proc { |level| Chef::Log.level = level.to_sym }
156
229
 
157
230
  option :standalone,
158
231
  :short => "-a",
@@ -194,31 +267,57 @@ module Shef
194
267
  :proc => lambda {|v| puts "Chef: #{::Chef::VERSION}"},
195
268
  :exit => 0
196
269
 
270
+ def self.print_help
271
+ instance = new
272
+ instance.parse_options([])
273
+ puts instance.opt_parser
274
+ puts
275
+ puts footer
276
+ puts
277
+ exit 1
278
+ end
279
+
197
280
  def self.setup!
198
281
  self.new.parse_opts
199
282
  end
200
283
 
201
284
  def parse_opts
202
- parse_options
285
+ remainder = parse_options
286
+ environment = remainder.first
203
287
  # We have to nuke ARGV to make sure irb's option parser never sees it.
204
288
  # otherwise, IRB complains about command line switches it doesn't recognize.
205
289
  ARGV.clear
206
- config[:config_file] = config_file_for_shef_mode
290
+ config[:config_file] = config_file_for_shef_mode(environment)
207
291
  config_msg = config[:config_file] || "none (standalone shef session)"
208
292
  puts "loading configuration: #{config_msg}"
209
293
  Chef::Config.from_file(config[:config_file]) if !config[:config_file].nil? && File.exists?(config[:config_file]) && File.readable?(config[:config_file])
210
294
  Chef::Config.merge!(config)
211
295
  end
212
-
296
+
213
297
  private
214
-
215
- def config_file_for_shef_mode
216
- return config[:config_file] if config[:config_file]
217
- return "/etc/chef/solo.rb" if config[:solo]
218
- return "/etc/chef/client.rb" if config[:client]
219
- nil
298
+
299
+ def config_file_for_shef_mode(environment)
300
+ if config[:config_file]
301
+ config[:config_file]
302
+ elsif environment
303
+ Shef.env = environment
304
+ config_file_to_try = ::File.join(ENV['HOME'], '.chef', environment, 'shef.rb')
305
+ unless ::File.exist?(config_file_to_try)
306
+ puts "could not find shef config for environment #{environment} at #{config_file_to_try}"
307
+ exit 1
308
+ end
309
+ config_file_to_try
310
+ elsif ENV['HOME'] && ::File.exist?(File.join(ENV['HOME'], '.chef', 'shef.rb'))
311
+ File.join(ENV['HOME'], '.chef', 'shef.rb')
312
+ elsif config[:solo]
313
+ "/etc/chef/solo.rb"
314
+ elsif config[:client]
315
+ "/etc/chef/client.rb"
316
+ else
317
+ nil
318
+ end
220
319
  end
221
320
 
222
321
  end
223
-
322
+
224
323
  end