tdi 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/Gemfile.lock +6 -2
- data/README.md +257 -22
- data/bin/tdi +25 -13
- data/doc/json/acl.json +25 -0
- data/doc/json/file.json +31 -0
- data/doc/json/http.json +38 -0
- data/doc/json/ssh.json +16 -0
- data/helper/acl.rb +28 -17
- data/helper/file.rb +9 -8
- data/helper/http.rb +65 -65
- data/helper/ssh.rb +23 -12
- data/lib/planner.rb +9 -7
- data/lib/rblank.rb +1 -1
- data/lib/rmerge.rb +1 -1
- data/lib/runner.rb +36 -16
- data/lib/tdi.rb +20 -8
- data/lib/tdi/version.rb +2 -2
- data/lib/util.rb +110 -0
- data/tdi.gemspec +2 -0
- metadata +35 -2
data/lib/planner.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (C) 2013-
|
2
|
+
# Copyright (C) 2013-2015 Globo.com
|
3
3
|
#
|
4
4
|
|
5
5
|
# This file is part of TDI.
|
@@ -92,7 +92,7 @@ def plan_compiler(opts, plan)
|
|
92
92
|
compiled_plan = {}
|
93
93
|
|
94
94
|
# Role.
|
95
|
-
# Ex: {"
|
95
|
+
# Ex: {"app": {"desc": "...", "acl": {"domain1": {"port": 80}...}...}...}
|
96
96
|
plan.select { |key, val|
|
97
97
|
val.is_a?(Hash)
|
98
98
|
}.each_with_index do |(role_name, role_content), index|
|
@@ -169,7 +169,7 @@ def plan_inheriter(opts, plan)
|
|
169
169
|
inherited_plan = {}
|
170
170
|
|
171
171
|
# Role may inherit from role.
|
172
|
-
# Ex: {"
|
172
|
+
# Ex: {"app": {"desc": "...", "inherits": "other_role", "acl": {"domain1": {"port": 80}...}...}...}
|
173
173
|
plan.select { |key, val|
|
174
174
|
val.is_a?(Hash)
|
175
175
|
}.each_with_index do |(role_name, role_content), index|
|
@@ -243,10 +243,12 @@ def plan_filter(opts, plan)
|
|
243
243
|
filtered_plan = {}
|
244
244
|
flag_err = false
|
245
245
|
|
246
|
-
# Ex:
|
247
|
-
# Ex:
|
248
|
-
# Ex:
|
249
|
-
# Ex:
|
246
|
+
# Ex: -p admin
|
247
|
+
# Ex: -p admin::acl
|
248
|
+
# Ex: --plan app
|
249
|
+
# Ex: --plan app::acl
|
250
|
+
# Ex: --plan fe
|
251
|
+
# Ex: --plan admin::acl,app,fe
|
250
252
|
if opts.plan?
|
251
253
|
# Do filter.
|
252
254
|
puts 'Filtering following test plan from input file:'.cyan if opts[:verbose] > 0
|
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-2015 Globo.com
|
3
3
|
#
|
4
4
|
|
5
5
|
# This file is part of TDI.
|
@@ -19,6 +19,7 @@
|
|
19
19
|
|
20
20
|
require 'os'
|
21
21
|
require_relative 'tdi'
|
22
|
+
require_relative 'util'
|
22
23
|
|
23
24
|
# Run tests.
|
24
25
|
def runner(opts, filename, plan)
|
@@ -59,7 +60,6 @@ def runner(opts, filename, plan)
|
|
59
60
|
else
|
60
61
|
puts "* #{role_name.capitalize} - #{role_content['desc']}".cyan
|
61
62
|
end
|
62
|
-
# puts "Running tests for role: #{role_name}".cyan if opts[:verbose] > 0
|
63
63
|
puts "Total test plans to run for this role: #{total_plans}".cyan if opts[:verbose] > 1
|
64
64
|
|
65
65
|
# Test plan.
|
@@ -70,7 +70,6 @@ def runner(opts, filename, plan)
|
|
70
70
|
total_cases = plan_content.select { |key, val| val.is_a?(Hash) }.size
|
71
71
|
|
72
72
|
puts "* #{plan_name.upcase}".cyan
|
73
|
-
# puts "Test plan: #{role_name}::#{plan_name}".cyan if opts[:verbose] > 0
|
74
73
|
puts "Total test cases to run for this plan: #{total_cases}".cyan if opts[:verbose] > 1
|
75
74
|
|
76
75
|
if opts[:verbose] > 3
|
@@ -82,7 +81,7 @@ def runner(opts, filename, plan)
|
|
82
81
|
# Test plan content (test cases).
|
83
82
|
# Ex: {"domain1": {"port": 80}, "domain2": {"port": 80}...}
|
84
83
|
if tdiplan.respond_to?(plan_name)
|
85
|
-
tdiplan.send(plan_name, plan_content)
|
84
|
+
tdiplan.send(plan_name, role_name, plan_name, plan_content)
|
86
85
|
else
|
87
86
|
puts "Skipping not supported test plan type \"#{plan_name}\" for \"#{role_name}::#{plan_name}\".".yellow
|
88
87
|
tdiplan.skip = tdiplan.skip + total_cases
|
@@ -95,7 +94,40 @@ def runner(opts, filename, plan)
|
|
95
94
|
# Summary.
|
96
95
|
summary(opts, tdiplan)
|
97
96
|
|
97
|
+
# Report file.
|
98
|
+
report(opts, tdiplan)
|
99
|
+
|
98
100
|
# Shred.
|
101
|
+
shred(opts, filename, tdiplan)
|
102
|
+
|
103
|
+
puts 'Running tests... done.'.green if opts[:verbose] > 1
|
104
|
+
|
105
|
+
ret = tdiplan.plan_passed? ? 0 : 1
|
106
|
+
ret = 0 if opts.nofail?
|
107
|
+
ret
|
108
|
+
end
|
109
|
+
|
110
|
+
# Display test summary.
|
111
|
+
def summary(opts, tdiplan)
|
112
|
+
puts '=' * 79
|
113
|
+
puts "Total: #{tdiplan.total} | Skip: #{tdiplan.skip} | Pass: #{tdiplan.pass} | Warn: #{tdiplan.warn} | Fail: #{tdiplan.fail}"
|
114
|
+
end
|
115
|
+
|
116
|
+
# Generate report file.
|
117
|
+
def report(opts, tdiplan)
|
118
|
+
if opts[:verbose] > 2
|
119
|
+
puts 'Report:'.cyan
|
120
|
+
a_p tdiplan.report
|
121
|
+
end
|
122
|
+
|
123
|
+
if opts.reportfile?
|
124
|
+
puts "Generating report file: \"#{opts[:reportfile]}\"".cyan if opts[:verbose] > 1
|
125
|
+
File.open(opts[:reportfile], 'w') { |file| file.write(JSON.pretty_generate(tdiplan.report)) }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Remove tdi plan file.
|
130
|
+
def shred(opts, filename, tdiplan)
|
99
131
|
if opts.shred?
|
100
132
|
puts "Shreding and removing test plan file: \"#{filename}\"...".cyan if opts[:verbose] > 2
|
101
133
|
if OS.linux?
|
@@ -112,16 +144,4 @@ def runner(opts, filename, plan)
|
|
112
144
|
puts "ERR: Shreding and removing test plan file: \"#{filename}\".".light_magenta
|
113
145
|
end
|
114
146
|
end
|
115
|
-
|
116
|
-
puts 'Running tests... done.'.green if opts[:verbose] > 1
|
117
|
-
|
118
|
-
ret = tdiplan.plan_passed? ? 0 : 1
|
119
|
-
ret = 0 if opts.nofail?
|
120
|
-
ret
|
121
|
-
end
|
122
|
-
|
123
|
-
# Display test summary.
|
124
|
-
def summary(opts, tdiplan)
|
125
|
-
puts '=' * 79
|
126
|
-
puts "Total: #{tdiplan.total} | Skip: #{tdiplan.skip} | Pass: #{tdiplan.pass} | Warn: #{tdiplan.warn} | Fail: #{tdiplan.fail}"
|
127
147
|
end
|
data/lib/tdi.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (C) 2013-
|
2
|
+
# Copyright (C) 2013-2015 Globo.com
|
3
3
|
#
|
4
4
|
|
5
5
|
# This file is part of TDI.
|
@@ -17,13 +17,14 @@
|
|
17
17
|
# You should have received a copy of the GNU General Public License
|
18
18
|
# along with TDI. If not, see <http://www.gnu.org/licenses/>.
|
19
19
|
|
20
|
+
require 'socket'
|
20
21
|
require_relative 'tdi/version'
|
21
22
|
|
22
23
|
class TDI
|
23
24
|
attr_accessor :plan_passed, :case_passed
|
24
25
|
alias :plan_passed? :plan_passed
|
25
26
|
alias :case_passed? :case_passed
|
26
|
-
attr_accessor :skip, :pass, :warn, :fail
|
27
|
+
attr_accessor :skip, :pass, :warn, :fail, :report
|
27
28
|
|
28
29
|
def initialize
|
29
30
|
@plan_passed = true
|
@@ -32,21 +33,32 @@ class TDI
|
|
32
33
|
@pass = 0
|
33
34
|
@warn = 0
|
34
35
|
@fail = 0
|
36
|
+
@report = {hostname: Socket.gethostname}
|
35
37
|
end
|
36
38
|
|
37
|
-
def
|
39
|
+
def update_report(status, role_name, plan_name, res_dict)
|
40
|
+
@report[role_name] = {} unless @report.has_key?(role_name)
|
41
|
+
@report[role_name][plan_name] = [] unless @report[role_name].has_key?(plan_name)
|
42
|
+
res_dict[:status] = status
|
43
|
+
@report[role_name][plan_name] << res_dict
|
44
|
+
end
|
45
|
+
|
46
|
+
def success(role_name, plan_name, res_msg, res_dict)
|
47
|
+
update_report(:pass, role_name, plan_name, res_dict)
|
38
48
|
# I like the seventies.
|
39
|
-
printf("%-70s [ %s ]\n",
|
49
|
+
printf("%-70s [ %s ]\n", res_msg, 'PASS'.light_green)
|
40
50
|
@pass += 1
|
41
51
|
end
|
42
52
|
|
43
|
-
def warning(
|
44
|
-
|
53
|
+
def warning(role_name, plan_name, res_msg, res_dict)
|
54
|
+
update_report(:warn, role_name, plan_name, res_dict)
|
55
|
+
printf("%-70s [ %s ]\n", res_msg, 'WARN'.light_yellow)
|
45
56
|
@warn += 1
|
46
57
|
end
|
47
58
|
|
48
|
-
def failure(
|
49
|
-
|
59
|
+
def failure(role_name, plan_name, res_msg, res_dict)
|
60
|
+
update_report(:fail, role_name, plan_name, res_dict)
|
61
|
+
printf("%-70s [ %s ]\n", res_msg, 'FAIL'.light_red)
|
50
62
|
@plan_passed = false
|
51
63
|
@case_passed = false
|
52
64
|
@fail += 1
|
data/lib/tdi/version.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (C) 2013-
|
2
|
+
# Copyright (C) 2013-2015 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.1.
|
21
|
+
VERSION = '0.1.7'
|
22
22
|
end
|
data/lib/util.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2013-2015 Globo.com
|
3
|
+
#
|
4
|
+
|
5
|
+
# This file is part of TDI.
|
6
|
+
|
7
|
+
# TDI is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
|
12
|
+
# TDI is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with TDI. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
require 'awesome_print'
|
21
|
+
require 'socket'
|
22
|
+
require 'ipaddress'
|
23
|
+
require 'resolv'
|
24
|
+
|
25
|
+
# Awesome Print config.
|
26
|
+
def a_p(obj)
|
27
|
+
ap obj, {
|
28
|
+
indent: 2,
|
29
|
+
index: false,
|
30
|
+
color: {
|
31
|
+
args: :pale,
|
32
|
+
array: :white,
|
33
|
+
bigdecimal: :blue,
|
34
|
+
class: :yellow,
|
35
|
+
date: :greenish,
|
36
|
+
falseclass: :red,
|
37
|
+
fixnum: :blue,
|
38
|
+
float: :blue,
|
39
|
+
hash: :blue,
|
40
|
+
keyword: :cyan,
|
41
|
+
method: :purpleish,
|
42
|
+
nilclass: :red,
|
43
|
+
rational: :blue,
|
44
|
+
string: :green,
|
45
|
+
struct: :pale,
|
46
|
+
symbol: :cyanish,
|
47
|
+
time: :greenish,
|
48
|
+
trueclass: :green,
|
49
|
+
variable: :cyanish,
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
# Return a list of local networks and it's details.
|
55
|
+
def local_networks
|
56
|
+
Socket.getifaddrs.each.select { |ifaddr| ifaddr.addr.ipv4? and ! ifaddr.name.start_with?('lo') }.
|
57
|
+
map do |ifaddr|
|
58
|
+
ip = IPAddress::IPv4.new("#{ifaddr.addr.ip_address}/#{ifaddr.netmask.ip_address}")
|
59
|
+
{
|
60
|
+
interface: ifaddr.name,
|
61
|
+
net: ip.network.to_string,
|
62
|
+
network: ip.network.address,
|
63
|
+
netmask: ip.netmask,
|
64
|
+
prefix: ip.prefix,
|
65
|
+
broadcast: ip.broadcast.address,
|
66
|
+
ipv4: ip.address,
|
67
|
+
}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Return the origin network to be used when trying to connect to a remote
|
72
|
+
# service.
|
73
|
+
#
|
74
|
+
# Stolen from:
|
75
|
+
#
|
76
|
+
# https://coderrr.wordpress.com/2008/05/28/get-your-local-ip-address/
|
77
|
+
#
|
78
|
+
# The above code does NOT make a connection or send any packets. Since UDP is a
|
79
|
+
# stateless protocol connect() merely makes a system call which figures out how
|
80
|
+
# to route the packets based on the address and what interface (and therefore IP
|
81
|
+
# address) it should bind to. addr() returns an array containing the family
|
82
|
+
# (AF_INET), local port, and local address (which is what we want) of the socket.
|
83
|
+
# This is a good alternative to `ifconfig`/`ipconfig` solutions because it
|
84
|
+
# doesn’t spawn a shell and it works the same on all systems.
|
85
|
+
def origin_network(remote)
|
86
|
+
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true
|
87
|
+
|
88
|
+
host = remote # host (name or IP)
|
89
|
+
if IPAddress.valid?(remote)
|
90
|
+
addr = remote # use address (already IP)
|
91
|
+
else
|
92
|
+
addr = Resolv.getaddress(remote) rescue nil # get address (resolve name to IP)
|
93
|
+
end
|
94
|
+
|
95
|
+
UDPSocket.open do |s|
|
96
|
+
begin
|
97
|
+
s.connect(remote, 1)
|
98
|
+
res = {from: local_networks.each.select { |locnet| locnet[:ipv4].eql?(s.addr.last) }.first}
|
99
|
+
rescue
|
100
|
+
res = {from: nil}
|
101
|
+
end
|
102
|
+
|
103
|
+
res[:to] = {host: host, addr: addr}
|
104
|
+
|
105
|
+
return res
|
106
|
+
end
|
107
|
+
|
108
|
+
ensure
|
109
|
+
Socket.do_not_reverse_lookup = orig
|
110
|
+
end
|
data/tdi.gemspec
CHANGED
@@ -29,4 +29,6 @@ validating your deployed infrastructure and external dependencies.)
|
|
29
29
|
spec.add_runtime_dependency 'colorize'
|
30
30
|
spec.add_runtime_dependency 'timeout', '0.0.0'
|
31
31
|
spec.add_runtime_dependency 'net-ssh'
|
32
|
+
spec.add_runtime_dependency 'awesome_print'
|
33
|
+
spec.add_runtime_dependency 'ipaddress'
|
32
34
|
end
|
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.1.
|
4
|
+
version: 0.1.7
|
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: 2015-
|
13
|
+
date: 2015-07-23 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -124,6 +124,34 @@ dependencies:
|
|
124
124
|
- - ">="
|
125
125
|
- !ruby/object:Gem::Version
|
126
126
|
version: '0'
|
127
|
+
- !ruby/object:Gem::Dependency
|
128
|
+
name: awesome_print
|
129
|
+
requirement: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :runtime
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
- !ruby/object:Gem::Dependency
|
142
|
+
name: ipaddress
|
143
|
+
requirement: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
type: :runtime
|
149
|
+
prerelease: false
|
150
|
+
version_requirements: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
127
155
|
description: |-
|
128
156
|
Test Driven Infrastructure acceptance helpers for
|
129
157
|
validating your deployed infrastructure and external dependencies.
|
@@ -145,6 +173,10 @@ files:
|
|
145
173
|
- README.md
|
146
174
|
- Rakefile
|
147
175
|
- bin/tdi
|
176
|
+
- doc/json/acl.json
|
177
|
+
- doc/json/file.json
|
178
|
+
- doc/json/http.json
|
179
|
+
- doc/json/ssh.json
|
148
180
|
- helper/acl.rb
|
149
181
|
- helper/file.rb
|
150
182
|
- helper/http.rb
|
@@ -155,6 +187,7 @@ files:
|
|
155
187
|
- lib/runner.rb
|
156
188
|
- lib/tdi.rb
|
157
189
|
- lib/tdi/version.rb
|
190
|
+
- lib/util.rb
|
158
191
|
- tdi.gemspec
|
159
192
|
homepage: https://github.com/globocom/tdi
|
160
193
|
licenses:
|