taste_tester 0.0.13 → 0.0.14
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 +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
|