riemann-tools 0.2.13 → 1.1.0

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.
Files changed (91) hide show
  1. checksums.yaml +5 -5
  2. data/.docker/Dockerfile +7 -0
  3. data/.docker/publish.sh +35 -0
  4. data/.github/dependabot.yml +11 -0
  5. data/.github/workflows/ci.yml +42 -0
  6. data/.github/workflows/codeql-analysis.yml +72 -0
  7. data/.gitignore +6 -0
  8. data/.rspec +2 -0
  9. data/.rubocop.yml +32 -0
  10. data/.travis.yml +31 -0
  11. data/CHANGELOG.md +422 -0
  12. data/Gemfile +6 -0
  13. data/ISSUE_TEMPLATE.md +15 -0
  14. data/README.markdown +14 -15
  15. data/Rakefile +23 -0
  16. data/SECURITY.md +42 -0
  17. data/bin/riemann-apache-status +92 -77
  18. data/bin/riemann-bench +54 -48
  19. data/bin/riemann-cloudant +44 -39
  20. data/bin/riemann-consul +82 -75
  21. data/bin/riemann-dir-files-count +53 -46
  22. data/bin/riemann-dir-space +53 -46
  23. data/bin/riemann-diskstats +78 -74
  24. data/bin/riemann-fd +68 -47
  25. data/bin/riemann-freeswitch +108 -102
  26. data/bin/riemann-haproxy +46 -39
  27. data/bin/riemann-health +4 -335
  28. data/bin/riemann-kvminstance +18 -12
  29. data/bin/riemann-memcached +35 -28
  30. data/bin/riemann-net +4 -103
  31. data/bin/riemann-nginx-status +74 -66
  32. data/bin/riemann-ntp +4 -32
  33. data/bin/riemann-portcheck +40 -30
  34. data/bin/riemann-proc +96 -89
  35. data/bin/riemann-varnish +51 -44
  36. data/bin/riemann-zookeeper +38 -33
  37. data/lib/riemann/tools/health.rb +347 -0
  38. data/lib/riemann/tools/net.rb +104 -0
  39. data/lib/riemann/tools/ntp.rb +41 -0
  40. data/lib/riemann/tools/utils.rb +17 -0
  41. data/lib/riemann/tools/version.rb +7 -0
  42. data/lib/riemann/tools.rb +40 -33
  43. data/riemann-tools.gemspec +42 -0
  44. data/tools/riemann-aws/LICENSE +21 -0
  45. data/tools/riemann-aws/README.md +54 -0
  46. data/tools/riemann-aws/Rakefile +37 -0
  47. data/tools/riemann-aws/bin/riemann-aws-billing +93 -0
  48. data/tools/riemann-aws/bin/riemann-aws-rds-status +68 -0
  49. data/tools/riemann-aws/bin/riemann-aws-sqs-status +50 -0
  50. data/tools/riemann-aws/bin/riemann-aws-status +83 -0
  51. data/tools/riemann-aws/bin/riemann-elb-metrics +168 -0
  52. data/tools/riemann-aws/bin/riemann-s3-list +87 -0
  53. data/tools/riemann-aws/bin/riemann-s3-status +102 -0
  54. data/tools/riemann-chronos/LICENSE +21 -0
  55. data/tools/riemann-chronos/README.md +10 -0
  56. data/tools/riemann-chronos/Rakefile +37 -0
  57. data/tools/riemann-chronos/bin/riemann-chronos +161 -0
  58. data/tools/riemann-docker/LICENSE +21 -0
  59. data/tools/riemann-docker/README.md +10 -0
  60. data/tools/riemann-docker/Rakefile +36 -0
  61. data/tools/riemann-docker/bin/riemann-docker +206 -0
  62. data/tools/riemann-elasticsearch/LICENSE +21 -0
  63. data/tools/riemann-elasticsearch/README.md +10 -0
  64. data/tools/riemann-elasticsearch/Rakefile +37 -0
  65. data/tools/riemann-elasticsearch/bin/riemann-elasticsearch +174 -0
  66. data/tools/riemann-marathon/LICENSE +21 -0
  67. data/tools/riemann-marathon/README.md +10 -0
  68. data/tools/riemann-marathon/Rakefile +37 -0
  69. data/tools/riemann-marathon/bin/riemann-marathon +163 -0
  70. data/tools/riemann-mesos/LICENSE +21 -0
  71. data/tools/riemann-mesos/README.md +10 -0
  72. data/tools/riemann-mesos/Rakefile +37 -0
  73. data/tools/riemann-mesos/bin/riemann-mesos +146 -0
  74. data/tools/riemann-munin/LICENSE +21 -0
  75. data/tools/riemann-munin/README.md +10 -0
  76. data/tools/riemann-munin/Rakefile +36 -0
  77. data/tools/riemann-munin/bin/riemann-munin +43 -0
  78. data/tools/riemann-rabbitmq/LICENSE +21 -0
  79. data/tools/riemann-rabbitmq/README.md +10 -0
  80. data/tools/riemann-rabbitmq/Rakefile +37 -0
  81. data/tools/riemann-rabbitmq/bin/riemann-rabbitmq +273 -0
  82. data/tools/riemann-riak/LICENSE +21 -0
  83. data/tools/riemann-riak/README.md +10 -0
  84. data/tools/riemann-riak/Rakefile +36 -0
  85. data/tools/riemann-riak/bin/riemann-riak +323 -0
  86. data/tools/riemann-riak/bin/riemann-riak-keys +13 -0
  87. data/tools/riemann-riak/bin/riemann-riak-ring +9 -0
  88. data/tools/riemann-riak/riak_status/key_count.erl +13 -0
  89. data/tools/riemann-riak/riak_status/riak_status.rb +152 -0
  90. data/tools/riemann-riak/riak_status/ringready.erl +9 -0
  91. metadata +195 -34
@@ -1,53 +1,60 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- # Gets the number of files present on a directory and submits it to riemann
4
-
5
- require File.expand_path('../../lib/riemann/tools', __FILE__)
6
-
7
- class Riemann::Tools::DirFilesCount
8
- include Riemann::Tools
4
+ Process.setproctitle($PROGRAM_NAME)
9
5
 
10
- opt :directory, "", :default => '/var/log'
11
- opt :service_prefix, "The first part of the service name, before the directory path", :default => "dir-files-count"
12
- opt :warning, "Dir files number warning threshold", :type => Integer
13
- opt :critical, "Dir files number critical threshold", :type => Integer
14
- opt :alert_on_missing, "Send a critical metric if the directory is missing?", :default => true
15
-
16
- def initialize
17
- @dir = opts.fetch(:directory)
18
- @service_prefix = opts.fetch(:service_prefix)
19
- @warning = opts.fetch(:warning, nil)
20
- @critical = opts.fetch(:critical, nil)
21
- @alert_on_missing = opts.fetch(:alert_on_missing)
22
- end
23
-
24
- def tick
25
- if Dir.exists?(@dir)
26
- metric = Dir.entries(@dir).size - 2
27
- report(
28
- :service => "#{@service_prefix} #{@dir}",
29
- :metric => metric,
30
- :state => state(metric),
31
- :tags => ['dir_files_count']
32
- )
33
- elsif @alert_on_missing
34
- report(
35
- :service => "#{@service_prefix} #{@dir} missing",
36
- :description => "#{@service_prefix} #{@dir} does not exist",
37
- :metric => metric,
38
- :state => 'critical',
39
- :tags => ['dir_files_count']
40
- )
41
- end
42
- end
6
+ # Gets the number of files present on a directory and submits it to riemann
43
7
 
44
- def state(metric)
45
- if @critical && metric > @critical
46
- 'critical'
47
- elsif @warning && metric > @warning
48
- 'warning'
49
- else
50
- 'ok'
8
+ require File.expand_path('../lib/riemann/tools', __dir__)
9
+
10
+ module Riemann
11
+ module Tools
12
+ class DirFilesCount
13
+ include Riemann::Tools
14
+
15
+ opt :directory, '', default: '/var/log'
16
+ opt :service_prefix, 'The first part of the service name, before the directory path', default: 'dir-files-count'
17
+ opt :warning, 'Dir files number warning threshold', type: Integer
18
+ opt :critical, 'Dir files number critical threshold', type: Integer
19
+ opt :alert_on_missing, 'Send a critical metric if the directory is missing?', default: true
20
+
21
+ def initialize
22
+ @dir = opts.fetch(:directory)
23
+ @service_prefix = opts.fetch(:service_prefix)
24
+ @warning = opts.fetch(:warning, nil)
25
+ @critical = opts.fetch(:critical, nil)
26
+ @alert_on_missing = opts.fetch(:alert_on_missing)
27
+ end
28
+
29
+ def tick
30
+ if Dir.exist?(@dir)
31
+ metric = Dir.entries(@dir).size - 2
32
+ report(
33
+ service: "#{@service_prefix} #{@dir}",
34
+ metric: metric,
35
+ state: state(metric),
36
+ tags: ['dir_files_count'],
37
+ )
38
+ elsif @alert_on_missing
39
+ report(
40
+ service: "#{@service_prefix} #{@dir} missing",
41
+ description: "#{@service_prefix} #{@dir} does not exist",
42
+ metric: metric,
43
+ state: 'critical',
44
+ tags: ['dir_files_count'],
45
+ )
46
+ end
47
+ end
48
+
49
+ def state(metric)
50
+ if @critical && metric > @critical
51
+ 'critical'
52
+ elsif @warning && metric > @warning
53
+ 'warning'
54
+ else
55
+ 'ok'
56
+ end
57
+ end
51
58
  end
52
59
  end
53
60
  end
@@ -1,53 +1,60 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- # Gathers the space used by a directory and submits it to riemann
4
-
5
- require File.expand_path('../../lib/riemann/tools', __FILE__)
6
-
7
- class Riemann::Tools::DirSpace
8
- include Riemann::Tools
4
+ Process.setproctitle($PROGRAM_NAME)
9
5
 
10
- opt :directory, "", :default => '/var/log'
11
- opt :service_prefix, "The first part of the service name, before the directory path", :default => "dir-space"
12
- opt :warning, "Dir space warning threshold (in bytes)", :type => Integer
13
- opt :critical, "Dir space critical threshold (in bytes)", :type => Integer
14
- opt :alert_on_missing, "Send a critical metric if the directory is missing?", :default => true
15
-
16
- def initialize
17
- @dir = opts.fetch(:directory)
18
- @service_prefix = opts.fetch(:service_prefix)
19
- @warning = opts.fetch(:warning, nil)
20
- @critical = opts.fetch(:critical, nil)
21
- @alert_on_missing = opts.fetch(:alert_on_missing)
22
- end
23
-
24
- def tick
25
- if Dir.exists?(@dir)
26
- metric = `du '#{@dir}'`.lines.to_a.last.split("\t")[0].to_i
27
- report(
28
- :service => "#{@service_prefix} #{@dir}",
29
- :metric => metric,
30
- :state => state(metric),
31
- :tags => ['dir_space']
32
- )
33
- elsif @alert_on_missing
34
- report(
35
- :service => "#{@service_prefix} #{@dir} missing",
36
- :description => "#{@service_prefix} #{@dir} does not exist",
37
- :metric => metric,
38
- :state => 'critical',
39
- :tags => ['dir_space']
40
- )
41
- end
42
- end
6
+ # Gathers the space used by a directory and submits it to riemann
43
7
 
44
- def state(metric)
45
- if @critical && metric > @critical
46
- 'critical'
47
- elsif @warning && metric > @warning
48
- 'warning'
49
- else
50
- 'ok'
8
+ require File.expand_path('../lib/riemann/tools', __dir__)
9
+
10
+ module Riemann
11
+ module Tools
12
+ class DirSpace
13
+ include Riemann::Tools
14
+
15
+ opt :directory, '', default: '/var/log'
16
+ opt :service_prefix, 'The first part of the service name, before the directory path', default: 'dir-space'
17
+ opt :warning, 'Dir space warning threshold (in bytes)', type: Integer
18
+ opt :critical, 'Dir space critical threshold (in bytes)', type: Integer
19
+ opt :alert_on_missing, 'Send a critical metric if the directory is missing?', default: true
20
+
21
+ def initialize
22
+ @dir = opts.fetch(:directory)
23
+ @service_prefix = opts.fetch(:service_prefix)
24
+ @warning = opts.fetch(:warning, nil)
25
+ @critical = opts.fetch(:critical, nil)
26
+ @alert_on_missing = opts.fetch(:alert_on_missing)
27
+ end
28
+
29
+ def tick
30
+ if Dir.exist?(@dir)
31
+ metric = `du '#{@dir}'`.lines.to_a.last.split("\t")[0].to_i
32
+ report(
33
+ service: "#{@service_prefix} #{@dir}",
34
+ metric: metric,
35
+ state: state(metric),
36
+ tags: ['dir_space'],
37
+ )
38
+ elsif @alert_on_missing
39
+ report(
40
+ service: "#{@service_prefix} #{@dir} missing",
41
+ description: "#{@service_prefix} #{@dir} does not exist",
42
+ metric: metric,
43
+ state: 'critical',
44
+ tags: ['dir_space'],
45
+ )
46
+ end
47
+ end
48
+
49
+ def state(metric)
50
+ if @critical && metric > @critical
51
+ 'critical'
52
+ elsif @warning && metric > @warning
53
+ 'warning'
54
+ else
55
+ 'ok'
56
+ end
57
+ end
51
58
  end
52
59
  end
53
60
  end
@@ -1,94 +1,98 @@
1
1
  #!/usr/bin/env ruby
2
- #
2
+ # frozen_string_literal: true
3
+
4
+ Process.setproctitle($PROGRAM_NAME)
3
5
  require 'rubygems'
4
6
  require 'riemann/tools'
5
7
 
6
- class Riemann::Tools::Diskstats
7
- include Riemann::Tools
8
+ module Riemann
9
+ module Tools
10
+ class Diskstats
11
+ include Riemann::Tools
8
12
 
9
- opt :devices, "Devices to monitor", :type => :strings, :default => nil
10
- opt :ignore_devices, "Devices to ignore", :type => :strings, :default =>nil
13
+ opt :devices, 'Devices to monitor', type: :strings, default: nil
14
+ opt :ignore_devices, 'Devices to ignore', type: :strings, default: nil
11
15
 
12
- def initialize
13
- @old_state = nil
14
- end
16
+ def initialize
17
+ @old_state = nil
18
+ end
15
19
 
16
- def state
17
- f = File.read('/proc/diskstats')
18
- state = f.split("\n").reject { |d| d =~ /(ram|loop)/ }.inject({}) do |s, line|
19
- if line =~ /^(?:\s+\d+){2}\s+([\w\d\-]+) (.*)$/
20
- dev = $1
21
-
22
- ['reads reqs',
23
- 'reads merged',
24
- 'reads sector',
25
- 'reads time',
26
- 'writes reqs',
27
- 'writes merged',
28
- 'writes sector',
29
- 'writes time',
30
- 'io reqs',
31
- 'io time',
32
- 'io weighted'
33
- ].map do |service|
34
- "#{dev} #{service}"
35
- end.zip(
36
- $2.split(/\s+/).map { |str| str.to_i }
37
- ).each do |service, value|
38
- s[service] = value
20
+ def state
21
+ f = File.read('/proc/diskstats')
22
+ state = f.split("\n").reject { |d| d =~ /(ram|loop)/ }.each_with_object({}) do |line, s|
23
+ next unless line =~ /^(?:\s+\d+){2}\s+([\w\d\-]+) (.*)$/
24
+
25
+ dev = Regexp.last_match(1)
26
+
27
+ ['reads reqs',
28
+ 'reads merged',
29
+ 'reads sector',
30
+ 'reads time',
31
+ 'writes reqs',
32
+ 'writes merged',
33
+ 'writes sector',
34
+ 'writes time',
35
+ 'io reqs',
36
+ 'io time',
37
+ 'io weighted',].map do |service|
38
+ "#{dev} #{service}"
39
+ end.zip( # rubocop:disable Style/MultilineBlockChain
40
+ Regexp.last_match(2).split(/\s+/).map(&:to_i),
41
+ ).each do |service, value|
42
+ s[service] = value
43
+ end
39
44
  end
40
- end
41
45
 
42
- s
43
- end
46
+ # Filter interfaces
47
+ if (is = opts[:devices])
48
+ state = state.select do |service, _value|
49
+ is.include? service.split(' ').first
50
+ end
51
+ end
44
52
 
45
- # Filter interfaces
46
- if is = opts[:devices]
47
- state = state.select do |service, value|
48
- is.include? service.split(' ').first
49
- end
50
- end
53
+ if (ign = opts[:ignore_devices])
54
+ state = state.reject do |service, _value|
55
+ ign.include? service.split(' ').first
56
+ end
57
+ end
51
58
 
52
- if ign = opts[:ignore_devices]
53
- state = state.reject do |service, value|
54
- ign.include? service.split(' ').first
59
+ state
55
60
  end
56
- end
57
61
 
58
- state
59
- end
60
-
61
- def tick
62
- state = self.state
63
-
64
- if @old_state
65
- state.each do |service, metric|
66
-
67
- if service =~ /io reqs$/
68
- report(
69
- :service => "diskstats " + service,
70
- :metric => metric,
71
- :state => "ok"
72
- )
73
- else
74
- delta = metric - @old_state[service]
75
-
76
- report(
77
- :service => "diskstats " + service,
78
- :metric => (delta.to_f / opts[:interval]),
79
- :state => "ok"
80
- )
62
+ def tick
63
+ state = self.state
64
+
65
+ if @old_state
66
+ state.each do |service, metric|
67
+ if service =~ /io reqs$/
68
+ report(
69
+ service: "diskstats #{service}",
70
+ metric: metric,
71
+ state: 'ok',
72
+ )
73
+ else
74
+ delta = metric - @old_state[service]
75
+
76
+ report(
77
+ service: "diskstats #{service}",
78
+ metric: (delta.to_f / opts[:interval]),
79
+ state: 'ok',
80
+ )
81
+ end
82
+
83
+ next unless service =~ /io time$/
84
+
85
+ report(
86
+ service: "diskstats #{service.gsub(/time/, 'util')}",
87
+ metric: (delta.to_f / (opts[:interval] * 1000)),
88
+ state: 'ok',
89
+ )
90
+ end
81
91
  end
82
92
 
83
- if service =~ /io time$/
84
- report(:service => "diskstats " + service.gsub(/time/, 'util'),
85
- :metric => (delta.to_f / (opts[:interval]*1000)),
86
- :state => "ok")
87
- end
93
+ @old_state = state
88
94
  end
89
95
  end
90
-
91
- @old_state = state
92
96
  end
93
97
  end
94
98
 
data/bin/riemann-fd CHANGED
@@ -1,65 +1,86 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ Process.setproctitle($PROGRAM_NAME)
2
5
 
3
6
  # Reports current file descriptor use to riemann.
4
7
  # By default reports the total system fd usage, can also report usage of individual processes
5
8
 
6
- require File.expand_path('../../lib/riemann/tools', __FILE__)
9
+ require File.expand_path('../lib/riemann/tools', __dir__)
7
10
 
8
- class Riemann::Tools::Health
9
- include Riemann::Tools
11
+ module Riemann
12
+ module Tools
13
+ class Health
14
+ include Riemann::Tools
10
15
 
11
- opt :fd_sys_warning, "open file descriptor threshold for system", :default => 800
12
- opt :fd_sys_critical, "open file descriptor critical threshold for system", :default => 900
13
- opt :fd_proc_warning, "open file descriptor threshold for process", :default => 800
14
- opt :fd_proc_critical, "open file descriptor critical threshold for process", :default => 900
15
- opt :processes, "list of processes to measure fd usage in addition to system total", :type => :ints
16
+ opt :fd_sys_warning, 'open file descriptor threshold for system', default: 800
17
+ opt :fd_sys_critical, 'open file descriptor critical threshold for system', default: 900
18
+ opt :fd_proc_warning, 'open file descriptor threshold for process', default: 800
19
+ opt :fd_proc_critical, 'open file descriptor critical threshold for process', default: 900
20
+ opt :processes, 'list of processes to measure fd usage in addition to system total', type: :ints
16
21
 
17
- def initialize
18
- @limits = {
19
- :fd => {:critical => opts[:fd_sys_critical], :warning => opts[:fd_sys_warning]},
20
- :process => {:critical => opts[:fd_proc_critical], :warning => opts[:fd_proc_warning]},
21
- }
22
- ostype = `uname -s`.chomp.downcase
23
- puts "WARNING: OS '#{ostype}' not explicitly supported. Falling back to Linux" unless ostype == "linux"
24
- @fd = method :linux_fd
25
- end
22
+ def initialize
23
+ @limits = {
24
+ fd: { critical: opts[:fd_sys_critical], warning: opts[:fd_sys_warning] },
25
+ process: { critical: opts[:fd_proc_critical], warning: opts[:fd_proc_warning] },
26
+ }
27
+ ostype = `uname -s`.chomp.downcase
28
+ case ostype
29
+ when 'freebsd'
30
+ @fd = method :freebsd_fd
31
+ else
32
+ puts "WARNING: OS '#{ostype}' not explicitly supported. Falling back to Linux" unless ostype == 'linux'
33
+ @fd = method :linux_fd
34
+ end
35
+ end
26
36
 
27
- def alert(service, state, metric, description)
28
- report(
29
- :service => service.to_s,
30
- :state => state.to_s,
31
- :metric => metric.to_f,
32
- :description => description
33
- )
34
- end
37
+ def alert(service, state, metric, description)
38
+ report(
39
+ service: service.to_s,
40
+ state: state.to_s,
41
+ metric: metric.to_f,
42
+ description: description,
43
+ )
44
+ end
35
45
 
36
- def linux_fd
37
- sys_used = Integer(`lsof | wc -l`)
38
- if sys_used > @limits[:fd][:critical]
39
- alert "fd sys", :critical, sys_used, "system is using #{sys_used} fds"
40
- elsif sys_used > @limits[:fd][:warning]
41
- alert "fd sys", :warning, sys_used, "system is using #{sys_used} fds"
42
- else
43
- alert "fd sys", :ok, sys_used, "system is using #{sys_used} fds"
44
- end
46
+ def freebsd_fd
47
+ sys_used = Integer(`sysctl -n kern.openfiles`)
48
+ if sys_used > @limits[:fd][:critical]
49
+ alert 'fd sys', :critical, sys_used, "system is using #{sys_used} fds"
50
+ elsif sys_used > @limits[:fd][:warning]
51
+ alert 'fd sys', :warning, sys_used, "system is using #{sys_used} fds"
52
+ else
53
+ alert 'fd sys', :ok, sys_used, "system is using #{sys_used} fds"
54
+ end
55
+ end
45
56
 
46
- unless opts[:processes].nil?
47
- opts[:processes].each do |process|
48
- used = Integer(`lsof -p #{process} | wc -l`)
49
- name, pid = `ps axo comm,pid | grep -w #{process}`.split
50
- if used > @limits[:process][:critical]
51
- alert "fd #{name} #{process}", :critical, used, "process #{name} #{process} is using #{used} fds"
52
- elsif used > @limits[:process][:warning]
53
- alert "fd #{name} #{process}", :warning, used, "process #{name} #{process} is using #{used} fds"
57
+ def linux_fd
58
+ sys_used = Integer(`lsof | wc -l`)
59
+ if sys_used > @limits[:fd][:critical]
60
+ alert 'fd sys', :critical, sys_used, "system is using #{sys_used} fds"
61
+ elsif sys_used > @limits[:fd][:warning]
62
+ alert 'fd sys', :warning, sys_used, "system is using #{sys_used} fds"
54
63
  else
55
- alert "fd #{name} #{process}", :ok, used, "process #{name} #{process} is using #{used} fds"
64
+ alert 'fd sys', :ok, sys_used, "system is using #{sys_used} fds"
65
+ end
66
+
67
+ opts[:processes]&.each do |process|
68
+ used = Integer(`lsof -p #{process} | wc -l`)
69
+ name, _pid = `ps axo comm,pid | grep -w #{process}`.split
70
+ if used > @limits[:process][:critical]
71
+ alert "fd #{name} #{process}", :critical, used, "process #{name} #{process} is using #{used} fds"
72
+ elsif used > @limits[:process][:warning]
73
+ alert "fd #{name} #{process}", :warning, used, "process #{name} #{process} is using #{used} fds"
74
+ else
75
+ alert "fd #{name} #{process}", :ok, used, "process #{name} #{process} is using #{used} fds"
76
+ end
56
77
  end
57
78
  end
58
- end
59
- end
60
79
 
61
- def tick
62
- @fd.call
80
+ def tick
81
+ @fd.call
82
+ end
83
+ end
63
84
  end
64
85
  end
65
86