tdi 0.2.3 → 0.2.4

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 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