chef 0.9.8.beta.1 → 0.9.8.beta.2
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/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
|