specinfra 2.25.1 → 2.26.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.
- checksums.yaml +4 -4
- data/lib/specinfra/backend/base.rb +31 -29
- data/lib/specinfra/backend/docker.rb +87 -85
- data/lib/specinfra/backend/dockerfile.rb +25 -24
- data/lib/specinfra/backend/exec.rb +58 -56
- data/lib/specinfra/backend/lxc.rb +34 -32
- data/lib/specinfra/backend/shell_script.rb +19 -17
- data/lib/specinfra/backend/ssh.rb +131 -129
- data/lib/specinfra/backend/telnet.rb +87 -85
- data/lib/specinfra/backend/winrm.rb +19 -17
- data/lib/specinfra/command/module/systemd.rb +44 -37
- data/lib/specinfra/command/module/zfs.rb +21 -14
- data/lib/specinfra/command/module.rb +6 -1
- data/lib/specinfra/command.rb +4 -1
- data/lib/specinfra/core.rb +18 -0
- data/lib/specinfra/ext/class.rb +9 -0
- data/lib/specinfra/ext/string.rb +14 -0
- data/lib/specinfra/ext.rb +2 -0
- data/lib/specinfra/helper/detect_os.rb +15 -13
- data/lib/specinfra/helper/os.rb +21 -16
- data/lib/specinfra/helper/set.rb +8 -3
- data/lib/specinfra/version.rb +1 -1
- data/lib/specinfra.rb +1 -38
- metadata +6 -2
@@ -3,171 +3,173 @@ require 'specinfra/backend/exec'
|
|
3
3
|
require 'net/ssh'
|
4
4
|
require 'net/scp'
|
5
5
|
|
6
|
-
module Specinfra
|
7
|
-
|
8
|
-
|
9
|
-
cmd =
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
module Specinfra
|
7
|
+
module Backend
|
8
|
+
class Ssh < Exec
|
9
|
+
def run_command(cmd, opt={})
|
10
|
+
cmd = build_command(cmd)
|
11
|
+
cmd = add_pre_command(cmd)
|
12
|
+
ret = with_env do
|
13
|
+
ssh_exec!(cmd)
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
ret[:stdout].gsub!(/\r\n/, "\n")
|
17
|
+
ret[:stdout].gsub!(/\A\n/, "") if sudo?
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
if @example
|
20
|
+
@example.metadata[:command] = cmd
|
21
|
+
@example.metadata[:stdout] = ret[:stdout]
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
CommandResult.new ret
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
def send_file(from, to)
|
28
|
+
scp_upload!(from, to)
|
29
|
+
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
def send_directory(from, to)
|
32
|
+
scp_upload!(from, to, :recursive => true)
|
33
|
+
end
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
def build_command(cmd)
|
36
|
+
cmd = super(cmd)
|
37
|
+
if sudo?
|
38
|
+
cmd = "#{sudo} -p '#{prompt}' #{cmd}"
|
39
|
+
end
|
40
|
+
cmd
|
38
41
|
end
|
39
|
-
cmd
|
40
|
-
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
private
|
44
|
+
def prompt
|
45
|
+
'Password: '
|
46
|
+
end
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
48
|
+
def with_env
|
49
|
+
env = get_config(:env) || {}
|
50
|
+
env[:LANG] ||= 'C'
|
50
51
|
|
51
|
-
|
52
|
-
|
52
|
+
ssh_options = get_config(:ssh_options) || {}
|
53
|
+
ssh_options[:send_env] ||= []
|
53
54
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
55
|
+
env.each do |key, value|
|
56
|
+
key = key.to_s
|
57
|
+
ENV["_SPECINFRA_#{key}"] = ENV[key];
|
58
|
+
ENV[key] = value
|
59
|
+
ssh_options[:send_env] << key
|
60
|
+
end
|
60
61
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
62
|
+
yield
|
63
|
+
ensure
|
64
|
+
env.each do |key, value|
|
65
|
+
key = key.to_s
|
66
|
+
ENV[key] = ENV.delete("_SPECINFRA_#{key}");
|
67
|
+
end
|
66
68
|
end
|
67
|
-
end
|
68
69
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
|
-
def create_scp
|
78
|
-
ssh = get_config(:ssh)
|
79
|
-
if ssh.nil?
|
80
|
-
ssh = create_ssh
|
70
|
+
def create_ssh
|
71
|
+
Net::SSH.start(
|
72
|
+
get_config(:host),
|
73
|
+
get_config(:ssh_options)[:user],
|
74
|
+
get_config(:ssh_options)
|
75
|
+
)
|
81
76
|
end
|
82
|
-
Net::SCP.new(ssh)
|
83
|
-
end
|
84
77
|
|
85
|
-
|
86
|
-
|
87
|
-
|
78
|
+
def create_scp
|
79
|
+
ssh = get_config(:ssh)
|
80
|
+
if ssh.nil?
|
81
|
+
ssh = create_ssh
|
82
|
+
end
|
83
|
+
Net::SCP.new(ssh)
|
88
84
|
end
|
89
85
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
|
-
def ssh_exec!(command)
|
97
|
-
stdout_data = ''
|
98
|
-
stderr_data = ''
|
99
|
-
exit_status = nil
|
100
|
-
exit_signal = nil
|
101
|
-
retry_prompt = /^Sorry, try again/
|
86
|
+
def scp_upload!(from, to, opt={})
|
87
|
+
if get_config(:scp).nil?
|
88
|
+
set_config(:scp, create_scp)
|
89
|
+
end
|
102
90
|
|
103
|
-
|
104
|
-
|
91
|
+
tmp = File.join('/tmp', File.basename(to))
|
92
|
+
scp = get_config(:scp)
|
93
|
+
scp.upload!(from, tmp, opt)
|
94
|
+
run_command(command.get(:move_file, tmp, to))
|
105
95
|
end
|
106
96
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
97
|
+
def ssh_exec!(command)
|
98
|
+
stdout_data = ''
|
99
|
+
stderr_data = ''
|
100
|
+
exit_status = nil
|
101
|
+
exit_signal = nil
|
102
|
+
retry_prompt = /^Sorry, try again/
|
103
|
+
|
104
|
+
if get_config(:ssh).nil?
|
105
|
+
set_config(:ssh, create_ssh)
|
113
106
|
end
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
channel.send_data "#{get_config(:sudo_password)}\n"
|
121
|
-
else
|
122
|
-
stdout_data += data
|
107
|
+
|
108
|
+
ssh = get_config(:ssh)
|
109
|
+
ssh.open_channel do |channel|
|
110
|
+
if get_config(:sudo_password) or get_config(:request_pty)
|
111
|
+
channel.request_pty do |ch, success|
|
112
|
+
abort "Could not obtain pty " if !success
|
123
113
|
end
|
124
114
|
end
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
115
|
+
channel.exec("#{command}") do |ch, success|
|
116
|
+
abort "FAILED: couldn't execute command (ssh.channel.exec)" if !success
|
117
|
+
channel.on_data do |ch, data|
|
118
|
+
if data.match retry_prompt
|
119
|
+
abort 'Wrong sudo password! Please confirm your password.'
|
120
|
+
elsif data.match /^#{prompt}/
|
121
|
+
channel.send_data "#{get_config(:sudo_password)}\n"
|
122
|
+
else
|
123
|
+
stdout_data += data
|
124
|
+
end
|
129
125
|
end
|
130
126
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
127
|
+
channel.on_extended_data do |ch, type, data|
|
128
|
+
if data.match /you must have a tty to run sudo/
|
129
|
+
abort 'Please write "set :request_pty, true" in your spec_helper.rb or other appropriate file.'
|
130
|
+
end
|
131
|
+
|
132
|
+
if data.match /^sudo: no tty present and no askpass program specified/
|
133
|
+
abort 'Please set sudo password to Specinfra.configuration.sudo_password.'
|
134
|
+
else
|
135
|
+
stderr_data += data
|
136
|
+
end
|
135
137
|
end
|
136
|
-
end
|
137
138
|
|
138
|
-
|
139
|
-
|
140
|
-
|
139
|
+
channel.on_request("exit-status") do |ch, data|
|
140
|
+
exit_status = data.read_long
|
141
|
+
end
|
141
142
|
|
142
|
-
|
143
|
-
|
143
|
+
channel.on_request("exit-signal") do |ch, data|
|
144
|
+
exit_signal = data.read_long
|
145
|
+
end
|
144
146
|
end
|
145
147
|
end
|
148
|
+
ssh.loop
|
149
|
+
{ :stdout => stdout_data, :stderr => stderr_data, :exit_status => exit_status, :exit_signal => exit_signal }
|
146
150
|
end
|
147
|
-
ssh.loop
|
148
|
-
{ :stdout => stdout_data, :stderr => stderr_data, :exit_status => exit_status, :exit_signal => exit_signal }
|
149
|
-
end
|
150
151
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
152
|
+
def sudo
|
153
|
+
if sudo_path = get_config(:sudo_path)
|
154
|
+
sudo_path += '/sudo'
|
155
|
+
else
|
156
|
+
sudo_path = 'sudo'
|
157
|
+
end
|
157
158
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
159
|
+
sudo_options = get_config(:sudo_options)
|
160
|
+
if sudo_options
|
161
|
+
sudo_options = sudo_options.shelljoin if sudo_options.is_a?(Array)
|
162
|
+
sudo_options = ' ' + sudo_options
|
163
|
+
end
|
163
164
|
|
164
|
-
|
165
|
-
|
165
|
+
"#{sudo_path.shellescape}#{sudo_options}"
|
166
|
+
end
|
166
167
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
168
|
+
def sudo?
|
169
|
+
user = get_config(:ssh_options)[:user]
|
170
|
+
disable_sudo = get_config(:disable_sudo)
|
171
|
+
user != 'root' && !disable_sudo
|
172
|
+
end
|
171
173
|
end
|
172
174
|
end
|
173
175
|
end
|
@@ -2,108 +2,110 @@
|
|
2
2
|
require 'specinfra/backend/exec'
|
3
3
|
require 'net/telnet'
|
4
4
|
|
5
|
-
module Specinfra
|
6
|
-
|
7
|
-
|
8
|
-
cmd =
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
@example
|
15
|
-
|
16
|
-
|
5
|
+
module Specinfra
|
6
|
+
module Backend
|
7
|
+
class Telnet < Exec
|
8
|
+
def run_command(cmd, opt={})
|
9
|
+
cmd = build_command(cmd)
|
10
|
+
cmd = add_pre_command(cmd)
|
11
|
+
ret = with_env do
|
12
|
+
telnet_exec!(cmd)
|
13
|
+
end
|
14
|
+
if @example
|
15
|
+
@example.metadata[:command] = cmd
|
16
|
+
@example.metadata[:stdout] = ret[:stdout]
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
19
|
+
CommandResult.new ret
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
def build_command(cmd)
|
23
|
+
cmd = super(cmd)
|
24
|
+
cmd
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
private
|
28
|
+
def prompt
|
29
|
+
'Login: '
|
30
|
+
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
def with_env
|
33
|
+
env = get_config(:env) || {}
|
34
|
+
env[:LANG] ||= 'C'
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
env.each do |key, value|
|
37
|
+
key = key.to_s
|
38
|
+
ENV["_SPECINFRA_#{key}"] = ENV[key];
|
39
|
+
ENV[key] = value
|
40
|
+
end
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
42
|
+
yield
|
43
|
+
ensure
|
44
|
+
env.each do |key, value|
|
45
|
+
key = key.to_s
|
46
|
+
ENV[key] = ENV.delete("_SPECINFRA_#{key}");
|
47
|
+
end
|
46
48
|
end
|
47
|
-
end
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
50
|
+
def add_pre_command(cmd)
|
51
|
+
if get_config(:pre_command)
|
52
|
+
pre_cmd = build_command(get_config(:pre_command))
|
53
|
+
"#{pre_cmd} && #{cmd}"
|
54
|
+
else
|
55
|
+
cmd
|
56
|
+
end
|
55
57
|
end
|
56
|
-
end
|
57
58
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
59
|
+
def telnet_exec!( command )
|
60
|
+
stdout_data = ''
|
61
|
+
stderr_data = ''
|
62
|
+
exit_status = nil
|
63
|
+
exit_signal = nil
|
64
|
+
retry_prompt = /^Login: /
|
65
|
+
if get_config(:telnet).nil?
|
66
|
+
set_config(:telnet, create_telnet)
|
67
|
+
end
|
68
|
+
telnet = get_config(:telnet)
|
69
|
+
re = []
|
70
|
+
unless telnet.nil?
|
71
|
+
re = telnet.cmd( "#{command}; echo $?" ).split("\n")[0..-2]
|
72
|
+
exit_status = re.last.to_i
|
73
|
+
stdout_data = re[1..-2].join("\n")
|
74
|
+
end
|
75
|
+
{ :stdout => stdout_data, :stderr => stderr_data, :exit_status => exit_status, :exit_signal => exit_signal }
|
66
76
|
end
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
77
|
+
|
78
|
+
def create_telnet
|
79
|
+
tel = Net::Telnet.new( "Host" => get_config(:host) )
|
80
|
+
tel.login(
|
81
|
+
"Name" => get_config(:telnet_options)[:user],
|
82
|
+
"Password" => get_config(:telnet_options)[:pass]
|
83
|
+
)
|
84
|
+
tel
|
85
|
+
rescue
|
86
|
+
return nil
|
73
87
|
end
|
74
|
-
{ :stdout => stdout_data, :stderr => stderr_data, :exit_status => exit_status, :exit_signal => exit_signal }
|
75
|
-
end
|
76
|
-
|
77
|
-
def create_telnet
|
78
|
-
tel = Net::Telnet.new( "Host" => get_config(:host) )
|
79
|
-
tel.login(
|
80
|
-
"Name" => get_config(:telnet_options)[:user],
|
81
|
-
"Password" => get_config(:telnet_options)[:pass]
|
82
|
-
)
|
83
|
-
tel
|
84
|
-
rescue
|
85
|
-
return nil
|
86
|
-
end
|
87
88
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
89
|
+
def sudo
|
90
|
+
if sudo_path = get_config(:sudo_path)
|
91
|
+
sudo_path += '/sudo'
|
92
|
+
else
|
93
|
+
sudo_path = 'sudo'
|
94
|
+
end
|
95
|
+
|
96
|
+
sudo_options = get_config(:sudo_options)
|
97
|
+
if sudo_options
|
98
|
+
sudo_options = sudo_options.shelljoin if sudo_options.is_a?(Array)
|
99
|
+
sudo_options = ' ' + sudo_options
|
100
|
+
end
|
94
101
|
|
95
|
-
|
96
|
-
if sudo_options
|
97
|
-
sudo_options = sudo_options.shelljoin if sudo_options.is_a?(Array)
|
98
|
-
sudo_options = ' ' + sudo_options
|
102
|
+
"#{sudo_path.shellescape}#{sudo_options}"
|
99
103
|
end
|
100
104
|
|
101
|
-
|
102
|
-
|
105
|
+
def sudo?
|
106
|
+
false
|
107
|
+
end
|
103
108
|
|
104
|
-
def sudo?
|
105
|
-
false
|
106
109
|
end
|
107
|
-
|
108
110
|
end
|
109
111
|
end
|
@@ -1,24 +1,26 @@
|
|
1
|
-
module Specinfra
|
2
|
-
|
3
|
-
|
1
|
+
module Specinfra
|
2
|
+
module Backend
|
3
|
+
class Winrm < Base
|
4
|
+
include PowerShell::ScriptHelper
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
def run_command(cmd, opts={})
|
7
|
+
set_config(:os, {:family => 'windows'})
|
8
|
+
script = create_script(cmd)
|
9
|
+
winrm = get_config(:winrm)
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
result = winrm.powershell(script)
|
12
|
+
stdout, stderr = [:stdout, :stderr].map do |s|
|
13
|
+
result[:data].select {|item| item.key? s}.map {|item| item[s]}.join
|
14
|
+
end
|
15
|
+
result[:exitcode] = 1 if result[:exitcode] == 0 and !stderr.empty?
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
if @example
|
18
|
+
@example.metadata[:command] = script
|
19
|
+
@example.metadata[:stdout] = stdout + stderr
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
+
CommandResult.new :stdout => stdout, :stderr => stderr, :exit_status => result[:exitcode]
|
23
|
+
end
|
22
24
|
end
|
23
25
|
end
|
24
26
|
end
|
@@ -1,40 +1,47 @@
|
|
1
|
-
module Specinfra
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module Specinfra
|
2
|
+
module Command
|
3
|
+
module Module
|
4
|
+
module Systemd
|
5
|
+
def check_is_enabled(service, level="multi-user.target")
|
6
|
+
if level.to_s =~ /^\d+$/
|
7
|
+
level = "runlevel#{level}.target"
|
8
|
+
end
|
9
|
+
unless service.include?('.')
|
10
|
+
service += '.service'
|
11
|
+
end
|
12
|
+
|
13
|
+
"systemctl --plain list-dependencies #{level} | grep '\\(^\\| \\)#{escape(service)}$'"
|
14
|
+
end
|
15
|
+
|
16
|
+
def check_is_running(service)
|
17
|
+
"systemctl is-active #{escape(service)}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def enable(service)
|
21
|
+
"systemctl enable #{escape(service)}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def disable(service)
|
25
|
+
"systemctl disable #{escape(service)}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def start(service)
|
29
|
+
"systemctl start #{escape(service)}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def stop(service)
|
33
|
+
"systemctl stop #{escape(service)}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def restart(service)
|
37
|
+
"systemctl restart #{escape(service)}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def reload(service)
|
41
|
+
"systemctl reload #{escape(service)}"
|
42
|
+
end
|
43
|
+
end
|
5
44
|
end
|
6
|
-
unless service.include?('.')
|
7
|
-
service += '.service'
|
8
|
-
end
|
9
|
-
|
10
|
-
"systemctl --plain list-dependencies #{level} | grep '\\(^\\| \\)#{escape(service)}$'"
|
11
|
-
end
|
12
|
-
|
13
|
-
def check_is_running(service)
|
14
|
-
"systemctl is-active #{escape(service)}"
|
15
|
-
end
|
16
|
-
|
17
|
-
def enable(service)
|
18
|
-
"systemctl enable #{escape(service)}"
|
19
|
-
end
|
20
|
-
|
21
|
-
def disable(service)
|
22
|
-
"systemctl disable #{escape(service)}"
|
23
|
-
end
|
24
|
-
|
25
|
-
def start(service)
|
26
|
-
"systemctl start #{escape(service)}"
|
27
|
-
end
|
28
|
-
|
29
|
-
def stop(service)
|
30
|
-
"systemctl stop #{escape(service)}"
|
31
|
-
end
|
32
|
-
|
33
|
-
def restart(service)
|
34
|
-
"systemctl restart #{escape(service)}"
|
35
|
-
end
|
36
|
-
|
37
|
-
def reload(service)
|
38
|
-
"systemctl reload #{escape(service)}"
|
39
45
|
end
|
40
46
|
end
|
47
|
+
|
@@ -1,18 +1,25 @@
|
|
1
|
-
module Specinfra
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module Specinfra
|
2
|
+
module Command
|
3
|
+
module Module
|
4
|
+
module Zfs
|
5
|
+
def check_exists(zfs)
|
6
|
+
"zfs list -H #{escape(zfs)}"
|
7
|
+
end
|
5
8
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
def check_has_property(zfs, property=nil)
|
10
|
+
commands = []
|
11
|
+
property.sort.each do |key, value|
|
12
|
+
regexp = "^#{value}$"
|
13
|
+
commands << "zfs list -H -o #{escape(key)} #{escape(zfs)} | grep -- #{escape(regexp)}"
|
14
|
+
end
|
15
|
+
commands.join(' && ')
|
16
|
+
end
|
14
17
|
|
15
|
-
|
16
|
-
|
18
|
+
def get_property(zfs)
|
19
|
+
"zfs get -Hp -o property,value all #{escape(zfs)}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
17
23
|
end
|
18
24
|
end
|
25
|
+
|