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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5f667efd818c32c1de975332ee09be3747289808
4
- data.tar.gz: 0cf62be71c8cfec8340700096c3e6dd971973c17
3
+ metadata.gz: 244daf4e0163efb156bc527c7b1f47d0a8f0aaa5
4
+ data.tar.gz: b6d43ee88862a8e6a51423b697cf8c53f4c3145d
5
5
  SHA512:
6
- metadata.gz: cb7abf813d5c473abe06ca6ac4b180fc558bdaf2ff79c0132723b4593bca85acc69770a25b12ce29fee99fc23b73c274d1e88a75510b728a44f27d7cd793b06b
7
- data.tar.gz: 35b2f359c3a5ad88ff0ed98c233bcc28f33a90f93b1cbed45716774ccb5f07978ed722cf9f0ee08e6b3d4aa1a2d102859a007b99b2081e108673508ff3524d69
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 => {'globo.com' => {:code => 301}},
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': {'globo.com': {'code': 301}},
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
- ### Validate
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 <rogerio.schneider@corp.globo.com>
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-2015 Globo.com
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.'
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2013-2015 Globo.com
2
+ # Copyright (C) 2013-2017 Globo.com
3
3
  #
4
4
 
5
5
  # This file is part of TDI.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2013-2015 Globo.com
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, if type is directory (remove tempfile).
68
- FileUtils.rm(filename) if type.eql?('directory') rescue nil
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
- @flag_success = File.symlink?(path)
84
- @flag_success = File.exist?(path) if @flag_success
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
- mount_p = Filesystem.mount_point(path)
92
- mount_t = Filesystem.mounts.select { |mount| mount.mount_point.eql?(mount_p) }.first.mount_type
93
-
94
- case location
95
- when 'local'
96
- @flag_success = false if REMOTE_FS_LIST.include?(mount_t)
97
- when 'nfs'
98
- @flag_success = false unless mount_t.eql?('nfs')
99
- else
100
- puts "ERR: Invalid file plan format \"#{location}\". Location must be \"local\" or \"nfs\".".light_magenta
101
- exit 1
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.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2013-2015 Globo.com
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
- res_dict = {url: case_name, net: origin_network(host)}
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)
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2013-2015 Globo.com
2
+ # Copyright (C) 2013-2017 Globo.com
3
3
  #
4
4
 
5
5
  # This file is part of TDI.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2013-2015 Globo.com
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) or val.is_a?(Hash) }
136
- new_case_content.merge!(plan_content.reject { |key, val| UNMERGEABLE_KEY_LIST.include?(key) or val.is_a?(Hash) })
137
- new_case_content.merge!(case_content.reject { |key, val| UNMERGEABLE_KEY_LIST.include?(key) or val.is_a?(Hash) })
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? or i_plan_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
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2013-2015 Globo.com
2
+ # Copyright (C) 2013-2017 Globo.com
3
3
  #
4
4
 
5
5
  # This file is part of TDI.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2013-2015 Globo.com
2
+ # Copyright (C) 2013-2017 Globo.com
3
3
  #
4
4
 
5
5
  # This file is part of TDI.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2013-2015 Globo.com
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) or role_content['notest'].eql?('true')
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) or role_content['notest'].eql?('true')
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 = tdiplan.plan_passed? ? 0 : 1
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-2015 Globo.com
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
 
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2013-2015 Globo.com
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.3'
21
+ VERSION = '0.2.4'
22
22
  end
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (C) 2013-2015 Globo.com
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? and ! ifaddr.name.start_with?('lo') }.
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.3
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 00:00:00.000000000 Z
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