knife-server 0.3.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +14 -4
- data/CHANGELOG.md +50 -9
- data/Gemfile +3 -0
- data/README.md +112 -40
- data/knife-server.gemspec +2 -1
- data/lib/chef/knife/bootstrap/_common.sh +24 -0
- data/lib/chef/knife/bootstrap/_omnibus.sh +117 -0
- data/lib/chef/knife/bootstrap/_platform_and_version.sh +115 -0
- data/lib/chef/knife/bootstrap/_set_hostname.sh +60 -0
- data/lib/chef/knife/bootstrap/auto.sh +116 -0
- data/lib/chef/knife/bootstrap/{chef-server-debian.erb → chef10/debian.erb} +22 -40
- data/lib/chef/knife/bootstrap/chef10/rhel.erb +185 -0
- data/lib/chef/knife/bootstrap/chef11/omnibus.erb +64 -0
- data/lib/chef/knife/bootstrap/chef11/rhel.erb +142 -0
- data/lib/chef/knife/server_bootstrap_base.rb +89 -41
- data/lib/chef/knife/server_bootstrap_ec2.rb +46 -74
- data/lib/chef/knife/server_bootstrap_standalone.rb +25 -19
- data/lib/knife/server/credentials.rb +44 -7
- data/lib/knife/server/ssh.rb +57 -1
- data/lib/knife/server/version.rb +1 -1
- data/spec/chef/knife/server_bootstrap_ec2_spec.rb +28 -5
- data/spec/chef/knife/server_bootstrap_standalone_spec.rb +66 -5
- data/spec/knife/server/credientials_spec.rb +34 -0
- metadata +14 -6
@@ -22,61 +22,33 @@ class Chef
|
|
22
22
|
class Knife
|
23
23
|
class ServerBootstrapEc2 < Knife
|
24
24
|
|
25
|
+
banner "knife server bootstrap ec2 (options)"
|
26
|
+
|
25
27
|
include Knife::ServerBootstrapBase
|
26
28
|
|
27
29
|
deps do
|
28
30
|
require 'knife/server/ssh'
|
29
31
|
require 'knife/server/credentials'
|
30
32
|
require 'knife/server/ec2_security_group'
|
31
|
-
require 'chef/knife/ec2_server_create'
|
32
|
-
require 'fog'
|
33
|
-
Chef::Knife::Ec2ServerCreate.load_deps
|
34
|
-
end
|
35
|
-
|
36
|
-
banner "knife server bootstrap ec2 (options)"
|
37
33
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
:short => "-S KEY",
|
57
|
-
:long => "--ssh-key KEY",
|
58
|
-
:description => "The AWS SSH key id",
|
59
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:aws_ssh_key_id] = key }
|
60
|
-
|
61
|
-
option :flavor,
|
62
|
-
:short => "-f FLAVOR",
|
63
|
-
:long => "--flavor FLAVOR",
|
64
|
-
:description => "The flavor of server (m1.small, m1.medium, etc)",
|
65
|
-
:proc => Proc.new { |f| Chef::Config[:knife][:flavor] = f },
|
66
|
-
:default => "m1.small"
|
67
|
-
|
68
|
-
option :image,
|
69
|
-
:short => "-I IMAGE",
|
70
|
-
:long => "--image IMAGE",
|
71
|
-
:description => "The AMI for the server",
|
72
|
-
:proc => Proc.new { |i| Chef::Config[:knife][:image] = i }
|
73
|
-
|
74
|
-
option :availability_zone,
|
75
|
-
:short => "-Z ZONE",
|
76
|
-
:long => "--availability-zone ZONE",
|
77
|
-
:description => "The Availability Zone",
|
78
|
-
:default => "us-east-1b",
|
79
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:availability_zone] = key }
|
34
|
+
begin
|
35
|
+
require 'chef/knife/ec2_server_create'
|
36
|
+
require 'fog'
|
37
|
+
Chef::Knife::Ec2ServerCreate.load_deps
|
38
|
+
|
39
|
+
current_options = self.options
|
40
|
+
self.options = Chef::Knife::Ec2ServerCreate.options.dup
|
41
|
+
self.options.merge!(current_options)
|
42
|
+
rescue LoadError => ex
|
43
|
+
ui.error [
|
44
|
+
"Knife plugin knife-ec2 could not be loaded.",
|
45
|
+
"Please add the knife-ec2 gem to your Gemfile or",
|
46
|
+
"install the gem manually with `gem install knife-ec2'.",
|
47
|
+
"(#{ex.message})"
|
48
|
+
].join(" ")
|
49
|
+
exit 1
|
50
|
+
end
|
51
|
+
end
|
80
52
|
|
81
53
|
option :security_groups,
|
82
54
|
:short => "-G X,Y,Z",
|
@@ -85,20 +57,6 @@ class Chef
|
|
85
57
|
:default => ["infrastructure"],
|
86
58
|
:proc => Proc.new { |groups| groups.split(',') }
|
87
59
|
|
88
|
-
option :tags,
|
89
|
-
:short => "-T T=V[,T=V,...]",
|
90
|
-
:long => "--tags Tag=Value[,Tag=Value...]",
|
91
|
-
:description => "The tags for this server",
|
92
|
-
:proc => Proc.new { |tags| tags.split(',') }
|
93
|
-
|
94
|
-
option :ebs_size,
|
95
|
-
:long => "--ebs-size SIZE",
|
96
|
-
:description => "The size of the EBS volume in GB, for EBS-backed instances"
|
97
|
-
|
98
|
-
option :ebs_no_delete_on_term,
|
99
|
-
:long => "--ebs-no-delete-on-term",
|
100
|
-
:description => "Do not delete EBS volumn on instance termination"
|
101
|
-
|
102
60
|
def run
|
103
61
|
validate!
|
104
62
|
config_security_group
|
@@ -112,7 +70,9 @@ class Chef
|
|
112
70
|
ENV['WEBUI_PASSWORD'] = config[:webui_password]
|
113
71
|
ENV['AMQP_PASSWORD'] = config[:amqp_password]
|
114
72
|
bootstrap = Chef::Knife::Ec2ServerCreate.new
|
115
|
-
|
73
|
+
Chef::Knife::Ec2ServerCreate.options.keys.each do |attr|
|
74
|
+
bootstrap.config[attr] = config_val(attr)
|
75
|
+
end
|
116
76
|
bootstrap.config[:tags] = bootstrap_tags
|
117
77
|
bootstrap.config[:distro] = bootstrap_distro
|
118
78
|
bootstrap
|
@@ -121,16 +81,16 @@ class Chef
|
|
121
81
|
def ec2_connection
|
122
82
|
@ec2_connection ||= Fog::Compute.new(
|
123
83
|
:provider => 'AWS',
|
124
|
-
:aws_access_key_id =>
|
125
|
-
:aws_secret_access_key =>
|
126
|
-
:region =>
|
84
|
+
:aws_access_key_id => config_val(:aws_access_key_id),
|
85
|
+
:aws_secret_access_key => config_val(:aws_secret_access_key),
|
86
|
+
:region => config_val(:region)
|
127
87
|
)
|
128
88
|
end
|
129
89
|
|
130
90
|
def server_dns_name
|
131
91
|
server = ec2_connection.servers.find do |s|
|
132
92
|
s.state == "running" &&
|
133
|
-
s.tags['Name'] ==
|
93
|
+
s.tags['Name'] == config_val(:chef_node_name) &&
|
134
94
|
s.tags['Role'] == 'chef_server'
|
135
95
|
end
|
136
96
|
|
@@ -144,25 +104,37 @@ class Chef
|
|
144
104
|
ui.error "You did not provide a valid --node-name value."
|
145
105
|
exit 1
|
146
106
|
end
|
107
|
+
if config_val(:platform) == "auto"
|
108
|
+
ui.error "Auto platform mode cannot be used with knife-ec2 plugin"
|
109
|
+
exit 1
|
110
|
+
end
|
147
111
|
end
|
148
112
|
|
149
|
-
def config_security_group(name =
|
113
|
+
def config_security_group(name = nil)
|
114
|
+
name = config_val(:security_groups).first if name.nil?
|
115
|
+
|
150
116
|
::Knife::Server::Ec2SecurityGroup.new(ec2_connection, ui).
|
151
117
|
configure_chef_server_group(name, :description => "#{name} group")
|
152
118
|
end
|
153
119
|
|
154
120
|
def bootstrap_tags
|
155
|
-
Hash[Array(
|
121
|
+
Hash[Array(config_val(:tags)).map { |t| t.split('=') }].
|
156
122
|
merge({"Role" => "chef_server"}).map { |k,v| "#{k}=#{v}" }
|
157
123
|
end
|
158
124
|
|
159
125
|
def ssh_connection
|
160
|
-
|
126
|
+
opts = {
|
161
127
|
:host => server_dns_name,
|
162
|
-
:user =>
|
163
|
-
:port =>
|
164
|
-
:keys => [
|
165
|
-
|
128
|
+
:user => config_val(:ssh_user),
|
129
|
+
:port => config_val(:ssh_port),
|
130
|
+
:keys => [config_val(:identity_file)].compact
|
131
|
+
}
|
132
|
+
if config_val(:host_key_verify) == false
|
133
|
+
opts[:user_known_hosts_file] = "/dev/null"
|
134
|
+
opts[:paranoid] = false
|
135
|
+
end
|
136
|
+
|
137
|
+
::Knife::Server::SSH.new(opts)
|
166
138
|
end
|
167
139
|
end
|
168
140
|
end
|
@@ -22,6 +22,8 @@ class Chef
|
|
22
22
|
class Knife
|
23
23
|
class ServerBootstrapStandalone < Knife
|
24
24
|
|
25
|
+
banner "knife server bootstrap standalone (options)"
|
26
|
+
|
25
27
|
include Knife::ServerBootstrapBase
|
26
28
|
|
27
29
|
deps do
|
@@ -29,20 +31,17 @@ class Chef
|
|
29
31
|
require 'knife/server/credentials'
|
30
32
|
require 'chef/knife/bootstrap'
|
31
33
|
Chef::Knife::Bootstrap.load_deps
|
32
|
-
end
|
33
34
|
|
34
|
-
|
35
|
+
current_options = self.options
|
36
|
+
self.options = Chef::Knife::Bootstrap.options.dup
|
37
|
+
self.options.merge!(current_options)
|
38
|
+
end
|
35
39
|
|
36
40
|
option :host,
|
37
41
|
:short => "-H FQDN_OR_IP",
|
38
42
|
:long => "--host FQDN_OR_IP",
|
39
43
|
:description => "Hostname or IP address of host to bootstrap"
|
40
44
|
|
41
|
-
option :ssh_password,
|
42
|
-
:short => "-P PASSWORD",
|
43
|
-
:long => "--ssh-password PASSWORD",
|
44
|
-
:description => "The ssh password"
|
45
|
-
|
46
45
|
def run
|
47
46
|
validate!
|
48
47
|
check_ssh_connection
|
@@ -57,10 +56,11 @@ class Chef
|
|
57
56
|
ENV['AMQP_PASSWORD'] = config[:amqp_password]
|
58
57
|
bootstrap = Chef::Knife::Bootstrap.new
|
59
58
|
bootstrap.name_args = [ config[:host] ]
|
60
|
-
|
61
|
-
|
59
|
+
Chef::Knife::Bootstrap.options.keys.each do |attr|
|
60
|
+
bootstrap.config[attr] = config_val(attr)
|
61
|
+
end
|
62
62
|
bootstrap.config[:distro] = bootstrap_distro
|
63
|
-
bootstrap.config[:use_sudo] = true unless
|
63
|
+
bootstrap.config[:use_sudo] = true unless config_val(:ssh_user) == "root"
|
64
64
|
bootstrap
|
65
65
|
end
|
66
66
|
|
@@ -80,21 +80,27 @@ class Chef
|
|
80
80
|
def check_ssh_connection
|
81
81
|
ssh_connection.exec! "hostname -f"
|
82
82
|
rescue Net::SSH::AuthenticationFailed
|
83
|
-
ui.warn("Failed to authenticate #{
|
83
|
+
ui.warn("Failed to authenticate #{config_val(:ssh_user)} - " +
|
84
84
|
"trying password auth")
|
85
85
|
config[:ssh_password] = ui.ask(
|
86
|
-
"Enter password for #{
|
86
|
+
"Enter password for #{config_val(:ssh_user)}@#{config_val(:host)}: "
|
87
87
|
) { |q| q.echo = false }
|
88
88
|
end
|
89
89
|
|
90
90
|
def ssh_connection
|
91
|
-
|
92
|
-
:host =>
|
93
|
-
:user =>
|
94
|
-
:password =>
|
95
|
-
:port =>
|
96
|
-
:keys => [
|
97
|
-
|
91
|
+
opts = {
|
92
|
+
:host => config_val(:host),
|
93
|
+
:user => config_val(:ssh_user),
|
94
|
+
:password => config_val(:ssh_password),
|
95
|
+
:port => config_val(:ssh_port),
|
96
|
+
:keys => [config_val(:identity_file)].compact
|
97
|
+
}
|
98
|
+
if config_val(:host_key_verify) == false
|
99
|
+
opts[:user_known_hosts_file] = "/dev/null"
|
100
|
+
opts[:paranoid] = false
|
101
|
+
end
|
102
|
+
|
103
|
+
::Knife::Server::SSH.new(opts)
|
98
104
|
end
|
99
105
|
end
|
100
106
|
end
|
@@ -21,9 +21,10 @@ require 'fileutils'
|
|
21
21
|
module Knife
|
22
22
|
module Server
|
23
23
|
class Credentials
|
24
|
-
def initialize(ssh, validation_key_path)
|
24
|
+
def initialize(ssh, validation_key_path, options = {})
|
25
25
|
@ssh = ssh
|
26
26
|
@validation_key_path = validation_key_path
|
27
|
+
@omnibus = options[:omnibus]
|
27
28
|
end
|
28
29
|
|
29
30
|
def install_validation_key(suffix = Time.now.to_i)
|
@@ -32,20 +33,39 @@ module Knife
|
|
32
33
|
backup_file_path(@validation_key_path, suffix))
|
33
34
|
end
|
34
35
|
|
36
|
+
chef10_key = "/etc/chef/validation.pem"
|
37
|
+
omnibus_key = "/etc/chef-server/chef-validator.pem"
|
38
|
+
|
35
39
|
File.open(@validation_key_path, "wb") do |f|
|
36
|
-
f.write(@ssh.exec!("cat
|
40
|
+
f.write(@ssh.exec!("cat #{omnibus? ? omnibus_key : chef10_key}"))
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
40
44
|
def create_root_client
|
41
|
-
|
45
|
+
chef10_cmd = [
|
42
46
|
"knife configure",
|
43
47
|
"--initial",
|
44
48
|
"--server-url http://127.0.0.1:4000",
|
45
49
|
"--user root",
|
46
50
|
'--repository ""',
|
47
51
|
"--defaults --yes"
|
48
|
-
].join(" ")
|
52
|
+
].join(" ")
|
53
|
+
|
54
|
+
omnibus_cmd = [
|
55
|
+
"echo '#{ENV['WEBUI_PASSWORD']}' |",
|
56
|
+
"knife configure",
|
57
|
+
"--initial",
|
58
|
+
"--server-url http://127.0.0.1:8000",
|
59
|
+
"--user root",
|
60
|
+
'--repository ""',
|
61
|
+
"--admin-client-name chef-webui",
|
62
|
+
"--admin-client-key /etc/chef-server/chef-webui.pem",
|
63
|
+
"--validation-client-name chef-validator",
|
64
|
+
"--validation-key /etc/chef-server/chef-validator.pem",
|
65
|
+
"--defaults --yes"
|
66
|
+
].join(" ")
|
67
|
+
|
68
|
+
@ssh.exec!(omnibus? ? omnibus_cmd : chef10_cmd)
|
49
69
|
end
|
50
70
|
|
51
71
|
def install_client_key(user, client_key_path, suffix = Time.now.to_i)
|
@@ -65,17 +85,34 @@ module Knife
|
|
65
85
|
|
66
86
|
private
|
67
87
|
|
88
|
+
def omnibus?
|
89
|
+
@omnibus
|
90
|
+
end
|
91
|
+
|
68
92
|
def backup_file_path(file_path, suffix)
|
69
93
|
parts = file_path.rpartition(".")
|
70
94
|
"#{parts[0]}.#{suffix}.#{parts[2]}"
|
71
95
|
end
|
72
96
|
|
73
97
|
def create_user_client(user)
|
74
|
-
|
98
|
+
chef10_cmd = [
|
75
99
|
"knife client create",
|
76
100
|
user,
|
77
|
-
"--admin
|
78
|
-
|
101
|
+
"--admin",
|
102
|
+
"--file /tmp/chef-client-#{user}.pem",
|
103
|
+
"--disable-editing"
|
104
|
+
].join(" ")
|
105
|
+
|
106
|
+
omnibus_cmd = [
|
107
|
+
"knife user create",
|
108
|
+
user,
|
109
|
+
"--admin",
|
110
|
+
"--file /tmp/chef-client-#{user}.pem",
|
111
|
+
"--disable-editing",
|
112
|
+
"--password #{ENV['WEBUI_PASSWORD']}"
|
113
|
+
].join(" ")
|
114
|
+
|
115
|
+
@ssh.exec!(omnibus? ? omnibus_cmd : chef10_cmd)
|
79
116
|
end
|
80
117
|
end
|
81
118
|
end
|
data/lib/knife/server/ssh.rb
CHANGED
@@ -22,6 +22,7 @@ module Knife
|
|
22
22
|
module Server
|
23
23
|
class SSH
|
24
24
|
DEFAULT_OPTIONS = { :user => "root", :port => "22" }.freeze
|
25
|
+
USER_SWITCH_COMMAND = %[sudo USER=root HOME="$(getent passwd root | cut -d : -f 6)"]
|
25
26
|
|
26
27
|
def initialize(params)
|
27
28
|
options = DEFAULT_OPTIONS.merge(params)
|
@@ -36,7 +37,7 @@ module Knife
|
|
36
37
|
full_cmd = cmd
|
37
38
|
else
|
38
39
|
full_cmd = [
|
39
|
-
|
40
|
+
USER_SWITCH_COMMAND,
|
40
41
|
%[bash -c '#{cmd}']
|
41
42
|
].join(" ")
|
42
43
|
end
|
@@ -47,6 +48,61 @@ module Knife
|
|
47
48
|
end
|
48
49
|
result
|
49
50
|
end
|
51
|
+
|
52
|
+
# runs a script on the target host by passing it to the stdin of a sh
|
53
|
+
# process. returns stdout and the exit status. does not care about stderr.
|
54
|
+
def run_script(content)
|
55
|
+
user_switch = ""
|
56
|
+
|
57
|
+
unless @user == "root"
|
58
|
+
user_switch = USER_SWITCH_COMMAND
|
59
|
+
end
|
60
|
+
|
61
|
+
wrapper = <<-EOF
|
62
|
+
if [ -e /dev/fd/0 ]
|
63
|
+
then
|
64
|
+
#{user_switch} /bin/sh /dev/fd/0
|
65
|
+
elif [ -e /dev/stdin ]
|
66
|
+
then
|
67
|
+
#{user_switch} /bin/sh /dev/stdin
|
68
|
+
else
|
69
|
+
echo "Cannot find method of communicating with the shell via stdin"
|
70
|
+
exit 1
|
71
|
+
fi
|
72
|
+
EOF
|
73
|
+
|
74
|
+
result = ""
|
75
|
+
exit_status = nil
|
76
|
+
|
77
|
+
Net::SSH.start(@host, @user, @options) do |ssh|
|
78
|
+
ssh.open_channel do |ch|
|
79
|
+
ch.on_open_failed do |ch, code, desc|
|
80
|
+
raise "Connection Error to #{ip}: #{desc}"
|
81
|
+
end
|
82
|
+
|
83
|
+
ch.exec(wrapper) do |channel, type, data|
|
84
|
+
# spit out the shell script and close stdin so sh can do its magic.
|
85
|
+
channel.send_data(content)
|
86
|
+
channel.eof!
|
87
|
+
|
88
|
+
# then we just wait for sweet, sweet output.
|
89
|
+
channel.on_data do |ch2, data|
|
90
|
+
result << data
|
91
|
+
end
|
92
|
+
|
93
|
+
channel.on_request("exit-status") do |ch2, data|
|
94
|
+
exit_status = data.read_long
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
ch.wait
|
99
|
+
end
|
100
|
+
|
101
|
+
ssh.loop
|
102
|
+
end
|
103
|
+
|
104
|
+
return result, exit_status
|
105
|
+
end
|
50
106
|
end
|
51
107
|
end
|
52
108
|
end
|
data/lib/knife/server/version.rb
CHANGED
@@ -33,6 +33,8 @@ describe Chef::Knife::ServerBootstrapEc2 do
|
|
33
33
|
@stderr = StringIO.new
|
34
34
|
@knife.ui.stub!(:stderr).and_return(@stderr)
|
35
35
|
@knife.config[:chef_node_name] = "yakky"
|
36
|
+
@knife.config[:platform] = "omnibus"
|
37
|
+
@knife.config[:ssh_user] = "root"
|
36
38
|
end
|
37
39
|
|
38
40
|
let(:connection) { mock(Fog::Compute::AWS) }
|
@@ -102,17 +104,25 @@ describe Chef::Knife::ServerBootstrapEc2 do
|
|
102
104
|
bootstrap.config[:distro].should eq("distro-praha")
|
103
105
|
end
|
104
106
|
|
105
|
-
it "configs the bootstrap's distro to
|
107
|
+
it "configs the bootstrap's distro to chef11/omnibus by default" do
|
106
108
|
@knife.config.delete(:distro)
|
107
109
|
|
108
|
-
bootstrap.config[:distro].should eq("
|
110
|
+
bootstrap.config[:distro].should eq("chef11/omnibus")
|
109
111
|
end
|
110
112
|
|
111
113
|
it "configs the bootstrap's distro value driven off platform value" do
|
112
114
|
@knife.config.delete(:distro)
|
113
115
|
@knife.config[:platform] = "freebsd"
|
114
116
|
|
115
|
-
bootstrap.config[:distro].should eq("
|
117
|
+
bootstrap.config[:distro].should eq("chef11/freebsd")
|
118
|
+
end
|
119
|
+
|
120
|
+
it "configs the bootstrap's distro based on bootstrap_version and platform" do
|
121
|
+
@knife.config.delete(:distro)
|
122
|
+
@knife.config[:platform] = "freebsd"
|
123
|
+
@knife.config[:bootstrap_version] = "10"
|
124
|
+
|
125
|
+
bootstrap.config[:distro].should eq("chef10/freebsd")
|
116
126
|
end
|
117
127
|
|
118
128
|
it "configs the bootstrap's ENV with the webui password" do
|
@@ -251,13 +261,26 @@ describe Chef::Knife::ServerBootstrapEc2 do
|
|
251
261
|
@knife.run
|
252
262
|
end
|
253
263
|
|
254
|
-
it "installs a new validation.pem key from the server" do
|
264
|
+
it "installs a new validation.pem key from the chef 10 server" do
|
265
|
+
@knife.config[:bootstrap_version] = "10"
|
266
|
+
Knife::Server::SSH.should_receive(:new).with({
|
267
|
+
:host => "grapes.wrath", :user => "root",
|
268
|
+
:port => "2345", :keys => ["~/.ssh/mykey_dsa"]
|
269
|
+
})
|
270
|
+
Knife::Server::Credentials.should_receive(:new).
|
271
|
+
with(ssh, "/etc/chef/validation.pem", {})
|
272
|
+
credentials.should_receive(:install_validation_key)
|
273
|
+
|
274
|
+
@knife.run
|
275
|
+
end
|
276
|
+
|
277
|
+
it "installs a new validation.pem key from the omnibus server" do
|
255
278
|
Knife::Server::SSH.should_receive(:new).with({
|
256
279
|
:host => "grapes.wrath", :user => "root",
|
257
280
|
:port => "2345", :keys => ["~/.ssh/mykey_dsa"]
|
258
281
|
})
|
259
282
|
Knife::Server::Credentials.should_receive(:new).
|
260
|
-
with(ssh, "/etc/chef/validation.pem")
|
283
|
+
with(ssh, "/etc/chef/validation.pem", {:omnibus => true})
|
261
284
|
credentials.should_receive(:install_validation_key)
|
262
285
|
|
263
286
|
@knife.run
|