ztk 1.1.2 → 1.2.0
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/README.md +1 -1
- data/lib/ztk/ssh/bootstrap.rb +48 -0
- data/lib/ztk/ssh/command.rb +73 -0
- data/lib/ztk/ssh/download.rb +68 -0
- data/lib/ztk/ssh/exec.rb +148 -0
- data/lib/ztk/ssh/upload.rb +68 -0
- data/lib/ztk/ssh.rb +12 -246
- data/lib/ztk/template.rb +23 -0
- data/lib/ztk/version.rb +1 -1
- metadata +9 -4
data/README.md
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
################################################################################
|
2
|
+
#
|
3
|
+
# Author: Zachary Patten <zachary@jovelabs.net>
|
4
|
+
# Copyright: Copyright (c) Zachary Patten
|
5
|
+
# License: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
################################################################################
|
20
|
+
module ZTK
|
21
|
+
class SSH
|
22
|
+
|
23
|
+
module Bootstrap
|
24
|
+
require 'tempfile'
|
25
|
+
|
26
|
+
def bootstrap(content, use_sudo=true)
|
27
|
+
tempfile = Tempfile.new("bootstrap")
|
28
|
+
|
29
|
+
File.open(tempfile, 'w') do |file|
|
30
|
+
file.puts(content)
|
31
|
+
file.respond_to?(:flush) and file.flush
|
32
|
+
end
|
33
|
+
|
34
|
+
self.upload(tempfile.path, tempfile.path)
|
35
|
+
|
36
|
+
command = Array.new
|
37
|
+
command << %(sudo) if (use_sudo == true)
|
38
|
+
command << %(/bin/bash)
|
39
|
+
command << tempfile.path
|
40
|
+
command = command.join(' ')
|
41
|
+
|
42
|
+
self.exec(command, :silence => true)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
################################################################################
|
2
|
+
#
|
3
|
+
# Author: Zachary Patten <zachary@jovelabs.net>
|
4
|
+
# Copyright: Copyright (c) Zachary Patten
|
5
|
+
# License: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
################################################################################
|
20
|
+
module ZTK
|
21
|
+
class SSH
|
22
|
+
|
23
|
+
module Command
|
24
|
+
|
25
|
+
# Builds our SSH console command.
|
26
|
+
def console_command
|
27
|
+
verbosity = ((ENV['LOG_LEVEL'].upcase == "DEBUG") ? '-vv' : '-q')
|
28
|
+
|
29
|
+
command = [ "/usr/bin/env ssh" ]
|
30
|
+
command << [ verbosity ]
|
31
|
+
command << [ "-x" ]
|
32
|
+
command << [ "-a" ]
|
33
|
+
command << [ "-o", "UserKnownHostsFile=/dev/null" ]
|
34
|
+
command << [ "-o", "StrictHostKeyChecking=no" ]
|
35
|
+
command << [ "-o", "KeepAlive=yes" ]
|
36
|
+
command << [ "-o", "ServerAliveInterval=60" ]
|
37
|
+
command << [ "-o", %(ProxyCommand="#{proxy_command}") ] if config.proxy_host_name
|
38
|
+
command << [ "-i", config.keys ] if config.keys
|
39
|
+
command << [ "-p", config.port ] if config.port
|
40
|
+
command << "#{config.user}@#{config.host_name}"
|
41
|
+
command = command.flatten.compact.join(' ')
|
42
|
+
config.ui.logger.debug { "console_command(#{command.inspect})" }
|
43
|
+
command
|
44
|
+
end
|
45
|
+
|
46
|
+
# Builds our SSH proxy command.
|
47
|
+
def proxy_command
|
48
|
+
!config.proxy_user and log_and_raise(SSHError, "You must specify an proxy user in order to SSH proxy.")
|
49
|
+
!config.proxy_host_name and log_and_raise(SSHError, "You must specify an proxy host_name in order to SSH proxy.")
|
50
|
+
|
51
|
+
verbosity = ((ENV['LOG_LEVEL'].upcase == "DEBUG") ? '-vv' : '-q')
|
52
|
+
|
53
|
+
command = ["/usr/bin/env ssh"]
|
54
|
+
command << [ verbosity ]
|
55
|
+
command << [ "-x" ]
|
56
|
+
command << [ "-a" ]
|
57
|
+
command << [ "-o", "UserKnownHostsFile=/dev/null" ]
|
58
|
+
command << [ "-o", "StrictHostKeyChecking=no" ]
|
59
|
+
command << [ "-o", "KeepAlive=yes" ]
|
60
|
+
command << [ "-o", "ServerAliveInterval=60" ]
|
61
|
+
command << [ "-i", config.proxy_keys ] if config.proxy_keys
|
62
|
+
command << [ "-p", config.proxy_port ] if config.proxy_port
|
63
|
+
command << "#{config.proxy_user}@#{config.proxy_host_name}"
|
64
|
+
command << "'/usr/bin/env nc %h %p'"
|
65
|
+
command = command.flatten.compact.join(' ')
|
66
|
+
config.ui.logger.debug { "proxy_command(#{command.inspect})" }
|
67
|
+
command
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
################################################################################
|
2
|
+
#
|
3
|
+
# Author: Zachary Patten <zachary@jovelabs.net>
|
4
|
+
# Copyright: Copyright (c) Zachary Patten
|
5
|
+
# License: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
################################################################################
|
20
|
+
module ZTK
|
21
|
+
class SSH
|
22
|
+
|
23
|
+
module Download
|
24
|
+
|
25
|
+
# Downloads a remote file to the local host.
|
26
|
+
#
|
27
|
+
# @param [String] remote The remote file/path you with to download from.
|
28
|
+
# @param [String] local The local file/path you wish to download to.
|
29
|
+
#
|
30
|
+
# @example Download a file:
|
31
|
+
# $logger = ZTK::Logger.new(STDOUT)
|
32
|
+
# ssh = ZTK::SSH.new
|
33
|
+
# ssh.config do |config|
|
34
|
+
# config.user = ENV["USER"]
|
35
|
+
# config.host_name = "127.0.0.1"
|
36
|
+
# end
|
37
|
+
# local = File.expand_path(File.join("/tmp", "id_rsa.pub"))
|
38
|
+
# remote = File.expand_path(File.join(ENV["HOME"], ".ssh", "id_rsa.pub"))
|
39
|
+
# ssh.download(remote, local)
|
40
|
+
def download(remote, local)
|
41
|
+
config.ui.logger.debug { "config=#{config.send(:table).inspect}" }
|
42
|
+
config.ui.logger.info { "download(#{remote.inspect}, #{local.inspect})" }
|
43
|
+
|
44
|
+
ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
|
45
|
+
@sftp = Net::SFTP.start(config.host_name, config.user, ssh_options)
|
46
|
+
sftp.download!(remote.to_s, local.to_s) do |event, downloader, *args|
|
47
|
+
case event
|
48
|
+
when :open
|
49
|
+
config.ui.logger.debug { "download(#{args[0].remote} -> #{args[0].local})" }
|
50
|
+
when :close
|
51
|
+
config.ui.logger.debug { "close(#{args[0].local})" }
|
52
|
+
when :mkdir
|
53
|
+
config.ui.logger.debug { "mkdir(#{args[0]})" }
|
54
|
+
when :get
|
55
|
+
config.ui.logger.debug { "get(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
|
56
|
+
when :finish
|
57
|
+
config.ui.logger.debug { "finish" }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
data/lib/ztk/ssh/exec.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
################################################################################
|
2
|
+
#
|
3
|
+
# Author: Zachary Patten <zachary@jovelabs.net>
|
4
|
+
# Copyright: Copyright (c) Zachary Patten
|
5
|
+
# License: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
################################################################################
|
20
|
+
module ZTK
|
21
|
+
class SSH
|
22
|
+
|
23
|
+
module Exec
|
24
|
+
|
25
|
+
# Executes a command on the remote host.
|
26
|
+
#
|
27
|
+
# @param [String] command The command to execute.
|
28
|
+
# @param [Hash] options The options hash for executing the command.
|
29
|
+
# @option options [Boolean] :silence Squelch output to STDOUT and STDERR.
|
30
|
+
# If the log level is :debug, STDOUT and STDERR will go to the log file
|
31
|
+
# regardless of this setting. STDOUT and STDERR are always returned in
|
32
|
+
# the output return value regardless of this setting.
|
33
|
+
#
|
34
|
+
# @return [OpenStruct#output] The output of the command, both STDOUT and
|
35
|
+
# STDERR.
|
36
|
+
# @return [OpenStruct#exit] The exit status (i.e. $?).
|
37
|
+
#
|
38
|
+
# @example Execute a command:
|
39
|
+
#
|
40
|
+
# ssh = ZTK::SSH.new
|
41
|
+
# ssh.config do |config|
|
42
|
+
# config.user = ENV["USER"]
|
43
|
+
# config.host_name = "127.0.0.1"
|
44
|
+
# end
|
45
|
+
# puts ssh.exec("hostname").inspect
|
46
|
+
def exec(command, options={})
|
47
|
+
options = OpenStruct.new({ :exit_code => 0, :silence => false }.merge(config.send(:table)).merge(options))
|
48
|
+
|
49
|
+
options.ui.logger.debug { "config=#{options.send(:table).inspect}" }
|
50
|
+
options.ui.logger.debug { "options=#{options.send(:table).inspect}" }
|
51
|
+
options.ui.logger.info { "exec(#{command.inspect})" }
|
52
|
+
|
53
|
+
output = ""
|
54
|
+
exit_code = -1
|
55
|
+
exit_signal = nil
|
56
|
+
stdout_header = false
|
57
|
+
stderr_header = false
|
58
|
+
|
59
|
+
begin
|
60
|
+
Timeout.timeout(options.timeout) do
|
61
|
+
ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
|
62
|
+
@ssh = Net::SSH.start(options.host_name, options.user, ssh_options)
|
63
|
+
|
64
|
+
channel = ssh.open_channel do |chan|
|
65
|
+
options.ui.logger.debug { "Channel opened." }
|
66
|
+
|
67
|
+
(options.request_pty == true) and chan.request_pty do |ch, success|
|
68
|
+
if success
|
69
|
+
options.ui.logger.debug { "PTY obtained." }
|
70
|
+
else
|
71
|
+
options.ui.logger.warn { "Could not obtain PTY." }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
direct_log(:info) { log_header("COMMAND") }
|
76
|
+
direct_log(:info) { "#{command}\n" }
|
77
|
+
direct_log(:info) { log_header("OPENED") }
|
78
|
+
|
79
|
+
chan.exec(command) do |ch, success|
|
80
|
+
success or log_and_raise(SSHError, "Could not execute '#{command}'.")
|
81
|
+
|
82
|
+
ch.on_data do |c, data|
|
83
|
+
if !stdout_header
|
84
|
+
direct_log(:info) { log_header("STDOUT") }
|
85
|
+
stdout_header = true
|
86
|
+
stderr_header = false
|
87
|
+
end
|
88
|
+
direct_log(:info) { data }
|
89
|
+
|
90
|
+
options.ui.stdout.print(data) unless options.silence
|
91
|
+
output += data
|
92
|
+
end
|
93
|
+
|
94
|
+
ch.on_extended_data do |c, type, data|
|
95
|
+
if !stderr_header
|
96
|
+
direct_log(:warn) { log_header("STDERR") }
|
97
|
+
stderr_header = true
|
98
|
+
stdout_header = false
|
99
|
+
end
|
100
|
+
direct_log(:warn) { data }
|
101
|
+
|
102
|
+
options.ui.stderr.print(data) unless options.silence
|
103
|
+
output += data
|
104
|
+
end
|
105
|
+
|
106
|
+
ch.on_request("exit-status") do |ch, data|
|
107
|
+
exit_code = data.read_long
|
108
|
+
end
|
109
|
+
|
110
|
+
ch.on_request("exit-signal") do |ch, data|
|
111
|
+
exit_signal = data.read_long
|
112
|
+
end
|
113
|
+
|
114
|
+
ch.on_open_failed do |c, code, desc|
|
115
|
+
options.ui.logger.fatal { "Open failed! (#{code.inspect} - #{desc.inspect})" }
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
120
|
+
channel.wait
|
121
|
+
|
122
|
+
direct_log(:info) { log_header("CLOSED") }
|
123
|
+
options.ui.logger.debug { "Channel closed." }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
rescue Timeout::Error => e
|
128
|
+
direct_log(:fatal) { log_header("TIMEOUT") }
|
129
|
+
log_and_raise(SSHError, "Session timed out after #{options.timeout} seconds!")
|
130
|
+
end
|
131
|
+
|
132
|
+
message = [
|
133
|
+
"exit_code=#{exit_code}",
|
134
|
+
(exit_signal.nil? ? nil : "exit_signal=#{exit_signal} (#{EXIT_SIGNALS[exit_signal]})")
|
135
|
+
].compact.join(", ")
|
136
|
+
|
137
|
+
options.ui.logger.debug { message }
|
138
|
+
|
139
|
+
if !options.ignore_exit_status && (exit_code != options.exit_code)
|
140
|
+
log_and_raise(SSHError, message)
|
141
|
+
end
|
142
|
+
OpenStruct.new(:command => command, :output => output, :exit_code => exit_code, :exit_signal => exit_signal)
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
################################################################################
|
2
|
+
#
|
3
|
+
# Author: Zachary Patten <zachary@jovelabs.net>
|
4
|
+
# Copyright: Copyright (c) Zachary Patten
|
5
|
+
# License: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
################################################################################
|
20
|
+
module ZTK
|
21
|
+
class SSH
|
22
|
+
|
23
|
+
module Upload
|
24
|
+
|
25
|
+
# Uploads a local file to a remote host.
|
26
|
+
#
|
27
|
+
# @param [String] local The local file/path you wish to upload from.
|
28
|
+
# @param [String] remote The remote file/path you with to upload to.
|
29
|
+
#
|
30
|
+
# @example Upload a file:
|
31
|
+
# $logger = ZTK::Logger.new(STDOUT)
|
32
|
+
# ssh = ZTK::SSH.new
|
33
|
+
# ssh.config do |config|
|
34
|
+
# config.user = ENV["USER"]
|
35
|
+
# config.host_name = "127.0.0.1"
|
36
|
+
# end
|
37
|
+
# local = File.expand_path(File.join(ENV["HOME"], ".ssh", "id_rsa.pub"))
|
38
|
+
# remote = File.expand_path(File.join("/tmp", "id_rsa.pub"))
|
39
|
+
# ssh.upload(local, remote)
|
40
|
+
def upload(local, remote)
|
41
|
+
config.ui.logger.debug { "config=#{config.send(:table).inspect}" }
|
42
|
+
config.ui.logger.info { "upload(#{local.inspect}, #{remote.inspect})" }
|
43
|
+
|
44
|
+
ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
|
45
|
+
@sftp = Net::SFTP.start(config.host_name, config.user, ssh_options)
|
46
|
+
sftp.upload!(local.to_s, remote.to_s) do |event, uploader, *args|
|
47
|
+
case event
|
48
|
+
when :open
|
49
|
+
config.ui.logger.debug { "upload(#{args[0].local} -> #{args[0].remote})" }
|
50
|
+
when :close
|
51
|
+
config.ui.logger.debug { "close(#{args[0].remote})" }
|
52
|
+
when :mkdir
|
53
|
+
config.ui.logger.debug { "mkdir(#{args[0]})" }
|
54
|
+
when :put
|
55
|
+
config.ui.logger.debug { "put(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
|
56
|
+
when :finish
|
57
|
+
config.ui.logger.debug { "finish" }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
data/lib/ztk/ssh.rb
CHANGED
@@ -84,7 +84,6 @@ module ZTK
|
|
84
84
|
#
|
85
85
|
# @author Zachary Patten <zachary@jovelabs.net>
|
86
86
|
class SSH < ZTK::Base
|
87
|
-
|
88
87
|
# Exit Signal Mappings
|
89
88
|
EXIT_SIGNALS = {
|
90
89
|
1 => "SIGHUP",
|
@@ -116,6 +115,18 @@ module ZTK
|
|
116
115
|
27 => "SIGPROF"
|
117
116
|
}
|
118
117
|
|
118
|
+
autoload :Bootstrap, 'ztk/ssh/bootstrap'
|
119
|
+
autoload :Command, 'ztk/ssh/command'
|
120
|
+
autoload :Download, 'ztk/ssh/download'
|
121
|
+
autoload :Exec, 'ztk/ssh/exec'
|
122
|
+
autoload :Upload, 'ztk/ssh/upload'
|
123
|
+
|
124
|
+
include ZTK::SSH::Bootstrap
|
125
|
+
include ZTK::SSH::Command
|
126
|
+
include ZTK::SSH::Download
|
127
|
+
include ZTK::SSH::Exec
|
128
|
+
include ZTK::SSH::Upload
|
129
|
+
|
119
130
|
# @param [Hash] configuration Configuration options hash.
|
120
131
|
# @option config [String] :host_name Server hostname to connect to.
|
121
132
|
# @option config [String] :user Username to use for authentication.
|
@@ -185,254 +196,9 @@ module ZTK
|
|
185
196
|
Kernel.exec(console_command)
|
186
197
|
end
|
187
198
|
|
188
|
-
# Executes a command on the remote host.
|
189
|
-
#
|
190
|
-
# @param [String] command The command to execute.
|
191
|
-
# @param [Hash] options The options hash for executing the command.
|
192
|
-
# @option options [Boolean] :silence Squelch output to STDOUT and STDERR.
|
193
|
-
# If the log level is :debug, STDOUT and STDERR will go to the log file
|
194
|
-
# regardless of this setting. STDOUT and STDERR are always returned in
|
195
|
-
# the output return value regardless of this setting.
|
196
|
-
#
|
197
|
-
# @return [OpenStruct#output] The output of the command, both STDOUT and
|
198
|
-
# STDERR.
|
199
|
-
# @return [OpenStruct#exit] The exit status (i.e. $?).
|
200
|
-
#
|
201
|
-
# @example Execute a command:
|
202
|
-
#
|
203
|
-
# ssh = ZTK::SSH.new
|
204
|
-
# ssh.config do |config|
|
205
|
-
# config.user = ENV["USER"]
|
206
|
-
# config.host_name = "127.0.0.1"
|
207
|
-
# end
|
208
|
-
# puts ssh.exec("hostname").inspect
|
209
|
-
def exec(command, options={})
|
210
|
-
options = OpenStruct.new({ :exit_code => 0, :silence => false }.merge(config.send(:table)).merge(options))
|
211
|
-
|
212
|
-
options.ui.logger.debug { "config=#{options.send(:table).inspect}" }
|
213
|
-
options.ui.logger.debug { "options=#{options.send(:table).inspect}" }
|
214
|
-
options.ui.logger.info { "exec(#{command.inspect})" }
|
215
|
-
|
216
|
-
output = ""
|
217
|
-
exit_code = -1
|
218
|
-
exit_signal = nil
|
219
|
-
stdout_header = false
|
220
|
-
stderr_header = false
|
221
|
-
|
222
|
-
begin
|
223
|
-
Timeout.timeout(options.timeout) do
|
224
|
-
ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
|
225
|
-
@ssh = Net::SSH.start(options.host_name, options.user, ssh_options)
|
226
|
-
|
227
|
-
channel = ssh.open_channel do |chan|
|
228
|
-
options.ui.logger.debug { "Channel opened." }
|
229
|
-
|
230
|
-
(options.request_pty == true) and chan.request_pty do |ch, success|
|
231
|
-
if success
|
232
|
-
options.ui.logger.debug { "PTY obtained." }
|
233
|
-
else
|
234
|
-
options.ui.logger.warn { "Could not obtain PTY." }
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
direct_log(:info) { log_header("COMMAND") }
|
239
|
-
direct_log(:info) { "#{command}\n" }
|
240
|
-
direct_log(:info) { log_header("OPENED") }
|
241
|
-
|
242
|
-
chan.exec(command) do |ch, success|
|
243
|
-
success or log_and_raise(SSHError, "Could not execute '#{command}'.")
|
244
|
-
|
245
|
-
ch.on_data do |c, data|
|
246
|
-
if !stdout_header
|
247
|
-
direct_log(:info) { log_header("STDOUT") }
|
248
|
-
stdout_header = true
|
249
|
-
stderr_header = false
|
250
|
-
end
|
251
|
-
direct_log(:info) { data }
|
252
|
-
|
253
|
-
options.ui.stdout.print(data) unless options.silence
|
254
|
-
output += data
|
255
|
-
end
|
256
|
-
|
257
|
-
ch.on_extended_data do |c, type, data|
|
258
|
-
if !stderr_header
|
259
|
-
direct_log(:warn) { log_header("STDERR") }
|
260
|
-
stderr_header = true
|
261
|
-
stdout_header = false
|
262
|
-
end
|
263
|
-
direct_log(:warn) { data }
|
264
|
-
|
265
|
-
options.ui.stderr.print(data) unless options.silence
|
266
|
-
output += data
|
267
|
-
end
|
268
|
-
|
269
|
-
ch.on_request("exit-status") do |ch, data|
|
270
|
-
exit_code = data.read_long
|
271
|
-
end
|
272
|
-
|
273
|
-
ch.on_request("exit-signal") do |ch, data|
|
274
|
-
exit_signal = data.read_long
|
275
|
-
end
|
276
|
-
|
277
|
-
ch.on_open_failed do |c, code, desc|
|
278
|
-
options.ui.logger.fatal { "Open failed! (#{code.inspect} - #{desc.inspect})" }
|
279
|
-
end
|
280
|
-
|
281
|
-
end
|
282
|
-
end
|
283
|
-
channel.wait
|
284
|
-
|
285
|
-
direct_log(:info) { log_header("CLOSED") }
|
286
|
-
options.ui.logger.debug { "Channel closed." }
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
rescue Timeout::Error => e
|
291
|
-
direct_log(:fatal) { log_header("TIMEOUT") }
|
292
|
-
log_and_raise(SSHError, "Session timed out after #{options.timeout} seconds!")
|
293
|
-
end
|
294
|
-
|
295
|
-
message = [
|
296
|
-
"exit_code=#{exit_code}",
|
297
|
-
(exit_signal.nil? ? nil : "exit_signal=#{exit_signal} (#{EXIT_SIGNALS[exit_signal]})")
|
298
|
-
].compact.join(", ")
|
299
|
-
|
300
|
-
options.ui.logger.debug { message }
|
301
|
-
|
302
|
-
if !options.ignore_exit_status && (exit_code != options.exit_code)
|
303
|
-
log_and_raise(SSHError, message)
|
304
|
-
end
|
305
|
-
OpenStruct.new(:command => command, :output => output, :exit_code => exit_code, :exit_signal => exit_signal)
|
306
|
-
end
|
307
|
-
|
308
|
-
# Uploads a local file to a remote host.
|
309
|
-
#
|
310
|
-
# @param [String] local The local file/path you wish to upload from.
|
311
|
-
# @param [String] remote The remote file/path you with to upload to.
|
312
|
-
#
|
313
|
-
# @example Upload a file:
|
314
|
-
# $logger = ZTK::Logger.new(STDOUT)
|
315
|
-
# ssh = ZTK::SSH.new
|
316
|
-
# ssh.config do |config|
|
317
|
-
# config.user = ENV["USER"]
|
318
|
-
# config.host_name = "127.0.0.1"
|
319
|
-
# end
|
320
|
-
# local = File.expand_path(File.join(ENV["HOME"], ".ssh", "id_rsa.pub"))
|
321
|
-
# remote = File.expand_path(File.join("/tmp", "id_rsa.pub"))
|
322
|
-
# ssh.upload(local, remote)
|
323
|
-
def upload(local, remote)
|
324
|
-
config.ui.logger.debug { "config=#{config.send(:table).inspect}" }
|
325
|
-
config.ui.logger.info { "upload(#{local.inspect}, #{remote.inspect})" }
|
326
|
-
|
327
|
-
ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
|
328
|
-
@sftp = Net::SFTP.start(config.host_name, config.user, ssh_options)
|
329
|
-
sftp.upload!(local.to_s, remote.to_s) do |event, uploader, *args|
|
330
|
-
case event
|
331
|
-
when :open
|
332
|
-
config.ui.logger.debug { "upload(#{args[0].local} -> #{args[0].remote})" }
|
333
|
-
when :close
|
334
|
-
config.ui.logger.debug { "close(#{args[0].remote})" }
|
335
|
-
when :mkdir
|
336
|
-
config.ui.logger.debug { "mkdir(#{args[0]})" }
|
337
|
-
when :put
|
338
|
-
config.ui.logger.debug { "put(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
|
339
|
-
when :finish
|
340
|
-
config.ui.logger.debug { "finish" }
|
341
|
-
end
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
true
|
346
|
-
end
|
347
|
-
|
348
|
-
# Downloads a remote file to the local host.
|
349
|
-
#
|
350
|
-
# @param [String] remote The remote file/path you with to download from.
|
351
|
-
# @param [String] local The local file/path you wish to download to.
|
352
|
-
#
|
353
|
-
# @example Download a file:
|
354
|
-
# $logger = ZTK::Logger.new(STDOUT)
|
355
|
-
# ssh = ZTK::SSH.new
|
356
|
-
# ssh.config do |config|
|
357
|
-
# config.user = ENV["USER"]
|
358
|
-
# config.host_name = "127.0.0.1"
|
359
|
-
# end
|
360
|
-
# local = File.expand_path(File.join("/tmp", "id_rsa.pub"))
|
361
|
-
# remote = File.expand_path(File.join(ENV["HOME"], ".ssh", "id_rsa.pub"))
|
362
|
-
# ssh.download(remote, local)
|
363
|
-
def download(remote, local)
|
364
|
-
config.ui.logger.debug { "config=#{config.send(:table).inspect}" }
|
365
|
-
config.ui.logger.info { "download(#{remote.inspect}, #{local.inspect})" }
|
366
|
-
|
367
|
-
ZTK::RescueRetry.try(:tries => 3, :on => EOFError) do
|
368
|
-
@sftp = Net::SFTP.start(config.host_name, config.user, ssh_options)
|
369
|
-
sftp.download!(remote.to_s, local.to_s) do |event, downloader, *args|
|
370
|
-
case event
|
371
|
-
when :open
|
372
|
-
config.ui.logger.debug { "download(#{args[0].remote} -> #{args[0].local})" }
|
373
|
-
when :close
|
374
|
-
config.ui.logger.debug { "close(#{args[0].local})" }
|
375
|
-
when :mkdir
|
376
|
-
config.ui.logger.debug { "mkdir(#{args[0]})" }
|
377
|
-
when :get
|
378
|
-
config.ui.logger.debug { "get(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
|
379
|
-
when :finish
|
380
|
-
config.ui.logger.debug { "finish" }
|
381
|
-
end
|
382
|
-
end
|
383
|
-
end
|
384
|
-
|
385
|
-
true
|
386
|
-
end
|
387
|
-
|
388
199
|
|
389
200
|
private
|
390
201
|
|
391
|
-
# Builds our SSH console command.
|
392
|
-
def console_command
|
393
|
-
command = [ "/usr/bin/env ssh" ]
|
394
|
-
command << [ "-q" ]
|
395
|
-
command << [ "-4" ]
|
396
|
-
command << [ "-x" ]
|
397
|
-
command << [ "-a" ]
|
398
|
-
command << [ "-o", "UserKnownHostsFile=/dev/null" ]
|
399
|
-
command << [ "-o", "StrictHostKeyChecking=no" ]
|
400
|
-
command << [ "-o", "KeepAlive=yes" ]
|
401
|
-
command << [ "-o", "ServerAliveInterval=60" ]
|
402
|
-
command << [ "-o", %(ProxyCommand="#{proxy_command}") ] if config.proxy_host_name
|
403
|
-
command << [ "-i", config.keys ] if config.keys
|
404
|
-
command << [ "-p", config.port ] if config.port
|
405
|
-
# command << [ "-vv" ]
|
406
|
-
command << "#{config.user}@#{config.host_name}"
|
407
|
-
command = command.flatten.compact.join(' ')
|
408
|
-
config.ui.logger.debug { "console_command(#{command.inspect})" }
|
409
|
-
command
|
410
|
-
end
|
411
|
-
|
412
|
-
# Builds our SSH proxy command.
|
413
|
-
def proxy_command
|
414
|
-
!config.proxy_user and log_and_raise(SSHError, "You must specify an proxy user in order to SSH proxy.")
|
415
|
-
!config.proxy_host_name and log_and_raise(SSHError, "You must specify an proxy host_name in order to SSH proxy.")
|
416
|
-
|
417
|
-
command = ["/usr/bin/env ssh"]
|
418
|
-
command << [ "-q" ]
|
419
|
-
command << [ "-4" ]
|
420
|
-
command << [ "-x" ]
|
421
|
-
command << [ "-a" ]
|
422
|
-
command << [ "-o", "UserKnownHostsFile=/dev/null" ]
|
423
|
-
command << [ "-o", "StrictHostKeyChecking=no" ]
|
424
|
-
command << [ "-o", "KeepAlive=yes" ]
|
425
|
-
command << [ "-o", "ServerAliveInterval=60" ]
|
426
|
-
command << [ "-i", config.proxy_keys ] if config.proxy_keys
|
427
|
-
command << [ "-p", config.proxy_port ] if config.proxy_port
|
428
|
-
# command << [ "-vv" ]
|
429
|
-
command << "#{config.proxy_user}@#{config.proxy_host_name}"
|
430
|
-
command << "'/usr/bin/env nc %h %p'"
|
431
|
-
command = command.flatten.compact.join(' ')
|
432
|
-
config.ui.logger.debug { "proxy_command(#{command.inspect})" }
|
433
|
-
command
|
434
|
-
end
|
435
|
-
|
436
202
|
# Builds our SSH options hash.
|
437
203
|
def ssh_options
|
438
204
|
options = {}
|
data/lib/ztk/template.rb
CHANGED
@@ -57,6 +57,29 @@ module ZTK
|
|
57
57
|
render_template(load_template(template), context)
|
58
58
|
end
|
59
59
|
|
60
|
+
# Renders a "DO NOT EDIT" notice for placement in generated files.
|
61
|
+
#
|
62
|
+
# @param [Hash] options Options hash.
|
63
|
+
# @option options [String] :message An optional message to display in the notice.
|
64
|
+
# @option options [String] :char The comment character; defaults to '#'.
|
65
|
+
#
|
66
|
+
# @return [String] The rendered noticed.
|
67
|
+
def do_not_edit_notice(options={})
|
68
|
+
message = options[:message]
|
69
|
+
char = (options[:char] || '#')
|
70
|
+
notice = Array.new
|
71
|
+
|
72
|
+
notice << char
|
73
|
+
notice << "#{char} WARNING: AUTOMATICALLY GENERATED FILE; DO NOT EDIT!"
|
74
|
+
notice << char
|
75
|
+
notice << "#{char} #{message}" if !message.nil?
|
76
|
+
notice << char
|
77
|
+
notice << "#{char} Generated @ #{Time.now.utc}"
|
78
|
+
notice << char
|
79
|
+
|
80
|
+
notice.join("\n")
|
81
|
+
end
|
82
|
+
|
60
83
|
|
61
84
|
private
|
62
85
|
|
data/lib/ztk/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ztk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: erubis
|
@@ -200,6 +200,11 @@ files:
|
|
200
200
|
- lib/ztk/rescue_retry.rb
|
201
201
|
- lib/ztk/spinner.rb
|
202
202
|
- lib/ztk/ssh.rb
|
203
|
+
- lib/ztk/ssh/bootstrap.rb
|
204
|
+
- lib/ztk/ssh/command.rb
|
205
|
+
- lib/ztk/ssh/download.rb
|
206
|
+
- lib/ztk/ssh/exec.rb
|
207
|
+
- lib/ztk/ssh/upload.rb
|
203
208
|
- lib/ztk/tcp_socket_check.rb
|
204
209
|
- lib/ztk/template.rb
|
205
210
|
- lib/ztk/ui.rb
|
@@ -240,7 +245,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
240
245
|
version: '0'
|
241
246
|
segments:
|
242
247
|
- 0
|
243
|
-
hash:
|
248
|
+
hash: 1781664085234422223
|
244
249
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
245
250
|
none: false
|
246
251
|
requirements:
|
@@ -249,7 +254,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
249
254
|
version: '0'
|
250
255
|
segments:
|
251
256
|
- 0
|
252
|
-
hash:
|
257
|
+
hash: 1781664085234422223
|
253
258
|
requirements: []
|
254
259
|
rubyforge_project:
|
255
260
|
rubygems_version: 1.8.25
|