taste_tester 0.0.15 → 0.0.16
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/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: []
|