taste_tester 0.0.15 → 0.0.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/taste-tester +19 -4
- data/lib/taste_tester/commands.rb +13 -0
- data/lib/taste_tester/config.rb +1 -0
- data/lib/taste_tester/host.rb +18 -0
- data/lib/taste_tester/server.rb +0 -1
- data/lib/taste_tester/ssh.rb +19 -13
- data/lib/taste_tester/tunnel.rb +101 -21
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 945fe28beeb0251fc2b6236c833e77b8b0f6e7b2242c9f7d4882bd4fcf270506
|
4
|
+
data.tar.gz: 1001f712736d466b1fe1db53332d7668ebebf028d62a8566c2923ee88db2f0d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ccf12a4962ae584ff10f2b0b7782a48d2e9e04f320a0b592895b3ec545a2921372ccd4e5e9b8994435791fbe281fe9d9043881b2f780f6aadd60ec64613e910f
|
7
|
+
data.tar.gz: 5b8f422febc1b1f8d2bee6a408aaff95a1dbd41011ad267c4637180a53214219fc44d725543160866ea8947bacd5eec68e002d6afc87352f7fa721fed05157ea
|
data/bin/taste-tester
CHANGED
@@ -39,6 +39,15 @@ module TasteTester
|
|
39
39
|
exit(1)
|
40
40
|
end
|
41
41
|
|
42
|
+
# Do an initial read of the config file if it's in the default place, so
|
43
|
+
# that if people override chef_client_command the help message is correct.
|
44
|
+
if File.exists?(File.expand_path(TasteTester::Config.config_file))
|
45
|
+
TasteTester::Config.from_file(
|
46
|
+
File.expand_path(TasteTester::Config.config_file),
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
cmd = TasteTester::Config.chef_client_command
|
42
51
|
description = <<-ENDOFDESCRIPTION
|
43
52
|
Welcome to taste-tester!
|
44
53
|
|
@@ -48,18 +57,17 @@ TLDR; Most common usage is:
|
|
48
57
|
vi cookbooks/... # Make your changes and commit locally
|
49
58
|
taste-tester test -s [host] # Put host in test mode
|
50
59
|
ssh root@[host] # Log on host
|
51
|
-
|
60
|
+
#{format('%-28s', " #{cmd}")} # Run chef and watch it break
|
52
61
|
vi cookbooks/... # Fix your cookbooks
|
53
62
|
taste-tester upload # Upload the diff
|
54
63
|
ssh root@[host] # Log on host
|
55
|
-
|
64
|
+
#{format('%-28s', " #{cmd}")} # Run chef and watch it succeed
|
56
65
|
<#{verify}>
|
57
66
|
taste-tester untest -s [host] # Put host back in production
|
58
67
|
# (optional - will revert itself after 1 hour)
|
59
68
|
|
60
69
|
And you're done!
|
61
|
-
Note: There may be site specific
|
62
|
-
use a wrapper. See local documentation for details.
|
70
|
+
Note: There may be site specific testing instructions, see local documentation for details.
|
63
71
|
|
64
72
|
MODES:
|
65
73
|
test
|
@@ -100,6 +108,11 @@ MODES:
|
|
100
108
|
status
|
101
109
|
Print out the state of the world.
|
102
110
|
|
111
|
+
run
|
112
|
+
Run #{cmd} on the machine specified by '-s' over SSH and print the output.
|
113
|
+
NOTE!! This is #{'NOT'.red} a sufficient test, you must log onto the remote
|
114
|
+
machine and verify the changes you are trying to make are actually present.
|
115
|
+
|
103
116
|
stop
|
104
117
|
You probably don't want this. It will shutdown the chef-zero server on
|
105
118
|
your localhost.
|
@@ -429,6 +442,8 @@ MODES:
|
|
429
442
|
TasteTester::Commands.test
|
430
443
|
when :untest
|
431
444
|
TasteTester::Commands.untest
|
445
|
+
when :run
|
446
|
+
TasteTester::Commands.runchef
|
432
447
|
when :upload
|
433
448
|
TasteTester::Commands.upload
|
434
449
|
when :impact
|
@@ -149,6 +149,19 @@ module TasteTester
|
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
152
|
+
def self.runchef
|
153
|
+
hosts = TasteTester::Config.servers
|
154
|
+
unless hosts
|
155
|
+
logger.warn('You must provide a hostname')
|
156
|
+
exit(1)
|
157
|
+
end
|
158
|
+
server = TasteTester::Server.new
|
159
|
+
hosts.each do |hostname|
|
160
|
+
host = TasteTester::Host.new(hostname, server)
|
161
|
+
host.runchef
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
152
165
|
def self.keeptesting
|
153
166
|
hosts = TasteTester::Config.servers
|
154
167
|
unless hosts
|
data/lib/taste_tester/config.rb
CHANGED
@@ -45,6 +45,7 @@ module TasteTester
|
|
45
45
|
knife_config "#{ENV['HOME']}/.chef/knife-#{ENV['USER']}-taste-tester.rb"
|
46
46
|
checksum_dir "#{ENV['HOME']}/.chef/checksums"
|
47
47
|
skip_repo_checks false
|
48
|
+
chef_client_command 'chef-client'
|
48
49
|
testing_time 3600
|
49
50
|
chef_port_range [5000, 5500]
|
50
51
|
tunnel_port 4001
|
data/lib/taste_tester/host.rb
CHANGED
@@ -44,6 +44,24 @@ module TasteTester
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
def runchef
|
48
|
+
logger.warn("Running '#{TasteTester::Config.chef_client_command}' " +
|
49
|
+
"on #{@name}")
|
50
|
+
transport = get_transport
|
51
|
+
transport << TasteTester::Config.chef_client_command
|
52
|
+
|
53
|
+
io = IO.new(1)
|
54
|
+
status, = transport.run(io)
|
55
|
+
logger.warn("Finished #{TasteTester::Config.chef_client_command}" +
|
56
|
+
" on #{@name} with status #{status}")
|
57
|
+
if status.zero?
|
58
|
+
msg = "#{TasteTester::Config.chef_client_command} was successful" +
|
59
|
+
' - please log to the host and confirm all the intended' +
|
60
|
+
' changes were made'
|
61
|
+
logger.error msg.upcase
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
47
65
|
def get_transport
|
48
66
|
case TasteTester::Config.transport
|
49
67
|
when 'locallink'
|
data/lib/taste_tester/server.rb
CHANGED
data/lib/taste_tester/ssh.rb
CHANGED
@@ -34,48 +34,54 @@ module TasteTester
|
|
34
34
|
|
35
35
|
alias << add
|
36
36
|
|
37
|
-
def run
|
38
|
-
exec(cmd, logger)
|
37
|
+
def run(stream = nil)
|
38
|
+
exec(cmd, logger, stream)
|
39
39
|
end
|
40
40
|
|
41
|
-
def run!
|
42
|
-
exec!(cmd, logger)
|
41
|
+
def run!(stream = nil)
|
42
|
+
exec!(cmd, logger, stream)
|
43
43
|
rescue StandardError => e
|
44
44
|
logger.error(e.message)
|
45
45
|
error!
|
46
46
|
end
|
47
47
|
|
48
48
|
def error!
|
49
|
-
error =
|
49
|
+
error = <<~ERRORMESSAGE
|
50
50
|
SSH returned error while connecting to #{TasteTester::Config.user}@#{@host}
|
51
51
|
The host might be broken or your SSH access is not working properly
|
52
52
|
Try doing
|
53
|
-
|
53
|
+
|
54
|
+
#{ssh_base_cmd} -v #{TasteTester::Config.user}@#{@host}
|
55
|
+
|
54
56
|
to see if ssh connection is good.
|
55
57
|
If ssh works, add '-v' key to taste-tester to see the list of commands it's
|
56
58
|
trying to execute, and try to run them manually on destination host
|
57
59
|
ERRORMESSAGE
|
58
|
-
|
60
|
+
logger.error(error)
|
59
61
|
fail TasteTester::Exceptions::SshError
|
60
62
|
end
|
61
63
|
|
62
64
|
private
|
63
65
|
|
66
|
+
def ssh_base_cmd
|
67
|
+
jumps = TasteTester::Config.jumps ? "-J #{TasteTester::Config.jumps}" : ''
|
68
|
+
"#{TasteTester::Config.ssh_command} #{jumps}"
|
69
|
+
end
|
70
|
+
|
64
71
|
def cmd
|
65
72
|
@cmds.each do |cmd|
|
66
73
|
logger.info("Will run: '#{cmd}' on #{@host}")
|
67
74
|
end
|
68
75
|
cmds = @cmds.join(' && ')
|
69
|
-
|
70
|
-
cmd = "#{TasteTester::Config.ssh_command} #{jumps} " +
|
71
|
-
'-T -o BatchMode=yes ' +
|
76
|
+
cmd = "#{ssh_base_cmd} -T -o BatchMode=yes " +
|
72
77
|
"-o ConnectTimeout=#{TasteTester::Config.ssh_connect_timeout} " +
|
73
78
|
"#{TasteTester::Config.user}@#{@host} "
|
79
|
+
cc = Base64.encode64(cmds).delete("\n")
|
80
|
+
cmd += "\"echo '#{cc}' | base64 --decode"
|
74
81
|
if TasteTester::Config.user != 'root'
|
75
|
-
|
76
|
-
cmd += "\"echo '#{cc}' | base64 --decode | sudo bash -x\""
|
82
|
+
cmd += ' | sudo bash -x"'
|
77
83
|
else
|
78
|
-
cmd += "
|
84
|
+
cmd += ' | bash -x"'
|
79
85
|
end
|
80
86
|
cmd
|
81
87
|
end
|
data/lib/taste_tester/tunnel.rb
CHANGED
@@ -25,12 +25,6 @@ module TasteTester
|
|
25
25
|
def initialize(host, server)
|
26
26
|
@host = host
|
27
27
|
@server = server
|
28
|
-
if TasteTester::Config.testing_until
|
29
|
-
@delta_secs = TasteTester::Config.testing_until.strftime('%s').to_i -
|
30
|
-
Time.now.strftime('%s').to_i
|
31
|
-
else
|
32
|
-
@delta_secs = TasteTester::Config.testing_time
|
33
|
-
end
|
34
28
|
end
|
35
29
|
|
36
30
|
def run
|
@@ -43,13 +37,99 @@ module TasteTester
|
|
43
37
|
end
|
44
38
|
|
45
39
|
def cmd
|
46
|
-
@max_ping = @delta_secs / 10
|
47
|
-
pid = '$$'
|
48
40
|
@ts = TasteTester::Config.testing_end_time.strftime('%y%m%d%H%M.%S')
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
41
|
+
|
42
|
+
# Tie the life of our SSH tunnel with the life of timestamp file.
|
43
|
+
# taste-testing can be renewed, so we'll wait until:
|
44
|
+
# 1. the timestamp file is entirely gone
|
45
|
+
# 2. our parent sshd process dies
|
46
|
+
# 3. new taste-tester instance is running (file contains different PGID)
|
47
|
+
cmds = <<~EOS
|
48
|
+
log() {
|
49
|
+
[ -e /usr/bin/logger ] || return
|
50
|
+
logger -t taste-tester "$*"
|
51
|
+
}
|
52
|
+
# sets $current_pgid
|
53
|
+
# This is important, this should just be called ald let it set the
|
54
|
+
# variable. Do NOT call in a subshell like foo=$(get_current_pgid)
|
55
|
+
# as then you end up even further down the list of children
|
56
|
+
get_current_pgid() {
|
57
|
+
|
58
|
+
# if TT user is non-root, then it breaks down like this:
|
59
|
+
# we are 'bash'
|
60
|
+
# our parent is 'sudo'
|
61
|
+
# our parent's parent is 'bash "echo ..." | sudo bash -x'
|
62
|
+
# our parent's parent's parent is ssh
|
63
|
+
# - we want the progress-group ID of *that*
|
64
|
+
#
|
65
|
+
# EXCEPT... sometimes sudo forks itself one more time so it's
|
66
|
+
# we are 'bash'
|
67
|
+
# our parent is 'sudo'
|
68
|
+
# our parent's parent 'sudo'
|
69
|
+
# our parent's parent's parent is 'bash "echo ..." | sudo bash -x'
|
70
|
+
# our parent's parent's parent's parent is ssh
|
71
|
+
# - we want the progress-group ID of *that*
|
72
|
+
#
|
73
|
+
# BUT if the TT user is root, no sudo at all...
|
74
|
+
# we are 'bash'
|
75
|
+
# our parent is 'bash "echo ..." | bash -c
|
76
|
+
# our parent's parent is ssh
|
77
|
+
# - we want the progress-group ID of *that*
|
78
|
+
#
|
79
|
+
# We can make all sorts of assumptions, but the most reliable way
|
80
|
+
# to do this that's always correct is to is simply to walk parents until
|
81
|
+
# we hit something with SSH in the name. Start with PPID and go from
|
82
|
+
# there.
|
83
|
+
#
|
84
|
+
# There's a few commented out 'log's here that are too verbose
|
85
|
+
# for operation (since this function runs every minute) but are useful
|
86
|
+
# for debugging.
|
87
|
+
|
88
|
+
relevant_pid=''
|
89
|
+
current_pid=$PPID
|
90
|
+
while true; do
|
91
|
+
name=$(ps -o command= -p $current_pid)
|
92
|
+
if [[ "$name" =~ sshd ]]; then
|
93
|
+
# Uncomment the following for debugging...
|
94
|
+
#log "$current_pid is ssh, that's us!"
|
95
|
+
relevant_pid=$current_pid
|
96
|
+
break
|
97
|
+
fi
|
98
|
+
# Uncomment the following for debugging...
|
99
|
+
#log "$current_pid is $name, finding parent..."
|
100
|
+
current_pid=$(ps -o ppid= -p $current_pid)
|
101
|
+
done
|
102
|
+
if [ -z "$relevant_pid" ];then
|
103
|
+
log "Cannot determine relevant PGID"
|
104
|
+
exit 42
|
105
|
+
fi
|
106
|
+
current_pgid="$(ps -o pgid= -p $relevant_pid | sed "s| ||g")"
|
107
|
+
# Uncomment the following for debugging...
|
108
|
+
#log "PGID of ssh ($relevant_pid) is $current_pgid"
|
109
|
+
}
|
110
|
+
get_current_pgid
|
111
|
+
SSH_PGID=$current_pgid
|
112
|
+
|
113
|
+
echo $SSH_PGID > #{TasteTester::Config.timestamp_file} && \
|
114
|
+
touch -t #{@ts} #{TasteTester::Config.timestamp_file} && \
|
115
|
+
while true; do
|
116
|
+
if ! [ -f "#{TasteTester::Config.timestamp_file}" ]; then
|
117
|
+
log "Ending tunnel: timestamp file disappeared"
|
118
|
+
break
|
119
|
+
fi
|
120
|
+
current_pid="$(cat #{TasteTester::Config.timestamp_file})"
|
121
|
+
if ! [ "$current_pid" = "$SSH_PGID" ]; then
|
122
|
+
log "Ending tunnel: timestamp PGID changed"
|
123
|
+
break
|
124
|
+
fi
|
125
|
+
get_current_pgid
|
126
|
+
if ! [ "$current_pgid" = "$SSH_PGID" ]; then
|
127
|
+
log "Ending tunnel: timestamp PGID isn't ours"
|
128
|
+
break
|
129
|
+
fi
|
130
|
+
sleep 60
|
131
|
+
done
|
132
|
+
EOS
|
53
133
|
# As great as it would be to have ExitOnForwardFailure=yes,
|
54
134
|
# we had multiple cases of tunnels dying
|
55
135
|
# if -f and ExitOnForwardFailure are used together.
|
@@ -61,14 +141,17 @@ module TasteTester
|
|
61
141
|
"-o ConnectTimeout=#{TasteTester::Config.ssh_connect_timeout} " +
|
62
142
|
'-T -o BatchMode=yes ' +
|
63
143
|
'-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no ' +
|
64
|
-
|
144
|
+
'-o ServerAliveInterval=10 -o ServerAliveCountMax=6 ' +
|
65
145
|
"-f -R #{@port}:localhost:#{@server.port} "
|
146
|
+
cc = Base64.encode64(cmds).delete("\n")
|
147
|
+
|
148
|
+
# always base64 encode the command so we don't have to deal with quoting
|
149
|
+
cmd += "#{TasteTester::Config.user}@#{@host} \"echo '#{cc}' | base64" +
|
150
|
+
' --decode'
|
66
151
|
if TasteTester::Config.user != 'root'
|
67
|
-
|
68
|
-
cmd += "#{TasteTester::Config.user}@#{@host} \"echo '#{cc}' | base64" +
|
69
|
-
' --decode | sudo bash -x"'
|
152
|
+
cmd += ' | sudo bash -x"'
|
70
153
|
else
|
71
|
-
cmd += "
|
154
|
+
cmd += ' | bash -x"'
|
72
155
|
end
|
73
156
|
cmd
|
74
157
|
end
|
@@ -79,11 +162,8 @@ module TasteTester
|
|
79
162
|
# surround this in paryns, and make sure as a whole it evaluates
|
80
163
|
# to true so it doesn't mess up other things... even though this is
|
81
164
|
# the only thing we're currently executing in this SSH.
|
82
|
-
if TasteTester::Config.user != 'root'
|
83
|
-
sudo = 'sudo '
|
84
|
-
end
|
85
165
|
cmd = "( [ -s #{TasteTester::Config.timestamp_file} ]" +
|
86
|
-
|
166
|
+
' && kill -9 -- ' +
|
87
167
|
"-\$(cat #{TasteTester::Config.timestamp_file}) 2>/dev/null; " +
|
88
168
|
' true )'
|
89
169
|
ssh << cmd
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: taste_tester
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Phil Dibowitz
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2020-05-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: between_meals
|
@@ -17,14 +17,14 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: 0.0.
|
20
|
+
version: 0.0.10
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: 0.0.
|
27
|
+
version: 0.0.10
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: json
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -166,7 +166,7 @@ files:
|
|
166
166
|
- scripts/taste-untester
|
167
167
|
homepage: https://github.com/facebook/taste-tester
|
168
168
|
licenses:
|
169
|
-
- Apache
|
169
|
+
- Apache-2.0
|
170
170
|
metadata: {}
|
171
171
|
post_install_message:
|
172
172
|
rdoc_options: []
|