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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ba0813aa4a09d92549bb1555bef69e883a49b4ef292b9a2d7cc14dca77bce0e
4
- data.tar.gz: 4a51383665bad129bba1e5e4d2e1d9df991eb021700ce6dc9ab1098fa8089ad5
3
+ metadata.gz: 945fe28beeb0251fc2b6236c833e77b8b0f6e7b2242c9f7d4882bd4fcf270506
4
+ data.tar.gz: 1001f712736d466b1fe1db53332d7668ebebf028d62a8566c2923ee88db2f0d4
5
5
  SHA512:
6
- metadata.gz: 612005359c48c5cee20cb95e4c8880a087f1638887de099e0cd15dd99757b54a3e404643241289286c7d33ff9f29ed25dcd64d9bcefcefc2a9b36dfe97fbb6f0
7
- data.tar.gz: 99fae7d5975e0a9c05d1ca0e68c18c03a237eb4e4b17c8e870b16d71e8a434717a01d81dc0aad2b15b35ee1fbc292c3dad230c29ab92f362059f2d5285808de7
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
- # Run chef and watch it break
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
- # Run chef and watch it succeed
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 changes, e.g. instead of chef-client you may
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
@@ -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
@@ -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'
@@ -16,7 +16,6 @@
16
16
 
17
17
  require 'fileutils'
18
18
  require 'socket'
19
- require 'timeout'
20
19
 
21
20
  require 'between_meals/util'
22
21
  require 'taste_tester/config'
@@ -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 = <<-ERRORMESSAGE
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
- #{TasteTester::Config.ssh_command} -v #{TasteTester::Config.user}@#{@host}
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
- error.lines.each { |x| logger.error x.strip }
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
- jumps = TasteTester::Config.jumps ? "-J #{TasteTester::Config.jumps}" : ''
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
- cc = Base64.encode64(cmds).delete("\n")
76
- cmd += "\"echo '#{cc}' | base64 --decode | sudo bash -x\""
82
+ cmd += ' | sudo bash -x"'
77
83
  else
78
- cmd += "\'#{cmds}\'"
84
+ cmd += ' | bash -x"'
79
85
  end
80
86
  cmd
81
87
  end
@@ -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
- cmds = "ps -o pgid= -p $(ps -o ppid= -p #{pid}) | sed \"s| ||g\" " +
50
- " > #{TasteTester::Config.timestamp_file} &&" +
51
- " touch -t #{@ts} #{TasteTester::Config.timestamp_file} &&" +
52
- " sleep #{@delta_secs}"
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
- "-o ServerAliveInterval=10 -o ServerAliveCountMax=#{@max_ping} " +
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
- cc = Base64.encode64(cmds).delete("\n")
68
- cmd += "#{TasteTester::Config.user}@#{@host} \"echo '#{cc}' | base64" +
69
- ' --decode | sudo bash -x"'
152
+ cmd += ' | sudo bash -x"'
70
153
  else
71
- cmd += "root@#{@host} '#{cmds}'"
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
- " && #{sudo}kill -9 -- " +
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.15
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: 2019-09-19 00:00:00.000000000 Z
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.6
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.6
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: []