tdi 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +41 -3
- data/bin/tdi +6 -2
- data/doc/output/both.png +0 -0
- data/doc/output/failure.png +0 -0
- data/doc/output/nofail.png +0 -0
- data/doc/output/success.png +0 -0
- data/doc/output/warnfail.png +0 -0
- data/doc/output/warning.png +0 -0
- data/helper/acl.rb +1 -1
- data/helper/file.rb +43 -18
- data/helper/http.rb +13 -2
- data/helper/ssh.rb +1 -1
- data/lib/planner.rb +5 -5
- data/lib/rblank.rb +1 -1
- data/lib/rmerge.rb +1 -1
- data/lib/runner.rb +10 -4
- data/lib/tdi.rb +1 -8
- data/lib/tdi/version.rb +2 -2
- data/lib/util.rb +2 -2
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 244daf4e0163efb156bc527c7b1f47d0a8f0aaa5
|
4
|
+
data.tar.gz: b6d43ee88862a8e6a51423b697cf8c53f4c3145d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b26b2de5ab7ba6d092856a85deb9b374de6eb4f8633a78694c69167202fc1b55faf644fb22ef71b0100292c1b151c085cc7d664d43d35cb69a935eb8de2ec0f
|
7
|
+
data.tar.gz: 62b798baccb34ff7dbbb9cfe70b67575e79d3812e4458c3cf4c31c2b71f537db6724c5840c44844e0439e77beae64e5e2582d2b0a646cf7c3cdd5c683766df43
|
data/README.md
CHANGED
@@ -40,6 +40,9 @@ Examples:
|
|
40
40
|
tdi tdi.json -n
|
41
41
|
tdi tdi.json --nofail
|
42
42
|
|
43
|
+
tdi tdi.json -w
|
44
|
+
tdi tdi.json --warnfail
|
45
|
+
|
43
46
|
tdi tdi.json -p app
|
44
47
|
tdi tdi.json --plan app
|
45
48
|
tdi tdi.json --plan app::acl
|
@@ -60,6 +63,7 @@ Examples:
|
|
60
63
|
Options:
|
61
64
|
|
62
65
|
-n, --nofail No fail mode.
|
66
|
+
-w, --warnfail Fail if any warning.
|
63
67
|
-p, --plan Test plan list.
|
64
68
|
-r, --reportfile Report file to save test plan status.
|
65
69
|
-s, --shred Wipe out the test plan, leaving no trace behind.
|
@@ -247,7 +251,10 @@ tdi_plan = {
|
|
247
251
|
:app => {
|
248
252
|
:desc => 'Test role',
|
249
253
|
:acl => {'localhost' => {:port => [22, 80]}},
|
250
|
-
:http => {
|
254
|
+
:http => {
|
255
|
+
'globo.com' => {:code => 301},
|
256
|
+
'noexist.globo.com' => {:code => 200},
|
257
|
+
},
|
251
258
|
}
|
252
259
|
}
|
253
260
|
|
@@ -263,19 +270,50 @@ tdi_plan = {
|
|
263
270
|
'app': {
|
264
271
|
'desc': 'Test role',
|
265
272
|
'acl': {'localhost': {'port': [22, 80]}},
|
266
|
-
'http': {
|
273
|
+
'http': {
|
274
|
+
'globo.com': {'code': 301},
|
275
|
+
'noexist.globo.com': {'code': 200},
|
276
|
+
},
|
267
277
|
}
|
268
278
|
}
|
269
279
|
|
270
280
|
open('tdi.json', 'w').write(json.dumps(tdi_plan, indent=2))
|
271
281
|
```
|
272
282
|
|
273
|
-
###
|
283
|
+
### Validating your plan file
|
274
284
|
|
275
285
|
Use JSONLint site to validate your JSONs.
|
276
286
|
|
277
287
|
http://jsonlint.com/
|
278
288
|
|
289
|
+
## Running test plans and their possible `return codes`
|
290
|
+
|
291
|
+
Return codes are:
|
292
|
+
|
293
|
+
* `0` if all `success`
|
294
|
+
|
295
|
+
![success](https://raw.githubusercontent.com/globocom/tdi/master/doc/output/success.png)
|
296
|
+
|
297
|
+
* `0` by default even if warning (warning should not generate a validation error)
|
298
|
+
|
299
|
+
![warning](https://raw.githubusercontent.com/globocom/tdi/master/doc/output/warning.png)
|
300
|
+
|
301
|
+
* `1` if any `failure`
|
302
|
+
|
303
|
+
![failure](https://raw.githubusercontent.com/globocom/tdi/master/doc/output/failure.png)
|
304
|
+
|
305
|
+
* `2` if any `warning` plus option `-w` or `--warnfail`
|
306
|
+
|
307
|
+
![warnfail](https://raw.githubusercontent.com/globocom/tdi/master/doc/output/warnfail.png)
|
308
|
+
|
309
|
+
* `3` if both `failure` and `warning` plus option `-w` or `--warnfail`
|
310
|
+
|
311
|
+
![both](https://raw.githubusercontent.com/globocom/tdi/master/doc/output/both.png)
|
312
|
+
|
313
|
+
* `0` regardless failure or warning if `-n` or `--nofail`
|
314
|
+
|
315
|
+
![nofail](https://raw.githubusercontent.com/globocom/tdi/master/doc/output/nofail.png)
|
316
|
+
|
279
317
|
## Contributors
|
280
318
|
|
281
319
|
[People](https://github.com/globocom/tdi/graphs/contributors)
|
data/bin/tdi
CHANGED
@@ -13,14 +13,14 @@
|
|
13
13
|
#
|
14
14
|
# === Authors
|
15
15
|
#
|
16
|
-
# Rogério Carvalho Schneider <
|
16
|
+
# Rogério Carvalho Schneider <schneider@corp.globo.com>
|
17
17
|
# Leonardo Martins de Lima <leonardo.martins@corp.globo.com>
|
18
18
|
# Diogo Kiss <diogokiss@corp.globo.com>
|
19
19
|
# Francisco Corrêa <francisco@corp.globo.com>
|
20
20
|
#
|
21
21
|
# === Copyright
|
22
22
|
#
|
23
|
-
# Copyright (C) 2013-
|
23
|
+
# Copyright (C) 2013-2017 Globo.com
|
24
24
|
#
|
25
25
|
|
26
26
|
# This file is part of TDI.
|
@@ -170,6 +170,9 @@ Examples:
|
|
170
170
|
tdi tdi.json -n
|
171
171
|
tdi tdi.json --nofail
|
172
172
|
|
173
|
+
tdi tdi.json -w
|
174
|
+
tdi tdi.json --warnfail
|
175
|
+
|
173
176
|
tdi tdi.json -p app
|
174
177
|
tdi tdi.json --plan app
|
175
178
|
tdi tdi.json --plan app::acl
|
@@ -190,6 +193,7 @@ Examples:
|
|
190
193
|
Options:
|
191
194
|
EOS
|
192
195
|
on :n, :nofail, 'No fail mode.'
|
196
|
+
on :w, :warnfail, 'Fail if any warning.'
|
193
197
|
on :p, :plan, 'Test plan list.', as: Array, argument: :optional
|
194
198
|
on :r, :reportfile, 'Report file to save test plan status.', argument: :required
|
195
199
|
on :s, :shred, 'Wipe out the test plan, leaving no trace behind.'
|
data/doc/output/both.png
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/helper/acl.rb
CHANGED
data/helper/file.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (C) 2013-
|
2
|
+
# Copyright (C) 2013-2017 Globo.com
|
3
3
|
#
|
4
4
|
|
5
5
|
# This file is part of TDI.
|
@@ -64,41 +64,66 @@ class TDIPlan < TDI
|
|
64
64
|
rescue
|
65
65
|
@flag_success = false if perm.eql?('rw')
|
66
66
|
ensure
|
67
|
-
# Cleanup
|
68
|
-
|
67
|
+
# Cleanup. If type is directory, remove tempfile.
|
68
|
+
if type.eql?('directory')
|
69
|
+
FileUtils.rm(filename) rescue nil
|
70
|
+
end
|
69
71
|
end
|
70
72
|
end
|
71
73
|
|
72
74
|
# Type.
|
73
75
|
case type
|
74
76
|
when 'directory'
|
77
|
+
# Access?
|
78
|
+
# Must exist, be readable and executable.
|
79
|
+
@flag_success = File.exist?(path) && File.readable?(path) && File.executable?(path)
|
80
|
+
@flag_success = File.exist?(path) && File.readable?(path) && File.executable?(path) && File.writable?(path) if perm.eql?('rw')
|
81
|
+
|
75
82
|
# Path.
|
76
83
|
filename = "#{path}/#{ENV['HOSTNAME']}.rw"
|
77
|
-
testPerm filename, perm, type
|
84
|
+
testPerm filename, perm, type if @flag_success # must be accessible or false positive may manifest
|
78
85
|
when 'file'
|
86
|
+
# Access?
|
87
|
+
# Must exist and be readable.
|
88
|
+
@flag_success = File.exist?(path) && File.readable?(path)
|
89
|
+
@flag_success = File.exist?(path) && File.readable?(path) && File.writable?(path) if perm.eql?('rw')
|
90
|
+
|
79
91
|
# Path.
|
80
92
|
filename = path
|
81
|
-
testPerm filename, perm, type
|
93
|
+
testPerm filename, perm, type if @flag_success # must be accessible or false positive may manifest
|
82
94
|
when 'link'
|
83
|
-
|
84
|
-
|
95
|
+
# Access?
|
96
|
+
# Must exist and be readable. Target must also exist.
|
97
|
+
@flag_success = File.symlink?(path) && File.exist?(path) && File.readable?(path)
|
98
|
+
@flag_success = File.symlink?(path) && File.exist?(path) && File.readable?(path) && File.writable?(path) if perm.eql?('rw')
|
85
99
|
else
|
86
100
|
puts "ERR: Invalid file plan format \"#{type}\". Type must be \"directory\", \"file\" or \"link\".".light_magenta
|
87
101
|
exit 1
|
88
102
|
end
|
89
103
|
|
90
104
|
# Location.
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
105
|
+
if @flag_success
|
106
|
+
mount_p = File.realpath(path)
|
107
|
+
mount_t = nil
|
108
|
+
while !mount_p.eql?('/') && mount_t.nil?
|
109
|
+
mount_t = Filesystem.mounts.select { |mount| mount.mount_point.eql?(mount_p) }.first
|
110
|
+
mount_t = mount_t.mount_type unless mount_t.nil?
|
111
|
+
mount_p = File.expand_path("#{mount_p}/../") # pop leaf dir before next iteration
|
112
|
+
end
|
113
|
+
if mount_t.nil?
|
114
|
+
mount_p = '/'
|
115
|
+
mount_t = Filesystem.mounts.select { |mount| mount.mount_point.eql?(mount_p) }.first.mount_type
|
116
|
+
end
|
117
|
+
|
118
|
+
case location
|
119
|
+
when 'local'
|
120
|
+
@flag_success = false if REMOTE_FS_LIST.include?(mount_t)
|
121
|
+
when 'nfs'
|
122
|
+
@flag_success = false unless mount_t.eql?('nfs')
|
123
|
+
else
|
124
|
+
puts "ERR: Invalid file plan format \"#{location}\". Location must be \"local\" or \"nfs\".".light_magenta
|
125
|
+
exit 1
|
126
|
+
end
|
102
127
|
end
|
103
128
|
|
104
129
|
# Verdict.
|
data/helper/http.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (C) 2013-
|
2
|
+
# Copyright (C) 2013-2017 Globo.com
|
3
3
|
#
|
4
4
|
|
5
5
|
# This file is part of TDI.
|
@@ -75,7 +75,11 @@ class TDIPlan < TDI
|
|
75
75
|
addr = nil
|
76
76
|
proxy_addr = nil
|
77
77
|
res_str = case_name
|
78
|
-
|
78
|
+
if proxy.nil?
|
79
|
+
res_dict = {url: case_name, net: origin_network(host)}
|
80
|
+
else
|
81
|
+
res_dict = {url: case_name, net: origin_network(proxy)}
|
82
|
+
end
|
79
83
|
res_dict[:proxy] = "#{proxy}:#{proxy_port}" unless proxy.nil?
|
80
84
|
headers = nil
|
81
85
|
response = nil
|
@@ -88,6 +92,13 @@ class TDIPlan < TDI
|
|
88
92
|
begin
|
89
93
|
addr = getaddress(host).to_s
|
90
94
|
|
95
|
+
# Update report information to keep :to key pointing to the actual host,
|
96
|
+
# not to the proxy host. Add :via key with proxy network data.
|
97
|
+
unless proxy.nil?
|
98
|
+
res_dict[:net][:via] = res_dict[:net][:to]
|
99
|
+
res_dict[:net][:to] = {host: host, addr: addr}
|
100
|
+
end
|
101
|
+
|
91
102
|
if not proxy.nil? and not proxy_port.nil?
|
92
103
|
proxy_addr = getaddress(proxy).to_s
|
93
104
|
http = Net::HTTP::Proxy(proxy, proxy_port)
|
data/helper/ssh.rb
CHANGED
data/lib/planner.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (C) 2013-
|
2
|
+
# Copyright (C) 2013-2017 Globo.com
|
3
3
|
#
|
4
4
|
|
5
5
|
# This file is part of TDI.
|
@@ -132,9 +132,9 @@ def plan_compiler(opts, plan)
|
|
132
132
|
end
|
133
133
|
|
134
134
|
# Test case compile.
|
135
|
-
new_case_content = role_content.reject { |key, val| UNMERGEABLE_KEY_LIST.include?(key)
|
136
|
-
new_case_content.merge!(plan_content.reject { |key, val| UNMERGEABLE_KEY_LIST.include?(key)
|
137
|
-
new_case_content.merge!(case_content.reject { |key, val| UNMERGEABLE_KEY_LIST.include?(key)
|
135
|
+
new_case_content = role_content.reject { |key, val| UNMERGEABLE_KEY_LIST.include?(key) || val.is_a?(Hash) }
|
136
|
+
new_case_content.merge!(plan_content.reject { |key, val| UNMERGEABLE_KEY_LIST.include?(key) || val.is_a?(Hash) })
|
137
|
+
new_case_content.merge!(case_content.reject { |key, val| UNMERGEABLE_KEY_LIST.include?(key) || val.is_a?(Hash) })
|
138
138
|
|
139
139
|
# Test case (new, merged).
|
140
140
|
compiled_plan[role_name][plan_name][case_name] = new_case_content
|
@@ -209,7 +209,7 @@ def plan_inheriter(opts, plan)
|
|
209
209
|
unless i_plan.nil?
|
210
210
|
i_role_name, i_plan_name = role_plan_split(i_plan)
|
211
211
|
|
212
|
-
if i_role_name.nil?
|
212
|
+
if i_role_name.nil? || i_plan_name.nil?
|
213
213
|
puts "ERR: Invalid inheritance \"#{i_plan}\". Must match pattern \"role::plan\".".light_magenta
|
214
214
|
exit 1
|
215
215
|
end
|
data/lib/rblank.rb
CHANGED
data/lib/rmerge.rb
CHANGED
data/lib/runner.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (C) 2013-
|
2
|
+
# Copyright (C) 2013-2017 Globo.com
|
3
3
|
#
|
4
4
|
|
5
5
|
# This file is part of TDI.
|
@@ -30,7 +30,7 @@ def runner(opts, filename, plan)
|
|
30
30
|
# Ex: {"common": {"desc": "...", "acl": {"domain1": {"port": 80}...}...}...}
|
31
31
|
plan.select { |role_name, role_content|
|
32
32
|
if role_content.is_a?(Hash)
|
33
|
-
UNTESTABLE_ROLE_LIST.include?(role_name)
|
33
|
+
UNTESTABLE_ROLE_LIST.include?(role_name) || role_content['notest'].eql?('true')
|
34
34
|
end
|
35
35
|
}.each_pair do |role_name, role_content|
|
36
36
|
puts "Skipping reserved or disabled role: #{role_name}".yellow if opts[:verbose] > 0
|
@@ -39,7 +39,7 @@ def runner(opts, filename, plan)
|
|
39
39
|
# Remove untestable roles.
|
40
40
|
plan.reject! { |role_name, role_content|
|
41
41
|
if role_content.is_a?(Hash)
|
42
|
-
UNTESTABLE_ROLE_LIST.include?(role_name)
|
42
|
+
UNTESTABLE_ROLE_LIST.include?(role_name) || role_content['notest'].eql?('true')
|
43
43
|
end
|
44
44
|
}
|
45
45
|
total_roles = plan.select { |key, val| val.is_a?(Hash) }.size
|
@@ -102,8 +102,14 @@ def runner(opts, filename, plan)
|
|
102
102
|
|
103
103
|
puts 'Running tests... done.'.green if opts[:verbose] > 1
|
104
104
|
|
105
|
-
ret =
|
105
|
+
ret = 0
|
106
|
+
ret += 1 if tdiplan.fail > 0
|
107
|
+
ret += 2 if opts.warnfail? && tdiplan.warn > 0
|
106
108
|
ret = 0 if opts.nofail?
|
109
|
+
# 1 if failures
|
110
|
+
# 2 if warnings (only if -w/--warnfail is active)
|
111
|
+
# 3 if both (only if -w/--warnfail is active)
|
112
|
+
# 0 if none or if -n/--nofail is active
|
107
113
|
ret
|
108
114
|
end
|
109
115
|
|
data/lib/tdi.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (C) 2013-
|
2
|
+
# Copyright (C) 2013-2017 Globo.com
|
3
3
|
#
|
4
4
|
|
5
5
|
# This file is part of TDI.
|
@@ -21,14 +21,9 @@ require 'socket'
|
|
21
21
|
require_relative 'tdi/version'
|
22
22
|
|
23
23
|
class TDI
|
24
|
-
attr_accessor :plan_passed, :case_passed
|
25
|
-
alias :plan_passed? :plan_passed
|
26
|
-
alias :case_passed? :case_passed
|
27
24
|
attr_accessor :skip, :pass, :warn, :fail, :report
|
28
25
|
|
29
26
|
def initialize
|
30
|
-
@plan_passed = true
|
31
|
-
@case_passed = true
|
32
27
|
@skip = 0
|
33
28
|
@pass = 0
|
34
29
|
@warn = 0
|
@@ -59,8 +54,6 @@ class TDI
|
|
59
54
|
def failure(role_name, plan_name, res_msg, res_dict)
|
60
55
|
update_report(:fail, role_name, plan_name, res_dict)
|
61
56
|
printf("%-70s [ %s ]\n", res_msg, 'FAIL'.light_red)
|
62
|
-
@plan_passed = false
|
63
|
-
@case_passed = false
|
64
57
|
@fail += 1
|
65
58
|
end
|
66
59
|
|
data/lib/tdi/version.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (C) 2013-
|
2
|
+
# Copyright (C) 2013-2017 Globo.com
|
3
3
|
#
|
4
4
|
|
5
5
|
# This file is part of TDI.
|
@@ -18,5 +18,5 @@
|
|
18
18
|
# along with TDI. If not, see <http://www.gnu.org/licenses/>.
|
19
19
|
|
20
20
|
module Tdi
|
21
|
-
VERSION = '0.2.
|
21
|
+
VERSION = '0.2.4'
|
22
22
|
end
|
data/lib/util.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (C) 2013-
|
2
|
+
# Copyright (C) 2013-2017 Globo.com
|
3
3
|
#
|
4
4
|
|
5
5
|
# This file is part of TDI.
|
@@ -52,7 +52,7 @@ end
|
|
52
52
|
|
53
53
|
# Return a list of local networks and it's details.
|
54
54
|
def local_networks
|
55
|
-
Socket.getifaddrs.each.select { |ifaddr| ifaddr.addr.ipv4?
|
55
|
+
Socket.getifaddrs.each.select { |ifaddr| ifaddr.addr.ipv4? && !ifaddr.name.start_with?('lo') }.
|
56
56
|
map do |ifaddr|
|
57
57
|
ip = IPAddress::IPv4.new("#{ifaddr.addr.ip_address}/#{ifaddr.netmask.ip_address}")
|
58
58
|
{
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tdi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rogério Carvalho Schneider
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2017-04-
|
13
|
+
date: 2017-04-19 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -157,6 +157,12 @@ files:
|
|
157
157
|
- doc/json/file.json
|
158
158
|
- doc/json/http.json
|
159
159
|
- doc/json/ssh.json
|
160
|
+
- doc/output/both.png
|
161
|
+
- doc/output/failure.png
|
162
|
+
- doc/output/nofail.png
|
163
|
+
- doc/output/success.png
|
164
|
+
- doc/output/warnfail.png
|
165
|
+
- doc/output/warning.png
|
160
166
|
- helper/acl.rb
|
161
167
|
- helper/file.rb
|
162
168
|
- helper/http.rb
|