taste_tester 0.0.13 → 0.0.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +33 -7
- data/bin/taste-tester +50 -11
- data/lib/taste_tester/client.rb +117 -17
- data/lib/taste_tester/commands.rb +200 -12
- data/lib/taste_tester/config.rb +17 -4
- data/lib/taste_tester/exceptions.rb +7 -0
- data/lib/taste_tester/hooks.rb +15 -0
- data/lib/taste_tester/host.rb +131 -75
- data/lib/taste_tester/locallink.rb +6 -2
- data/lib/taste_tester/logging.rb +4 -3
- data/lib/taste_tester/noop.rb +6 -2
- data/lib/taste_tester/server.rb +23 -5
- data/lib/taste_tester/ssh.rb +12 -10
- data/lib/taste_tester/state.rb +22 -3
- data/lib/taste_tester/tunnel.rb +5 -5
- data/lib/taste_tester/windows.rb +1 -0
- metadata +37 -9
@@ -19,6 +19,7 @@ require 'taste_tester/host'
|
|
19
19
|
require 'taste_tester/config'
|
20
20
|
require 'taste_tester/client'
|
21
21
|
require 'taste_tester/logging'
|
22
|
+
require 'taste_tester/exceptions'
|
22
23
|
|
23
24
|
module TasteTester
|
24
25
|
# Functionality dispatch
|
@@ -28,6 +29,7 @@ module TasteTester
|
|
28
29
|
def self.start
|
29
30
|
server = TasteTester::Server.new
|
30
31
|
return if TasteTester::Server.running?
|
32
|
+
|
31
33
|
server.start
|
32
34
|
end
|
33
35
|
|
@@ -45,7 +47,12 @@ module TasteTester
|
|
45
47
|
server = TasteTester::Server.new
|
46
48
|
if TasteTester::Server.running?
|
47
49
|
logger.warn("Local taste-tester server running on port #{server.port}")
|
48
|
-
if server.
|
50
|
+
if TasteTester::Config.no_repo && server.last_upload_time
|
51
|
+
logger.warn("Last upload time was #{server.last_upload_time}")
|
52
|
+
elsif !TasteTester::Config.no_repo && server.latest_uploaded_ref
|
53
|
+
if server.last_upload_time
|
54
|
+
logger.warn("Last upload time was #{server.last_upload_time}")
|
55
|
+
end
|
49
56
|
logger.warn('Latest uploaded revision is ' +
|
50
57
|
server.latest_uploaded_ref)
|
51
58
|
else
|
@@ -77,12 +84,16 @@ module TasteTester
|
|
77
84
|
end
|
78
85
|
server = TasteTester::Server.new
|
79
86
|
unless TasteTester::Config.linkonly
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
87
|
+
if TasteTester::Config.no_repo
|
88
|
+
repo = nil
|
89
|
+
else
|
90
|
+
repo = BetweenMeals::Repo.get(
|
91
|
+
TasteTester::Config.repo_type,
|
92
|
+
TasteTester::Config.repo,
|
93
|
+
logger,
|
94
|
+
)
|
95
|
+
end
|
96
|
+
if repo && !repo.exists?
|
86
97
|
fail "Could not open repo from #{TasteTester::Config.repo}"
|
87
98
|
end
|
88
99
|
end
|
@@ -93,12 +104,11 @@ module TasteTester
|
|
93
104
|
tested_hosts = []
|
94
105
|
hosts.each do |hostname|
|
95
106
|
host = TasteTester::Host.new(hostname, server)
|
96
|
-
|
97
|
-
username = host.who_is_testing
|
98
|
-
logger.error("User #{username} is already testing on #{hostname}")
|
99
|
-
else
|
107
|
+
begin
|
100
108
|
host.test
|
101
109
|
tested_hosts << hostname
|
110
|
+
rescue TasteTester::Exceptions::AlreadyTestingError => e
|
111
|
+
logger.error("User #{e.username} is already testing on #{hostname}")
|
102
112
|
end
|
103
113
|
end
|
104
114
|
unless TasteTester::Config.skip_post_test_hook ||
|
@@ -106,6 +116,24 @@ module TasteTester
|
|
106
116
|
TasteTester::Hooks.post_test(TasteTester::Config.dryrun, repo,
|
107
117
|
tested_hosts)
|
108
118
|
end
|
119
|
+
# Strictly: hosts and tested_hosts should be sets to eliminate variance in
|
120
|
+
# order or duplicates. The exact comparison works here because we're
|
121
|
+
# building tested_hosts from hosts directly.
|
122
|
+
if tested_hosts == hosts
|
123
|
+
# No exceptions, complete success: every host listed is now configured
|
124
|
+
# to use our chef-zero instance.
|
125
|
+
exit(0)
|
126
|
+
end
|
127
|
+
if tested_hosts.empty?
|
128
|
+
# All requested hosts are being tested by another user. We didn't change
|
129
|
+
# their configuration.
|
130
|
+
exit(3)
|
131
|
+
end
|
132
|
+
# Otherwise, we got a mix of success and failure due to being tested by
|
133
|
+
# another user. We'll be pessemistic and return an error because the
|
134
|
+
# intent to taste test the complete list was not successful.
|
135
|
+
# code.
|
136
|
+
exit(2)
|
109
137
|
end
|
110
138
|
|
111
139
|
def self.untest
|
@@ -149,7 +177,7 @@ module TasteTester
|
|
149
177
|
|
150
178
|
def self.upload
|
151
179
|
server = TasteTester::Server.new
|
152
|
-
# On a
|
180
|
+
# On a force-upload rather than try to clean up whatever's on the server
|
153
181
|
# we'll restart chef-zero which will clear everything and do a full
|
154
182
|
# upload
|
155
183
|
if TasteTester::Config.force_upload
|
@@ -182,5 +210,165 @@ module TasteTester
|
|
182
210
|
logger.error(exception.backtrace.join("\n"))
|
183
211
|
exit 1
|
184
212
|
end
|
213
|
+
|
214
|
+
def self.impact
|
215
|
+
# Use the repository specified in config.rb to calculate the changes
|
216
|
+
# that may affect Chef. These changes will be further analyzed to
|
217
|
+
# determine specific roles which may change due to modifed dependencies.
|
218
|
+
repo = BetweenMeals::Repo.get(
|
219
|
+
TasteTester::Config.repo_type,
|
220
|
+
TasteTester::Config.repo,
|
221
|
+
logger,
|
222
|
+
)
|
223
|
+
if repo && !repo.exists?
|
224
|
+
fail "Could not open repo from #{TasteTester::Config.repo}"
|
225
|
+
end
|
226
|
+
|
227
|
+
changes = _find_changeset(repo)
|
228
|
+
|
229
|
+
# Use Knife (or custom logic) to check the dependencies of each role
|
230
|
+
# against the list of changes. `impacted_roles` will contian the set
|
231
|
+
# of roles with direct or indirect (dependency) modifications.
|
232
|
+
impacted_roles = TasteTester::Hooks.impact_find_roles(changes)
|
233
|
+
impacted_roles ||= _find_roles(changes)
|
234
|
+
|
235
|
+
# Do any post processing required on the list of impacted roles, such
|
236
|
+
# as looking up hostnames associated with each role.
|
237
|
+
final_impact = TasteTester::Hooks.post_impact(impacted_roles)
|
238
|
+
final_impact ||= impacted_roles
|
239
|
+
|
240
|
+
# Print the calculated impact. If a print hook is defined that
|
241
|
+
# returns true, then the default print function is skipped.
|
242
|
+
unless TasteTester::Hooks.print_impact(final_impact)
|
243
|
+
_print_impact(final_impact)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def self._find_changeset(repo)
|
248
|
+
# We want to compare changes in the current directory (working set) with
|
249
|
+
# the "most recent" commit in the VCS. For SVN, this will be the latest
|
250
|
+
# commit on the checked out repository (i.e. 'trunk'). Git/Hg may have
|
251
|
+
# different tags or labels assigned to the master branch, (i.e. 'master',
|
252
|
+
# 'stable', etc.) and should be configured if different than the default.
|
253
|
+
start_ref = case repo
|
254
|
+
when BetweenMeals::Repo::Svn
|
255
|
+
repo.latest_revision
|
256
|
+
when BetweenMeals::Repo::Git
|
257
|
+
TasteTester::Config.vcs_start_ref_git
|
258
|
+
when BetweenMeals::Repo::Hg
|
259
|
+
TasteTester::Config.vcs_start_ref_hg
|
260
|
+
end
|
261
|
+
end_ref = TasteTester::Config.vcs_end_ref
|
262
|
+
|
263
|
+
changeset = BetweenMeals::Changeset.new(
|
264
|
+
logger,
|
265
|
+
repo,
|
266
|
+
start_ref,
|
267
|
+
end_ref,
|
268
|
+
{
|
269
|
+
:cookbook_dirs =>
|
270
|
+
TasteTester::Config.relative_cookbook_dirs,
|
271
|
+
:role_dir =>
|
272
|
+
TasteTester::Config.relative_role_dir,
|
273
|
+
:databag_dir =>
|
274
|
+
TasteTester::Config.relative_databag_dir,
|
275
|
+
},
|
276
|
+
@track_symlinks,
|
277
|
+
)
|
278
|
+
|
279
|
+
return changeset
|
280
|
+
end
|
281
|
+
|
282
|
+
def self._find_roles(changes)
|
283
|
+
if TasteTester::Config.relative_cookbook_dirs.length > 1
|
284
|
+
logger.error('Knife deps does not support multiple cookbook paths.')
|
285
|
+
logger.error('Please flatten the cookbooks into a single directory' +
|
286
|
+
' or override the impact_find_roles function.')
|
287
|
+
exit(1)
|
288
|
+
end
|
289
|
+
|
290
|
+
cookbooks = Set.new(changes.cookbooks)
|
291
|
+
roles = Set.new(changes.roles)
|
292
|
+
databags = Set.new(changes.databags)
|
293
|
+
|
294
|
+
if cookbooks.empty? && roles.empty?
|
295
|
+
logger.warn('No cookbooks or roles have been modified.')
|
296
|
+
return Set.new
|
297
|
+
end
|
298
|
+
|
299
|
+
unless cookbooks.empty?
|
300
|
+
logger.info('Modified Cookbooks:')
|
301
|
+
cookbooks.each { |cb| logger.info("\t#{cb}") }
|
302
|
+
end
|
303
|
+
unless roles.empty?
|
304
|
+
logger.info('Modified Roles:')
|
305
|
+
roles.each { |r| logger.info("\t#{r}") }
|
306
|
+
end
|
307
|
+
unless databags.empty?
|
308
|
+
logger.info('Modified Databags:')
|
309
|
+
databags.each { |db| logger.info("\t#{db}") }
|
310
|
+
end
|
311
|
+
|
312
|
+
# Use Knife to list the dependecies for each role in the roles directory.
|
313
|
+
# This creates a recursive tree structure that is then searched for
|
314
|
+
# instances of modified cookbooks. This can be slow since it must read
|
315
|
+
# every line of the Knife output, then search all roles for dependencies.
|
316
|
+
# If you have a custom way to calculate these reverse dependencies, this
|
317
|
+
# is the part you would replace.
|
318
|
+
logger.info('Finding dependencies (this may take a minute or two)...')
|
319
|
+
knife = Mixlib::ShellOut.new(
|
320
|
+
"knife deps /#{TasteTester::Config.role_dir}/*.rb" +
|
321
|
+
" --config #{TasteTester::Config.knife_config}" +
|
322
|
+
" --chef-repo-path #{TasteTester::Config.absolute_base_dir}" +
|
323
|
+
' --tree --recurse',
|
324
|
+
)
|
325
|
+
knife.run_command
|
326
|
+
knife.error!
|
327
|
+
|
328
|
+
# Collapse the output from Knife into a hash structure that maps roles
|
329
|
+
# to the set of their dependencies. This will ignore duplicates in the
|
330
|
+
# Knife output, but must still process each line.
|
331
|
+
logger.info('Processing Dependencies...')
|
332
|
+
deps_hash = {}
|
333
|
+
curr_role = nil
|
334
|
+
|
335
|
+
knife.stdout.each_line do |line|
|
336
|
+
elem = line.rstrip
|
337
|
+
if elem.length == elem.lstrip.length
|
338
|
+
curr_role = elem
|
339
|
+
deps_hash[curr_role] = Set.new
|
340
|
+
else
|
341
|
+
deps_hash[curr_role].add(File.basename(elem, File.extname(elem)))
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
# Now we can search for modified dependencies by iterating over each
|
346
|
+
# role and checking the hash created earlier. Roles that have been
|
347
|
+
# modified directly are automatically included in the impacted set.
|
348
|
+
impacted_roles = Set.new(roles.map(&:name))
|
349
|
+
deps_hash.each do |role, deplist|
|
350
|
+
cookbooks.each do |cb|
|
351
|
+
if deplist.include?(cb.name)
|
352
|
+
impacted_roles.add(role)
|
353
|
+
logger.info("\tFound dependency: #{role} --> #{cb.name}")
|
354
|
+
break
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
return impacted_roles
|
360
|
+
end
|
361
|
+
|
362
|
+
def self._print_impact(final_impact)
|
363
|
+
if TasteTester::Config.json
|
364
|
+
puts JSON.pretty_generate(final_impact.to_a)
|
365
|
+
elsif final_impact.empty?
|
366
|
+
logger.warn('No impacted roles were found.')
|
367
|
+
else
|
368
|
+
logger.warn('The following roles have modified dependencies.' +
|
369
|
+
' Please test a host in each of these roles.')
|
370
|
+
final_impact.each { |r| logger.warn("\t#{r}") }
|
371
|
+
end
|
372
|
+
end
|
185
373
|
end
|
186
374
|
end
|
data/lib/taste_tester/config.rb
CHANGED
@@ -37,6 +37,7 @@ module TasteTester
|
|
37
37
|
config_file '/etc/taste-tester-config.rb'
|
38
38
|
plugin_path nil
|
39
39
|
chef_zero_path nil
|
40
|
+
bundle false
|
40
41
|
verbosity Logger::WARN
|
41
42
|
timestamp false
|
42
43
|
user 'root'
|
@@ -51,6 +52,7 @@ module TasteTester
|
|
51
52
|
timestamp_file '/etc/chef/test_timestamp'
|
52
53
|
use_ssh_tunnels false
|
53
54
|
ssh_command 'ssh'
|
55
|
+
ssh_connect_timeout 5
|
54
56
|
use_ssl true
|
55
57
|
chef_zero_logging true
|
56
58
|
chef_config_path '/etc/chef'
|
@@ -58,6 +60,16 @@ module TasteTester
|
|
58
60
|
my_hostname nil
|
59
61
|
track_symlinks false
|
60
62
|
transport 'ssh'
|
63
|
+
no_repo false
|
64
|
+
json false
|
65
|
+
|
66
|
+
# Start/End refs for calculating changes in the repo.
|
67
|
+
# - start_ref should be the "master" commit of the repository
|
68
|
+
# - end_ref should be nil to compare with the working set,
|
69
|
+
# or something like '.' to compare with the most recent commit
|
70
|
+
vcs_start_ref_git 'origin/HEAD'
|
71
|
+
vcs_start_ref_hg 'master'
|
72
|
+
vcs_end_ref nil
|
61
73
|
|
62
74
|
skip_pre_upload_hook false
|
63
75
|
skip_post_upload_hook false
|
@@ -97,6 +109,10 @@ module TasteTester
|
|
97
109
|
end
|
98
110
|
end
|
99
111
|
|
112
|
+
def self.absolute_base_dir
|
113
|
+
File.join(repo, base_dir)
|
114
|
+
end
|
115
|
+
|
100
116
|
def self.chef_port
|
101
117
|
require 'taste_tester/state'
|
102
118
|
port_range = (
|
@@ -116,11 +132,8 @@ module TasteTester
|
|
116
132
|
end
|
117
133
|
|
118
134
|
def self.testing_end_time
|
119
|
-
|
120
|
-
TasteTester::Config.testing_until
|
121
|
-
else
|
135
|
+
TasteTester::Config.testing_until ||
|
122
136
|
Time.now + TasteTester::Config.testing_time
|
123
|
-
end
|
124
137
|
end
|
125
138
|
end
|
126
139
|
end
|
data/lib/taste_tester/hooks.rb
CHANGED
@@ -48,6 +48,21 @@ module TasteTester
|
|
48
48
|
# Additional checks you want to do on the repo
|
49
49
|
def self.repo_checks(_dryrun, _repo); end
|
50
50
|
|
51
|
+
# Find the set of roles dependent on the changed files.
|
52
|
+
# If returning something other than a set of roles, post_impact and/or
|
53
|
+
# print_impact should be specified to handle the output.
|
54
|
+
def self.impact_find_roles(_changes); end
|
55
|
+
|
56
|
+
# Do stuff after we find impacted roles
|
57
|
+
# This should return a Set object with the final impact. To return more
|
58
|
+
# complex data, you must also provide a print_impact function which returns
|
59
|
+
# true to override the default output.
|
60
|
+
def self.post_impact(_impacted_roles); end
|
61
|
+
|
62
|
+
# Customized the printed output of impact
|
63
|
+
# If this method returns true, the default output will not be printed.
|
64
|
+
def self.print_impact(_final_impact); end
|
65
|
+
|
51
66
|
def self.get(file)
|
52
67
|
path = File.expand_path(file)
|
53
68
|
logger.warn("Loading plugin at #{path}")
|
data/lib/taste_tester/host.rb
CHANGED
@@ -23,12 +23,16 @@ require 'taste_tester/ssh'
|
|
23
23
|
require 'taste_tester/noop'
|
24
24
|
require 'taste_tester/locallink'
|
25
25
|
require 'taste_tester/tunnel'
|
26
|
+
require 'taste_tester/exceptions'
|
26
27
|
|
27
28
|
module TasteTester
|
28
29
|
# Manage state of the remote node
|
29
30
|
class Host
|
30
31
|
include TasteTester::Logging
|
31
32
|
|
33
|
+
TASTE_TESTER_CONFIG = 'client-taste-tester.rb'.freeze
|
34
|
+
USER_PREAMBLE = '# TasteTester by '.freeze
|
35
|
+
|
32
36
|
attr_reader :name
|
33
37
|
|
34
38
|
def initialize(name, server)
|
@@ -94,22 +98,45 @@ module TasteTester
|
|
94
98
|
@tunnel.run
|
95
99
|
end
|
96
100
|
|
97
|
-
|
101
|
+
serialized_config = Base64.encode64(config).delete("\n")
|
98
102
|
|
99
103
|
# Then setup the testing
|
100
104
|
transport = get_transport
|
101
105
|
|
106
|
+
# see if someone else is taste-testing
|
107
|
+
transport << we_testing
|
108
|
+
|
102
109
|
transport << 'logger -t taste-tester Moving server into taste-tester' +
|
103
110
|
" for #{@user}"
|
104
111
|
transport << touchcmd
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
112
|
+
# shell redirection is also racy, so make a temporary file first
|
113
|
+
transport << "tmpconf=$(mktemp #{TasteTester::Config.chef_config_path}/" +
|
114
|
+
"#{TASTE_TESTER_CONFIG}.TMPXXXXXX)"
|
115
|
+
transport << "/bin/echo -n \"#{serialized_config}\" | base64 --decode" +
|
116
|
+
' > "${tmpconf}"'
|
117
|
+
# then rename it to replace any existing file
|
118
|
+
transport << 'mv -f "${tmpconf}" ' +
|
119
|
+
"#{TasteTester::Config.chef_config_path}/#{TASTE_TESTER_CONFIG}"
|
120
|
+
transport << "( ln -vsf #{TasteTester::Config.chef_config_path}" +
|
121
|
+
"/#{TASTE_TESTER_CONFIG} #{TasteTester::Config.chef_config_path}/" +
|
111
122
|
"#{TasteTester::Config.chef_config}; true )"
|
112
|
-
|
123
|
+
|
124
|
+
# look again to see if someone else is taste-testing. This is where
|
125
|
+
# we work out if we won or lost a race with another user.
|
126
|
+
transport << we_testing
|
127
|
+
|
128
|
+
transport.run
|
129
|
+
|
130
|
+
case transport.status
|
131
|
+
when 0
|
132
|
+
# no problem, keep going.
|
133
|
+
nil
|
134
|
+
when 42
|
135
|
+
fail TasteTester::Exceptions::AlreadyTestingError,
|
136
|
+
transport.output.chomp
|
137
|
+
else
|
138
|
+
transport.error!
|
139
|
+
end
|
113
140
|
|
114
141
|
# Then run any other stuff they wanted
|
115
142
|
cmds = TasteTester::Hooks.test_remote_cmds(
|
@@ -117,7 +144,7 @@ module TasteTester
|
|
117
144
|
@name,
|
118
145
|
)
|
119
146
|
|
120
|
-
if cmds
|
147
|
+
if cmds&.any?
|
121
148
|
transport = get_transport
|
122
149
|
cmds.each { |c| transport << c }
|
123
150
|
transport.run!
|
@@ -132,15 +159,12 @@ module TasteTester
|
|
132
159
|
end
|
133
160
|
config_prod = TasteTester::Config.chef_config.split('.').join('-prod.')
|
134
161
|
[
|
135
|
-
"
|
136
|
-
TasteTester::Config.chef_config,
|
137
|
-
"rm -vf #{TasteTester::Config.chef_config_path}/client-taste-tester.rb",
|
138
|
-
"ln -vs #{TasteTester::Config.chef_config_path}/#{config_prod} " +
|
162
|
+
"ln -vsf #{TasteTester::Config.chef_config_path}/#{config_prod} " +
|
139
163
|
"#{TasteTester::Config.chef_config_path}/" +
|
140
164
|
TasteTester::Config.chef_config,
|
141
|
-
"
|
142
|
-
"ln -vs #{TasteTester::Config.chef_config_path}/client-prod.pem " +
|
165
|
+
"ln -vsf #{TasteTester::Config.chef_config_path}/client-prod.pem " +
|
143
166
|
"#{TasteTester::Config.chef_config_path}/client.pem",
|
167
|
+
"rm -vf #{TasteTester::Config.chef_config_path}/#{TASTE_TESTER_CONFIG}",
|
144
168
|
"rm -vf #{TasteTester::Config.timestamp_file}",
|
145
169
|
'logger -t taste-tester Returning server to production',
|
146
170
|
].each do |cmd|
|
@@ -149,43 +173,25 @@ module TasteTester
|
|
149
173
|
transport.run!
|
150
174
|
end
|
151
175
|
|
152
|
-
def
|
153
|
-
|
154
|
-
transport << 'grep "^# TasteTester by"' +
|
155
|
-
" #{TasteTester::Config.chef_config_path}/" +
|
156
|
-
TasteTester::Config.chef_config
|
157
|
-
output = transport.run
|
158
|
-
if output.first.zero?
|
159
|
-
user = output.last.match(/# TasteTester by (.*)$/)
|
160
|
-
if user
|
161
|
-
return user[1]
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
# Legacy FB stuff, remove after migration. Safe for everyone else.
|
166
|
-
transport = get_transport
|
167
|
-
transport << "file #{TasteTester::Config.chef_config_path}/" +
|
176
|
+
def we_testing
|
177
|
+
config_file = "#{TasteTester::Config.chef_config_path}/" +
|
168
178
|
TasteTester::Config.chef_config
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
true
|
186
|
-
else
|
187
|
-
false
|
188
|
-
end
|
179
|
+
# Look for signature of TasteTester
|
180
|
+
# 1. Look for USER_PREAMBLE line prefix
|
181
|
+
# 2. See if user is us, or someone else
|
182
|
+
# 3. if someone else is testing: emit username, exit with code 42 which
|
183
|
+
# short circuits the test verb
|
184
|
+
# This is written as a squiggly heredoc so the indentation of the awk is
|
185
|
+
# preserved. Later we remove the newlines to make it a bit easier to read.
|
186
|
+
shellcode = <<~ENDOFSHELLCODE
|
187
|
+
awk "\\$0 ~ /^#{USER_PREAMBLE}/{
|
188
|
+
if (\\$NF != \\"#{@user}\\"){
|
189
|
+
print \\$NF;
|
190
|
+
exit 42
|
191
|
+
}
|
192
|
+
}" #{config_file}
|
193
|
+
ENDOFSHELLCODE
|
194
|
+
shellcode.delete("\n")
|
189
195
|
end
|
190
196
|
|
191
197
|
def keeptesting
|
@@ -220,40 +226,90 @@ module TasteTester
|
|
220
226
|
if TasteTester::Config.use_ssh_tunnels
|
221
227
|
url = "#{scheme}://localhost:#{@tunnel.port}"
|
222
228
|
else
|
223
|
-
url = "#{scheme}://#{@server.host}"
|
229
|
+
url = +"#{scheme}://#{@server.host}"
|
224
230
|
url << ":#{TasteTester::State.port}" if TasteTester::State.port
|
225
231
|
end
|
226
|
-
|
227
|
-
|
228
|
-
#
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
end
|
232
|
+
ttconfig = <<~ENDOFSCRIPT
|
233
|
+
#{USER_PREAMBLE}#{@user}
|
234
|
+
# Prevent people from screwing up their permissions
|
235
|
+
if Process.euid != 0
|
236
|
+
puts 'Please run chef as root!'
|
237
|
+
Process.exit!
|
238
|
+
end
|
234
239
|
|
235
|
-
log_level :info
|
236
|
-
log_location STDOUT
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
+
log_level :info
|
241
|
+
log_location STDOUT
|
242
|
+
ssl_verify_mode :verify_none
|
243
|
+
ohai.plugin_path << File.join('#{TasteTester::Config.chef_config_path}', 'ohai_plugins')
|
244
|
+
ENDOFSCRIPT
|
240
245
|
|
241
|
-
|
242
|
-
|
246
|
+
if TasteTester::Config.bundle
|
247
|
+
ttconfig += <<~ENDOFSCRIPT
|
248
|
+
taste_tester_dest = File.join(Dir.tmpdir, 'taste-tester')
|
249
|
+
puts 'INFO: Downloading bundle from #{url}...'
|
250
|
+
FileUtils.rmtree(taste_tester_dest)
|
251
|
+
FileUtils.mkpath(taste_tester_dest)
|
252
|
+
FileUtils.touch(File.join(taste_tester_dest, 'chefignore'))
|
253
|
+
uri = URI('#{url}/file_store/tt.tgz')
|
254
|
+
Net::HTTP.start(
|
255
|
+
uri.host,
|
256
|
+
uri.port,
|
257
|
+
:use_ssl => #{TasteTester::Config.use_ssl},
|
258
|
+
# we expect self signed certificates
|
259
|
+
:verify_mode => OpenSSL::SSL::VERIFY_NONE,
|
260
|
+
) do |http|
|
261
|
+
http.request_get(uri) do |response|
|
262
|
+
# the use of stringIO means we are buffering the entire file in
|
263
|
+
# memory. This isn't very efficient, but it should work for
|
264
|
+
# most practical cases.
|
265
|
+
stream = Zlib::GzipReader.new(StringIO.new(response.body))
|
266
|
+
Gem::Package::TarReader.new(stream).each do |e|
|
267
|
+
dest = File.join(taste_tester_dest, e.full_name)
|
268
|
+
FileUtils.mkpath(File.dirname(dest))
|
269
|
+
if e.symlink?
|
270
|
+
File.symlink(e.header.linkname, dest)
|
271
|
+
else
|
272
|
+
File.open(dest, 'wb+') do |f|
|
273
|
+
# https://github.com/rubygems/rubygems/pull/2303
|
274
|
+
# IO.copy_stream(e, f)
|
275
|
+
# workaround:
|
276
|
+
f.write(e.read)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
puts 'INFO: Download complete'
|
283
|
+
solo true
|
284
|
+
local_mode true
|
285
|
+
ENDOFSCRIPT
|
286
|
+
else
|
287
|
+
ttconfig += <<~ENDOFSCRIPT
|
288
|
+
chef_server_url '#{url}'
|
289
|
+
ENDOFSCRIPT
|
290
|
+
end
|
243
291
|
|
244
292
|
extra = TasteTester::Hooks.test_remote_client_rb_extra_code(@name)
|
245
293
|
if extra
|
246
|
-
ttconfig +=
|
247
|
-
# Begin user-hook specified code
|
248
|
-
|
249
|
-
# End user-hook secified code
|
294
|
+
ttconfig += <<~ENDOFSCRIPT
|
295
|
+
# Begin user-hook specified code
|
296
|
+
#{extra}
|
297
|
+
# End user-hook secified code
|
250
298
|
|
251
|
-
|
299
|
+
ENDOFSCRIPT
|
252
300
|
end
|
253
301
|
|
254
|
-
ttconfig +=
|
255
|
-
puts 'INFO: Running on #{@name} in taste-tester by #{@user}'
|
256
|
-
|
302
|
+
ttconfig += <<~ENDOFSCRIPT
|
303
|
+
puts 'INFO: Running on #{@name} in taste-tester by #{@user}'
|
304
|
+
ENDOFSCRIPT
|
305
|
+
|
306
|
+
if TasteTester::Config.bundle
|
307
|
+
# This is last in the configuration file because it needs to override
|
308
|
+
# any values in test_remote_client_rb_extra_code
|
309
|
+
ttconfig += <<~ENDOFSCRIPT
|
310
|
+
chef_repo_path taste_tester_dest
|
311
|
+
ENDOFSCRIPT
|
312
|
+
end
|
257
313
|
return ttconfig
|
258
314
|
end
|
259
315
|
end
|
@@ -22,7 +22,7 @@ module TasteTester
|
|
22
22
|
include TasteTester::Logging
|
23
23
|
include BetweenMeals::Util
|
24
24
|
|
25
|
-
attr_reader :output
|
25
|
+
attr_reader :output, :status
|
26
26
|
|
27
27
|
def initialize
|
28
28
|
@host = 'localhost'
|
@@ -43,7 +43,11 @@ module TasteTester
|
|
43
43
|
@status, @output = exec!(cmd, logger)
|
44
44
|
rescue StandardError => e
|
45
45
|
logger.error(e.message)
|
46
|
-
|
46
|
+
error!
|
47
|
+
end
|
48
|
+
|
49
|
+
def error!
|
50
|
+
fail TasteTester::Exceptions::LocalLinkError
|
47
51
|
end
|
48
52
|
|
49
53
|
private
|
data/lib/taste_tester/logging.rb
CHANGED
@@ -35,8 +35,8 @@ module TasteTester
|
|
35
35
|
@logger ||= Logger.new(STDOUT)
|
36
36
|
end
|
37
37
|
|
38
|
-
def self.formatterproc=(
|
39
|
-
@@formatter_proc =
|
38
|
+
def self.formatterproc=(process)
|
39
|
+
@@formatter_proc = process
|
40
40
|
end
|
41
41
|
|
42
42
|
def self.use_log_formatter=(use_log_formatter)
|
@@ -49,6 +49,7 @@ module TasteTester
|
|
49
49
|
|
50
50
|
def formatter
|
51
51
|
return @@formatter_proc if @@formatter_proc
|
52
|
+
|
52
53
|
if @@use_log_formatter
|
53
54
|
proc do |severity, datetime, _progname, msg|
|
54
55
|
if severity == 'ERROR'
|
@@ -58,7 +59,7 @@ module TasteTester
|
|
58
59
|
end
|
59
60
|
else
|
60
61
|
proc do |severity, _datetime, _progname, msg|
|
61
|
-
msg.to_s.prepend("#{severity}: ") unless severity == 'WARN'
|
62
|
+
msg.dup.to_s.prepend("#{severity}: ") unless severity == 'WARN'
|
62
63
|
if severity == 'ERROR'
|
63
64
|
msg = msg.to_s.red
|
64
65
|
end
|