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.
- data/bin/shef +5 -18
- data/distro/common/man/man8/knife.8 +632 -243
- data/distro/common/markdown/knife.mkd +251 -44
- data/lib/chef/application/knife.rb +4 -0
- data/lib/chef/data_bag.rb +4 -4
- data/lib/chef/data_bag_item.rb +9 -1
- data/lib/chef/knife.rb +15 -6
- data/lib/chef/knife/bootstrap.rb +1 -7
- data/lib/chef/knife/bootstrap/ubuntu10.04-apt.erb +32 -0
- data/lib/chef/knife/cookbook_create.rb +83 -33
- data/lib/chef/knife/ec2_server_create.rb +25 -3
- data/lib/chef/mixin/language.rb +9 -1
- data/lib/chef/provider/file.rb +1 -0
- data/lib/chef/provider/package/zypper.rb +2 -2
- data/lib/chef/resource/remote_file.rb +1 -1
- data/lib/chef/shef.rb +146 -47
- data/lib/chef/shef/ext.rb +436 -181
- data/lib/chef/shef/model_wrapper.rb +120 -0
- data/lib/chef/shef/shef_rest.rb +28 -0
- data/lib/chef/shef/shef_session.rb +91 -4
- data/lib/chef/version.rb +1 -1
- metadata +6 -3
data/lib/chef/knife.rb
CHANGED
@@ -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
|
-
|
204
|
+
if opts[:default] and config[:defaults]
|
205
|
+
|
206
|
+
opts[:default]
|
207
|
+
|
209
208
|
else
|
210
|
-
|
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
|
data/lib/chef/knife/bootstrap.rb
CHANGED
@@ -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 =>
|
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
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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? &&
|
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 =
|
69
|
+
cookbook_path = Array(config[:cookbook_path]).first
|
48
70
|
cookbook_name = @name_args.first
|
49
|
-
|
50
|
-
email =
|
51
|
-
license = (
|
52
|
-
create_cookbook(cookbook_path,cookbook_name,
|
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,
|
76
|
+
create_metadata(cookbook_path,cookbook_name, copyright, email, license)
|
55
77
|
end
|
56
78
|
|
57
|
-
def create_cookbook(dir, cookbook_name,
|
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}, #{
|
96
|
+
# Copyright #{Time.now.year}, #{copyright}
|
75
97
|
#
|
76
98
|
EOH
|
77
99
|
case license
|
78
|
-
when
|
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
|
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
|
106
|
-
open(File.join(dir, cookbook_name, "README
|
107
|
-
|
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,
|
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
|
176
|
+
when "apachev2"
|
126
177
|
"Apache 2.0"
|
127
|
-
when
|
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,
|
134
|
-
long_description = "long_description IO.read(File.join(File.dirname(__FILE__), 'README
|
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 "#{
|
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
|
-
|
150
|
-
|
151
|
-
|
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 => "-
|
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
|
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 =
|
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")
|
data/lib/chef/mixin/language.rb
CHANGED
@@ -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]}
|
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} "
|
data/lib/chef/provider/file.rb
CHANGED
@@ -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
|
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
|
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
|
data/lib/chef/shef.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
:
|
155
|
-
|
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
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
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
|