prometheus-splash 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -0
- data/bin/splash +90 -10
- data/config/splash.yml +75 -17
- data/lib/splash/backends.rb +12 -0
- data/lib/splash/backends/file.rb +35 -0
- data/lib/splash/backends/redis.rb +42 -0
- data/lib/splash/commands.rb +55 -14
- data/lib/splash/config.rb +65 -26
- data/lib/splash/constants.rb +22 -9
- data/lib/splash/controller.rb +2 -13
- data/lib/splash/helpers.rb +34 -14
- data/lib/splash/logs.rb +27 -6
- data/lib/splash/orchestrator.rb +49 -0
- data/lib/splash/transports.rb +18 -0
- data/lib/splash/transports/rabbitmq.rb +45 -0
- data/prometheus-splash.gemspec +4 -2
- data/spec/templates_spec.rb +72 -0
- data/templates/report.txt +1 -0
- metadata +43 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f2d1cf627c0fd2371da011a6a97a24e82498b9a28b3d4d0485e185841bf7438
|
4
|
+
data.tar.gz: e0982026ea749cad8366f420e88cd376bcc8bd0a45e0ec292cc1b9a8237764c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b43dcf0493e86ac31da666c4a68c8f2f22f804231f1fd6c0bcc98e3235605e80a4c8e8def976a7c4bf1c54110bdbfe0e67d64040c9d857d23dc16a324cdd1826
|
7
|
+
data.tar.gz: 18b7dea993feea9e1a276bf02b5542d98a772754eb68b113bde17ba5f69ec9c346eaa56520d8b960aa658ba1cc4b53a17dd5f7e4af256f8d07b404131573acd4
|
data/Rakefile
CHANGED
data/bin/splash
CHANGED
@@ -5,6 +5,8 @@ begin
|
|
5
5
|
require 'prometheus/client'
|
6
6
|
require 'prometheus/client/push'
|
7
7
|
require 'thor'
|
8
|
+
require 'rufus-scheduler'
|
9
|
+
|
8
10
|
rescue Gem::GemNotFoundException
|
9
11
|
$stderr.puts "Loadind error, it's like you try to run Splash, with a lake of dependencies."
|
10
12
|
$stderr.puts "If you run on RVM, please run with rvmsudo and not with sudo."
|
@@ -17,9 +19,11 @@ require 'splash/constants'
|
|
17
19
|
require 'splash/helpers'
|
18
20
|
require 'splash/config'
|
19
21
|
require 'splash/templates'
|
22
|
+
require 'splash/backends'
|
20
23
|
|
21
24
|
require 'splash/commands'
|
22
25
|
require 'splash/logs'
|
26
|
+
require 'splash/orchestrator'
|
23
27
|
require 'splash/controller'
|
24
28
|
|
25
29
|
#inhibit warning : due to prometheus-client call to URI.encode warning
|
@@ -32,20 +36,23 @@ module CLISplash
|
|
32
36
|
|
33
37
|
class Commands < Thor
|
34
38
|
include Splash::Config
|
39
|
+
include Splash::Backends
|
35
40
|
|
36
|
-
desc "
|
41
|
+
desc "run NAME", "run for command/sequence or ack result"
|
37
42
|
long_desc <<-LONGDESC
|
38
|
-
|
43
|
+
Running command or sequence or ack result
|
39
44
|
with --no-trace prevent storing execution trace in TRACE_PATH (see config file)
|
40
45
|
with --ack, notify errorcode=0 to Prometheus PushGateway
|
46
|
+
with --no-notify, bypass Prometheus notification
|
41
47
|
LONGDESC
|
42
48
|
option :trace, :type => :boolean, :default => true
|
43
49
|
option :ack, :type => :boolean, negate: false
|
50
|
+
option :notify, :type => :boolean, :default => true
|
44
51
|
def wrap(name)
|
45
52
|
if is_root? then
|
46
53
|
command = Splash::CommandWrapper::new(name)
|
47
54
|
command.ack if options[:ack]
|
48
|
-
command.call_and_notify trace: options[:trace]
|
55
|
+
command.call_and_notify trace: options[:trace], notify: options[:notify]
|
49
56
|
else
|
50
57
|
$stderr.puts "Command wrapping need to be run as root"
|
51
58
|
exit 60
|
@@ -53,6 +60,31 @@ module CLISplash
|
|
53
60
|
end
|
54
61
|
|
55
62
|
|
63
|
+
|
64
|
+
desc "treeview", "Show commands sequence tree"
|
65
|
+
def treeview(command, depht = 0)
|
66
|
+
puts "Command : #{command.to_s}" if depht == 0
|
67
|
+
cmd = get_config.commands[command.to_sym]
|
68
|
+
if cmd[:on_failure] then
|
69
|
+
print " " * depht + " "
|
70
|
+
puts "* on failure => #{cmd[:on_failure]}"
|
71
|
+
treeview(cmd[:on_failure], depht+2)
|
72
|
+
end
|
73
|
+
if cmd[:on_success] then
|
74
|
+
print " " * depht + " "
|
75
|
+
puts "* on success => #{cmd[:on_success]}"
|
76
|
+
treeview(cmd[:on_success],depht+2)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
|
56
88
|
desc "list", "Show configured commands"
|
57
89
|
long_desc <<-LONGDESC
|
58
90
|
Show configured commands
|
@@ -63,11 +95,13 @@ module CLISplash
|
|
63
95
|
puts "Splash configured commands :"
|
64
96
|
list = get_config.commands
|
65
97
|
puts 'No configured commands found' if list.keys.empty?
|
66
|
-
|
98
|
+
list.keys.each do |command|
|
67
99
|
puts " * #{command.to_s}"
|
68
100
|
if options[:detail] then
|
69
101
|
puts " - command line : '#{list[command][:command]}'"
|
70
102
|
puts " - command description : '#{list[command][:desc]}'"
|
103
|
+
puts " - command failure callback : '#{list[command.to_sym][:on_failure]}'" if list[command.to_sym][:on_failure]
|
104
|
+
puts " - command success callback : '#{list[command.to_sym][:on_success]}'" if list[command.to_sym][:on_success]
|
71
105
|
end
|
72
106
|
end
|
73
107
|
end
|
@@ -80,6 +114,8 @@ module CLISplash
|
|
80
114
|
puts "Splash command : #{command}"
|
81
115
|
puts " - command line : '#{list[command.to_sym][:command]}'"
|
82
116
|
puts " - command description : '#{list[command.to_sym][:desc]}'"
|
117
|
+
puts " - command failure callback : '#{list[command.to_sym][:on_failure]}'" if list[command.to_sym][:on_failure]
|
118
|
+
puts " - command success callback : '#{list[command.to_sym][:on_success]}'" if list[command.to_sym][:on_success]
|
83
119
|
else
|
84
120
|
$stderr.puts "Command not configured"
|
85
121
|
exit 50
|
@@ -92,9 +128,10 @@ module CLISplash
|
|
92
128
|
list = get_config.commands
|
93
129
|
if list.keys.include? command.to_sym then
|
94
130
|
print "Splash command #{command} previous execution report:\n\n"
|
95
|
-
|
96
|
-
|
97
|
-
|
131
|
+
backend = get_backend :execution_trace
|
132
|
+
key = "#{command}_trace.last"
|
133
|
+
if backend.exist? key: key then
|
134
|
+
print backend.get key: key
|
98
135
|
else
|
99
136
|
puts "Command not already runned."
|
100
137
|
end
|
@@ -163,12 +200,25 @@ module CLISplash
|
|
163
200
|
|
164
201
|
|
165
202
|
class Logs < Thor
|
203
|
+
include Splash::Config
|
166
204
|
|
167
205
|
desc "analyse", "analyze logs in config"
|
168
206
|
def analyse
|
169
|
-
|
170
|
-
|
171
|
-
|
207
|
+
results = Splash::LogScanner::new
|
208
|
+
results.analyse
|
209
|
+
puts "SPlash Configured logs status :"
|
210
|
+
full_status = true
|
211
|
+
results.output.each do |result|
|
212
|
+
status = (result[:status] == :clean)? "OK": "KO"
|
213
|
+
puts " * Log : #{result[:log]} : [#{status}]"
|
214
|
+
puts " - Detected pattern : #{result[:pattern]}"
|
215
|
+
puts " - detailled Status : #{result[:status].to_s}"
|
216
|
+
puts " count = #{result[:count]}" if result[:status] == :matched
|
217
|
+
puts " nb lines = #{result[:lines]}" if result[:status] != :missing
|
218
|
+
full_status = false unless result[:status] == :clean
|
219
|
+
end
|
220
|
+
display_status = (full_status)? "OK": "KO"
|
221
|
+
puts "Global Status : [#{display_status}]"
|
172
222
|
end
|
173
223
|
|
174
224
|
desc "monitor", "monitor logs in config"
|
@@ -178,6 +228,36 @@ module CLISplash
|
|
178
228
|
result.notify
|
179
229
|
end
|
180
230
|
|
231
|
+
desc "show LOG", "show configured log monitoring for LOG"
|
232
|
+
def show(log)
|
233
|
+
log_record_set = get_config.logs.select{|item| item[:log] == log }
|
234
|
+
unless log_record_set.empty? then
|
235
|
+
record = log_record_set.first
|
236
|
+
puts "Splash log monitor : #{record[:log]}"
|
237
|
+
puts " -> pattern : /#{record[:pattern]}/"
|
238
|
+
else
|
239
|
+
$stderr.puts "log not configured"
|
240
|
+
exit 50
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
desc "list", "Show configured logs monitoring"
|
245
|
+
long_desc <<-LONGDESC
|
246
|
+
Show configured logs monitoring
|
247
|
+
with --detail, show logs monitor details
|
248
|
+
LONGDESC
|
249
|
+
option :detail, :type => :boolean
|
250
|
+
def list
|
251
|
+
puts "Splash configured log monitoring :"
|
252
|
+
log_record_set = get_config.logs
|
253
|
+
puts 'No configured commands found' if log_record_set.empty?
|
254
|
+
log_record_set.each do |record|
|
255
|
+
puts " * log monitor : #{record[:log]}"
|
256
|
+
if options[:detail] then
|
257
|
+
puts " -> pattern : /#{record[:pattern]}/"
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
181
261
|
|
182
262
|
end
|
183
263
|
end
|
data/config/splash.yml
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
# Splash Configuration
|
1
2
|
:splash:
|
3
|
+
|
4
|
+
### Main Configuration
|
2
5
|
:templates:
|
3
6
|
:execution:
|
4
7
|
:path: /etc/splash_execution_report.tpl
|
@@ -10,31 +13,86 @@
|
|
10
13
|
- :stderr
|
11
14
|
- :desc
|
12
15
|
- :status
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
:
|
16
|
+
- :exec_time
|
17
|
+
:backends:
|
18
|
+
:list:
|
19
|
+
- :file
|
20
|
+
- :redis
|
21
|
+
:stores:
|
22
|
+
:execution_trace:
|
23
|
+
:type: :file
|
24
|
+
:path: /tmp/splash
|
25
|
+
# :execution_trace:
|
26
|
+
# :type: :redis
|
27
|
+
# :host: localhost
|
28
|
+
# :port: 6379
|
29
|
+
# #:auth: "mykey"
|
30
|
+
# :base: 1
|
31
|
+
:transports:
|
32
|
+
:active: :rabbitmq
|
33
|
+
:rabbitmq:
|
34
|
+
:url: amqp://127.0.0.1
|
35
|
+
:daemon:
|
36
|
+
:logmon_scheduling:
|
37
|
+
:every: 20s
|
38
|
+
:process_name: "Splash : daemon."
|
39
|
+
:paths:
|
40
|
+
:pid_path: /tmp
|
41
|
+
:trace_path: /tmp/splash
|
42
|
+
:files:
|
43
|
+
:stdout_trace: stdout.txt
|
44
|
+
:stderr_trace: stderr.txt
|
45
|
+
:pid_file: splash.pid
|
18
46
|
:prometheus:
|
19
47
|
:pushgateway:
|
20
48
|
:host: "localhost"
|
21
49
|
:port: 9091
|
50
|
+
|
51
|
+
|
52
|
+
### configuration of commands and scheduling
|
22
53
|
:commands:
|
54
|
+
:id_root:
|
55
|
+
:desc: run id command on root
|
56
|
+
:command: id root
|
57
|
+
:true_test:
|
58
|
+
:desc: "test command returning true : 0"
|
59
|
+
:command: "true"
|
60
|
+
:schedule:
|
61
|
+
:every: "1h"
|
62
|
+
:on_failure: :ls_slash_tmp
|
63
|
+
:on_success: :pwd
|
23
64
|
:false_test:
|
24
|
-
:desc: "test command"
|
65
|
+
:desc: "test command returning false > 0"
|
25
66
|
:command: "false"
|
67
|
+
:schedule:
|
68
|
+
:every: "1h"
|
69
|
+
:on_failure: :ls_slash_tmp
|
70
|
+
:on_success: :pwd
|
26
71
|
:ls_slash_tmp:
|
27
72
|
:desc: list file in /tmp
|
28
73
|
:command: ls -al /tmp
|
29
74
|
:user: daemon
|
30
|
-
|
31
|
-
:
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
:
|
36
|
-
|
37
|
-
|
38
|
-
:
|
39
|
-
:
|
40
|
-
|
75
|
+
:on_success: :echo1
|
76
|
+
:pwd:
|
77
|
+
:desc: run pwd
|
78
|
+
:command: pwd
|
79
|
+
:on_success: :echo1
|
80
|
+
:on_failure: :echo2
|
81
|
+
:echo1:
|
82
|
+
:desc: echo 'foo'
|
83
|
+
:command: echo foo
|
84
|
+
:on_failure: :echo3
|
85
|
+
:echo2:
|
86
|
+
:desc: echo 'bar'
|
87
|
+
:command: echo bar
|
88
|
+
:echo3:
|
89
|
+
:desc: echo 'been'
|
90
|
+
:command: echo been
|
91
|
+
|
92
|
+
|
93
|
+
### configuration of monitored logs
|
94
|
+
:logs:
|
95
|
+
- :log: /tmp/test
|
96
|
+
:pattern: ERROR
|
97
|
+
- :log: /var/log/message
|
98
|
+
:pattern: error
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Dir[File.dirname(__FILE__) + '/backends/*.rb'].each {|file| require file }
|
2
|
+
|
3
|
+
module Splash
|
4
|
+
module Backends
|
5
|
+
include Splash::Config
|
6
|
+
def get_backend(store)
|
7
|
+
aclass = "Splash::Backends::#{get_config[:backends][:stores][store][:type].to_s.capitalize}"
|
8
|
+
return Kernel.const_get(aclass)::new(store)
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Splash
|
2
|
+
module Backends
|
3
|
+
class File
|
4
|
+
include Splash::Config
|
5
|
+
def initialize(store)
|
6
|
+
@config = get_config[:backends][:stores][store]
|
7
|
+
@path = @config[:path]
|
8
|
+
end
|
9
|
+
|
10
|
+
def list(pattern='*')
|
11
|
+
return Dir.glob(pattern)
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(options)
|
15
|
+
return ::File.readlines("#{@path}/#{options[:key]}").join
|
16
|
+
end
|
17
|
+
|
18
|
+
def put(options)
|
19
|
+
::File.open("#{@path}/#{options[:key]}", 'w') { |file|
|
20
|
+
file.write options[:value]
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def del(options)
|
25
|
+
::File.unlink("#{@path}/#{options[:key]}") if File.exist?("#{@path}/#{options[:key]}")
|
26
|
+
end
|
27
|
+
|
28
|
+
def exist?(options)
|
29
|
+
return ::File.exist?("#{@path}/#{options[:key]}")
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "redis"
|
2
|
+
|
3
|
+
module Splash
|
4
|
+
module Backends
|
5
|
+
class Redis
|
6
|
+
include Splash::Config
|
7
|
+
def initialize(store)
|
8
|
+
@config = get_config[:backends][:stores][store]
|
9
|
+
@store = ::Redis.new :host => @config[:host], :port => @config[:port], :db => @config[:base].to_i
|
10
|
+
@redis_cli_cmd = `which redis-cli`
|
11
|
+
@store.auth(@config[:auth]) if @config[:auth]
|
12
|
+
end
|
13
|
+
|
14
|
+
def list(pattern='*')
|
15
|
+
return @store.keys pattern
|
16
|
+
end
|
17
|
+
|
18
|
+
def get(options)
|
19
|
+
return @store.get(options[:key])
|
20
|
+
end
|
21
|
+
|
22
|
+
def put(options)
|
23
|
+
@store.set options[:key], options[:value]
|
24
|
+
end
|
25
|
+
|
26
|
+
def del(options)
|
27
|
+
@store.del options[:key]
|
28
|
+
end
|
29
|
+
|
30
|
+
def flush
|
31
|
+
`#{@redis_cli_cmd} -n 3 flushdb`
|
32
|
+
# @@store.flushdb
|
33
|
+
end
|
34
|
+
|
35
|
+
def exist?(options)
|
36
|
+
return ( not @store.get(options[:key]).nil?)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/lib/splash/commands.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'open3'
|
2
2
|
require 'date'
|
3
|
-
|
3
|
+
require 'socket'
|
4
4
|
|
5
5
|
module Splash
|
6
6
|
class CommandWrapper
|
7
7
|
include Splash::Templates
|
8
8
|
include Splash::Config
|
9
9
|
include Splash::Helpers
|
10
|
+
include Splash::Backends
|
10
11
|
|
11
12
|
def initialize(name)
|
12
13
|
@config = get_config
|
@@ -15,6 +16,12 @@ module Splash
|
|
15
16
|
$stderr.puts "Splash : command #{@name} is not defined in configuration"
|
16
17
|
exit 40
|
17
18
|
end
|
19
|
+
@registry = Prometheus::Client.registry
|
20
|
+
@url = "http://#{@config.prometheus_pushgateway_host}:#{@config.prometheus_pushgateway_port}"
|
21
|
+
@metric_exitcode = Prometheus::Client::Gauge.new(:errorcode, docstring: 'SPLASH metric batch errorcode')
|
22
|
+
@metric_time = Prometheus::Client::Gauge.new(:exectime, docstring: 'SPLASH metric batch execution time')
|
23
|
+
@registry.register(@metric_exitcode)
|
24
|
+
@registry.register(@metric_time)
|
18
25
|
end
|
19
26
|
|
20
27
|
def ack
|
@@ -23,39 +30,42 @@ module Splash
|
|
23
30
|
exit 0
|
24
31
|
end
|
25
32
|
|
26
|
-
def notify(value)
|
33
|
+
def notify(value,time)
|
27
34
|
unless verify_service host: @config.prometheus_pushgateway_host ,port: @config.prometheus_pushgateway_port then
|
28
35
|
$stderr.puts "Prometheus PushGateway Service IS NOT running"
|
29
36
|
$stderr.puts "Exit without notification."
|
30
37
|
exit 30
|
31
38
|
end
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
Prometheus::Client::Push.new(@name, nil, url).add(registry)
|
38
|
-
puts "Prometheus Gateway notified."
|
39
|
+
@metric_exitcode.set(value)
|
40
|
+
@metric_time.set(time)
|
41
|
+
hostname = Socket.gethostname
|
42
|
+
Prometheus::Client::Push.new(@name, hostname, @url).add(@registry)
|
43
|
+
puts " * Prometheus Gateway notified."
|
39
44
|
end
|
40
45
|
|
41
46
|
|
42
47
|
def call_and_notify(options)
|
43
48
|
puts "Executing command : '#{@name}' "
|
49
|
+
start = Time.now
|
44
50
|
unless options[:trace] then
|
45
|
-
puts "Traceless execution"
|
51
|
+
puts " * Traceless execution"
|
46
52
|
if @config.commands[@name.to_sym][:user] then
|
53
|
+
puts " * Execute with user : #{@config.commands[@name.to_sym][:user]}."
|
47
54
|
system("sudo -u #{@config.commands[@name.to_sym][:user]} #{@config.commands[@name.to_sym][:command]} > /dev/null 2>&1")
|
48
55
|
else
|
49
56
|
system("#{@config.commands[@name.to_sym][:command]} > /dev/null 2>&1")
|
50
57
|
end
|
58
|
+
time = Time.now - start
|
51
59
|
exit_code = $?.exitstatus
|
52
60
|
else
|
53
|
-
puts "Tracefull execution"
|
61
|
+
puts " * Tracefull execution"
|
54
62
|
if @config.commands[@name.to_sym][:user] then
|
63
|
+
puts " * Execute with user : #{@config.commands[@name.to_sym][:user]}."
|
55
64
|
stdout, stderr, status = Open3.capture3("sudo -u #{@config.commands[@name.to_sym][:user]} #{@config.commands[@name.to_sym][:command]}")
|
56
65
|
else
|
57
66
|
stdout, stderr, status = Open3.capture3(@config.commands[@name.to_sym][:command])
|
58
67
|
end
|
68
|
+
time = Time.now - start
|
59
69
|
tp = Template::new(
|
60
70
|
list_token: @config.execution_template_tokens,
|
61
71
|
template_file: @config.execution_template_path)
|
@@ -67,12 +77,43 @@ module Splash
|
|
67
77
|
tp.status = status.to_s
|
68
78
|
tp.stdout = stdout
|
69
79
|
tp.stderr = stderr
|
70
|
-
|
71
|
-
|
80
|
+
tp.exec_time = time.to_s
|
81
|
+
backend = get_backend :execution_trace
|
82
|
+
key = "#{@name}_trace.last"
|
83
|
+
backend.put key: key, value: tp.output
|
72
84
|
exit_code = status.exitstatus
|
85
|
+
|
73
86
|
end
|
74
87
|
|
75
|
-
|
88
|
+
puts " => exitcode #{exit_code}"
|
89
|
+
if options[:notify] then
|
90
|
+
notify(exit_code,time.to_i)
|
91
|
+
else
|
92
|
+
puts " * Without Prometheus notification"
|
93
|
+
end
|
94
|
+
on_failure = (@config.commands[@name.to_sym][:on_failure])? @config.commands[@name.to_sym][:on_failure] : false
|
95
|
+
on_success = (@config.commands[@name.to_sym][:on_success])? @config.commands[@name.to_sym][:on_success] : false
|
96
|
+
|
97
|
+
if on_failure and exit_code > 0 then
|
98
|
+
puts " * On failure callback : #{on_failure}"
|
99
|
+
if @config.commands.keys.include? on_failure then
|
100
|
+
@name = on_failure.to_s
|
101
|
+
call_and_notify options
|
102
|
+
else
|
103
|
+
$stderr.puts "on_failure call error : configuration mistake : #{on_failure} command inexistant."
|
104
|
+
end
|
105
|
+
end
|
106
|
+
if on_success and exit_code == 0 then
|
107
|
+
puts " * On success callback : #{on_success}"
|
108
|
+
if @config.commands.keys.include? on_success then
|
109
|
+
@name = on_success.to_s
|
110
|
+
call_and_notify options
|
111
|
+
else
|
112
|
+
$stderr.puts "on_success call error : configuration mistake : #{on_success} command inexistant."
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
|
76
117
|
exit exit_code
|
77
118
|
end
|
78
119
|
end
|
data/lib/splash/config.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
+
require 'etc'
|
1
2
|
module Splash
|
2
3
|
module Config
|
3
4
|
include Splash::Helpers
|
4
5
|
include Splash::Constants
|
5
6
|
|
6
7
|
|
7
|
-
|
8
|
+
# Class to manage configuration in Splash from Splash::Constants override by Yaml CONFIG
|
8
9
|
class Configuration < Hash
|
9
10
|
include Splash::Constants
|
11
|
+
|
10
12
|
def initialize(config_file=CONFIG_FILE)
|
11
13
|
config_from_file = readconf config_file
|
12
14
|
self[:version] = VERSION
|
@@ -15,24 +17,57 @@ module Splash
|
|
15
17
|
self[:prometheus_pushgateway_host] = (config_from_file[:prometheus][:pushgateway][:host])? config_from_file[:prometheus][:pushgateway][:host] : PROMETHEUS_PUSHGATEWAY_HOST
|
16
18
|
self[:prometheus_pushgateway_port] = (config_from_file[:prometheus][:pushgateway][:port])? config_from_file[:prometheus][:pushgateway][:port] : PROMETHEUS_PUSHGATEWAY_PORT
|
17
19
|
self[:daemon_process_name] = (config_from_file[:daemon][:process_name])? config_from_file[:daemon][:process_name] : DAEMON_PROCESS_NAME
|
18
|
-
self[:
|
19
|
-
self[:execution_template_tokens] = (config_from_file[:templates][:execution][:tokens])? config_from_file[:templates][:execution][:tokens] :
|
20
|
+
self[:daemon_logmon_scheduling] = (config_from_file[:daemon][:logmon_scheduling])? config_from_file[:daemon][:logmon_scheduling] : DAEMON_LOGMON_SCHEDULING
|
21
|
+
self[:execution_template_tokens] = (config_from_file[:templates][:execution][:tokens])? config_from_file[:templates][:execution][:tokens] : EXECUTION_TEMPLATE_TOKENS_LIST
|
20
22
|
self[:execution_template_path] = (config_from_file[:templates][:execution][:path])? config_from_file[:templates][:execution][:path] : EXECUTION_TEMPLATE
|
21
|
-
self[:
|
22
|
-
self[:pid_path] = (config_from_file[:daemon][:paths][:pid_path])? config_from_file[:daemon][:paths][:pid_path] : PID_PATH
|
23
|
+
self[:pid_path] = (config_from_file[:daemon][:paths][:pid_path])? config_from_file[:daemon][:paths][:pid_path] : DAEMON_PID_PATH
|
23
24
|
self[:trace_path] = (config_from_file[:daemon][:paths][:trace_path])? config_from_file[:daemon][:paths][:trace_path] : TRACE_PATH
|
24
|
-
self[:pid_file] = (config_from_file[:daemon][:files][:pid_file])? config_from_file[:daemon][:files][:pid_file] :
|
25
|
-
self[:stdout_trace] = (config_from_file[:daemon][:files][:stdout_trace])? config_from_file[:daemon][:files][:stdout_trace] :
|
26
|
-
self[:stderr_trace] = (config_from_file[:daemon][:files][:stderr_trace])? config_from_file[:daemon][:files][:stderr_trace] :
|
25
|
+
self[:pid_file] = (config_from_file[:daemon][:files][:pid_file])? config_from_file[:daemon][:files][:pid_file] : DAEMON_PID_FILE
|
26
|
+
self[:stdout_trace] = (config_from_file[:daemon][:files][:stdout_trace])? config_from_file[:daemon][:files][:stdout_trace] : DAEMON_STDOUT_TRACE
|
27
|
+
self[:stderr_trace] = (config_from_file[:daemon][:files][:stderr_trace])? config_from_file[:daemon][:files][:stderr_trace] : DAEMON_STDERR_TRACE
|
28
|
+
|
29
|
+
self[:transports] = {} ; self[:transports].merge! TRANSPORTS_STRUCT ; self[:transports].merge! config_from_file[:transports] if config_from_file[:transports]
|
30
|
+
self[:backends] = {} ; self[:backends].merge! BACKENDS_STRUCT ; self[:backends].merge! config_from_file[:backends] if config_from_file[:backends]
|
31
|
+
|
27
32
|
self[:logs] = (config_from_file[:logs])? config_from_file[:logs] : {}
|
28
33
|
self[:commands] = (config_from_file[:commands])? config_from_file[:commands] : {}
|
29
34
|
|
30
35
|
end
|
31
36
|
|
37
|
+
# @!group accessors on configurations Items
|
38
|
+
|
39
|
+
def Configuration.user_root
|
40
|
+
return Etc.getpwuid(0).name
|
41
|
+
end
|
42
|
+
|
43
|
+
def Configuration.group_root
|
44
|
+
return Etc.getgrgid(0).name
|
45
|
+
end
|
46
|
+
|
47
|
+
def user_root
|
48
|
+
return Configuration.user_root
|
49
|
+
end
|
50
|
+
|
51
|
+
def group_root
|
52
|
+
return Configuration.group_root
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def backends
|
57
|
+
return self[:backends]
|
58
|
+
end
|
59
|
+
|
60
|
+
def transports
|
61
|
+
return self[:transport]
|
62
|
+
end
|
63
|
+
|
64
|
+
def daemon_logmon_scheduling
|
65
|
+
return self[:daemon_logmon_scheduling]
|
66
|
+
end
|
67
|
+
|
32
68
|
def execution_template_path
|
33
69
|
return self[:execution_template_path]
|
34
70
|
end
|
35
|
-
|
36
71
|
def execution_template_tokens
|
37
72
|
return self[:execution_template_tokens]
|
38
73
|
end
|
@@ -67,14 +102,6 @@ module Splash
|
|
67
102
|
return self[:prometheus_pushgateway_port]
|
68
103
|
end
|
69
104
|
|
70
|
-
def daemon_user
|
71
|
-
return self[:daemon_user]
|
72
|
-
end
|
73
|
-
|
74
|
-
def daemon_group
|
75
|
-
return self[:daemon_group]
|
76
|
-
end
|
77
|
-
|
78
105
|
def full_pid_path
|
79
106
|
return "#{self[:pid_path]}/#{self[:pid_file]}"
|
80
107
|
end
|
@@ -87,6 +114,8 @@ module Splash
|
|
87
114
|
return "#{self[:trace_path]}/#{self[:stderr_trace]}"
|
88
115
|
end
|
89
116
|
|
117
|
+
# @!endgroup
|
118
|
+
|
90
119
|
private
|
91
120
|
def readconf(file = CONFIG_FILE)
|
92
121
|
return YAML.load_file(file)[:splash]
|
@@ -95,18 +124,21 @@ module Splash
|
|
95
124
|
|
96
125
|
end
|
97
126
|
|
98
|
-
|
127
|
+
# factory of Configuration Class instance
|
128
|
+
# @param [String] config_file the path of the YAML Config file
|
129
|
+
# @return [SPlash::Config::Configuration]
|
99
130
|
def get_config(config_file=CONFIG_FILE)
|
100
131
|
return Configuration::new config_file
|
101
132
|
end
|
102
133
|
|
103
|
-
|
134
|
+
# Setup action method for installing Splash
|
135
|
+
# @return [Integer] an errorcode value
|
104
136
|
def setupsplash
|
105
137
|
conf_in_path = search_file_in_gem "prometheus-splash", "config/splash.yml"
|
106
138
|
full_res = 0
|
107
139
|
puts "Splash -> setup : "
|
108
140
|
print "* Installing Configuration file : #{CONFIG_FILE} : "
|
109
|
-
if install_file source: conf_in_path, target: CONFIG_FILE, mode: "644", owner:
|
141
|
+
if install_file source: conf_in_path, target: CONFIG_FILE, mode: "644", owner: Configuration.user_root, group: Configuration.group_root then
|
110
142
|
puts "[OK]"
|
111
143
|
else
|
112
144
|
full_res =+ 1
|
@@ -115,7 +147,7 @@ module Splash
|
|
115
147
|
config = get_config
|
116
148
|
report_in_path = search_file_in_gem "prometheus-splash", "templates/report.txt"
|
117
149
|
print "* Installing template file : #{config.execution_template_path} : "
|
118
|
-
if install_file source: report_in_path, target: config.execution_template_path, mode: "644", owner:
|
150
|
+
if install_file source: report_in_path, target: config.execution_template_path, mode: "644", owner: config.user_root, group: config.group_root then
|
119
151
|
puts "[OK]"
|
120
152
|
else
|
121
153
|
full_res =+ 1
|
@@ -123,7 +155,7 @@ module Splash
|
|
123
155
|
end
|
124
156
|
|
125
157
|
print "* Creating/Checking pid file path : #{config[:pid_path]} : "
|
126
|
-
if make_folder path: config[:pid_path], mode: "644", owner:
|
158
|
+
if make_folder path: config[:pid_path], mode: "644", owner: config.user_root, group: config.group_root then
|
127
159
|
puts "[OK]"
|
128
160
|
else
|
129
161
|
full_res =+ 1
|
@@ -131,7 +163,7 @@ module Splash
|
|
131
163
|
end
|
132
164
|
|
133
165
|
print "* Creating/Checking trace file path : #{config[:trace_path]} : "
|
134
|
-
if make_folder path: config[:trace_path], mode: "
|
166
|
+
if make_folder path: config[:trace_path], mode: "644", owner: config.user_root, group: config.group_root then
|
135
167
|
puts "[OK]"
|
136
168
|
else
|
137
169
|
full_res =+ 1
|
@@ -148,12 +180,14 @@ module Splash
|
|
148
180
|
|
149
181
|
end
|
150
182
|
|
183
|
+
# Sanitycheck action method for testing installation of Splash
|
184
|
+
# @return [Integer] an errorcode value
|
151
185
|
def checkconfig
|
152
186
|
puts "Splash -> sanitycheck : "
|
153
187
|
config = get_config
|
154
188
|
full_res = 0
|
155
189
|
print "* Config file : #{CONFIG_FILE} : "
|
156
|
-
res = verify_file(name: CONFIG_FILE, mode: "644", owner:
|
190
|
+
res = verify_file(name: CONFIG_FILE, mode: "644", owner: config.user_root, group: config.group_root)
|
157
191
|
if res.empty? then
|
158
192
|
print "[OK]\n"
|
159
193
|
else
|
@@ -163,7 +197,7 @@ module Splash
|
|
163
197
|
end
|
164
198
|
|
165
199
|
print "* PID Path : #{config[:pid_path]} : "
|
166
|
-
res = verify_folder(name: config[:pid_path], mode: "644", owner:
|
200
|
+
res = verify_folder(name: config[:pid_path], mode: "644", owner: config.user_root, group: config.group_root)
|
167
201
|
if res.empty? then
|
168
202
|
print "[OK]\n"
|
169
203
|
else
|
@@ -174,7 +208,7 @@ module Splash
|
|
174
208
|
end
|
175
209
|
|
176
210
|
print "* trace Path : #{config[:trace_path]} : "
|
177
|
-
res = verify_folder(name: config[:trace_path], mode: "777", owner: config.
|
211
|
+
res = verify_folder(name: config[:trace_path], mode: "777", owner: config.user_root, group: config.group_root)
|
178
212
|
if res.empty? then
|
179
213
|
print "[OK]\n"
|
180
214
|
else
|
@@ -202,6 +236,11 @@ module Splash
|
|
202
236
|
|
203
237
|
private
|
204
238
|
|
239
|
+
# facilities to find a file in gem path
|
240
|
+
# @param [String] _gem a Gem name
|
241
|
+
# @param [String] _file a file relative path in the gem
|
242
|
+
# @return [String] the path of the file, if found.
|
243
|
+
# @return [False] if not found
|
205
244
|
def search_file_in_gem(_gem,_file)
|
206
245
|
if Gem::Specification.respond_to?(:find_by_name)
|
207
246
|
begin
|
data/lib/splash/constants.rb
CHANGED
@@ -1,22 +1,35 @@
|
|
1
1
|
module Splash
|
2
2
|
module Constants
|
3
|
-
VERSION = "0.0.
|
3
|
+
VERSION = "0.0.3"
|
4
|
+
|
4
5
|
CONFIG_FILE = "/etc/splash.yml"
|
5
|
-
DAEMON_USER = "root"
|
6
|
-
DAEMON_GROUP = "wheel"
|
7
|
-
PID_PATH="/var/run"
|
8
6
|
TRACE_PATH="/var/run/splash"
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
|
8
|
+
DAEMON_LOGMON_SCHEDULING={ :every => '20s'}
|
9
|
+
DAEMON_PROCESS_NAME="Splash : daemon."
|
10
|
+
DAEMON_PID_PATH="/var/run"
|
11
|
+
DAEMON_PID_FILE="splash.pid"
|
12
|
+
DAEMON_STDOUT_TRACE="stdout.txt"
|
13
|
+
DAEMON_STDERR_TRACE="stderr.txt"
|
14
|
+
|
13
15
|
AUTHOR="Romain GEORGES"
|
14
16
|
EMAIL = "gems@ultragreen.net"
|
15
17
|
COPYRIGHT="Ultragreen (c) 2020"
|
16
18
|
LICENSE="BSD-2-Clause"
|
19
|
+
|
17
20
|
PROMETHEUS_PUSHGATEWAY_HOST = "localhost"
|
18
21
|
PROMETHEUS_PUSHGATEWAY_PORT = "9091"
|
22
|
+
|
19
23
|
EXECUTION_TEMPLATE="/etc/splash_execution_report.tpl"
|
20
|
-
|
24
|
+
EXECUTION_TEMPLATE_TOKENS_LIST = [:date,:cmd_name,:cmd_line,:stdout,:stderr,:desc,:status,:exec_time]
|
25
|
+
|
26
|
+
BACKENDS_STRUCT = { :list => [:file,:redis],
|
27
|
+
:stores => { :execution_trace => { :type => :file, :path => "/var/run/splash" }}}
|
28
|
+
TRANSPORTS_STRUCT = { :list => [:rabbitmq],
|
29
|
+
:active => :rabbitmq,
|
30
|
+
:rabbitmq => { :url => 'amqp://localhost/'} }
|
31
|
+
|
32
|
+
|
33
|
+
|
21
34
|
end
|
22
35
|
end
|
data/lib/splash/controller.rb
CHANGED
@@ -5,6 +5,7 @@ module Splash
|
|
5
5
|
include Splash::Constants
|
6
6
|
include Splash::Helpers
|
7
7
|
include Splash::Config
|
8
|
+
include Splash::Orchestrator
|
8
9
|
|
9
10
|
def startdaemon(options = {})
|
10
11
|
config = get_config
|
@@ -17,21 +18,9 @@ module Splash
|
|
17
18
|
unless File::exist? config.full_pid_path then
|
18
19
|
res = daemonize :description => config.daemon_process_name,
|
19
20
|
:pid_file => config.full_pid_path,
|
20
|
-
:daemon_user => config.daemon_user,
|
21
|
-
:daemon_group => config.daemon_group,
|
22
21
|
:stdout_trace => config.full_stdout_trace_path,
|
23
22
|
:stderr_trace => config.full_stderr_trace_path do
|
24
|
-
|
25
|
-
while true
|
26
|
-
begin
|
27
|
-
sleep 5
|
28
|
-
puts "Notify"
|
29
|
-
result.analyse
|
30
|
-
result.notify
|
31
|
-
rescue Errno::ECONNREFUSED
|
32
|
-
$stderr.puts "PushGateway seems to be done, please start it."
|
33
|
-
end
|
34
|
-
end
|
23
|
+
Scheduler::new
|
35
24
|
end
|
36
25
|
if res == 0 then
|
37
26
|
pid = `cat #{config.full_pid_path}`.to_i
|
data/lib/splash/helpers.rb
CHANGED
@@ -7,7 +7,10 @@ module Splash
|
|
7
7
|
module Helpers
|
8
8
|
|
9
9
|
|
10
|
-
|
10
|
+
# facilité pour récupérer un PID depuis une regexp
|
11
|
+
# @param [Hash] options
|
12
|
+
# @option options [String] :pattern une regexp
|
13
|
+
# @return [String] le PID
|
11
14
|
def get_process(options = {})
|
12
15
|
pattern = options[:pattern]
|
13
16
|
res = `ps aux|grep '#{pattern}'|grep -v grep`.to_s
|
@@ -18,7 +21,8 @@ module Splash
|
|
18
21
|
end
|
19
22
|
end
|
20
23
|
|
21
|
-
|
24
|
+
# facilité pour vérifier si le process actif est root
|
25
|
+
# @return [Bool] vrai ou faux
|
22
26
|
def is_root?
|
23
27
|
case (Process.uid)
|
24
28
|
when 0
|
@@ -28,6 +32,9 @@ module Splash
|
|
28
32
|
end
|
29
33
|
end
|
30
34
|
|
35
|
+
# facilité pour s'assurer qu'on execute une méthode avec les droits root
|
36
|
+
# @param [Symbol] method a method name th wrap
|
37
|
+
# @return [void] le retour de la méthode wrappée
|
31
38
|
def run_as_root(method)
|
32
39
|
unless is_root?
|
33
40
|
$stderr.puts "You need to be root to execute this subcommands : #{method.to_s}"
|
@@ -39,13 +46,18 @@ module Splash
|
|
39
46
|
end
|
40
47
|
|
41
48
|
# method for daemonize blocks
|
42
|
-
# @param [Hash]
|
43
|
-
# @option
|
44
|
-
# @option
|
49
|
+
# @param [Hash] options the list of options, keys are symbols
|
50
|
+
# @option options [String] :description the description of the process, use for $0
|
51
|
+
# @option options [String] :pid_file the pid filename
|
52
|
+
# @option options [String] :daemon_user the user to change privileges
|
53
|
+
# @option options [String] :daemon_group the group to change privileges
|
54
|
+
# @option options [String] :stderr_trace the path of the file where to redirect STDERR
|
55
|
+
# @option options [String] :stdout_trace the path of the file where to redirect STDOUT
|
56
|
+
# @option options [Bool] :debug option to run foreground
|
45
57
|
# @yield a process definion or block given
|
46
58
|
# @example usage inline
|
47
59
|
# class Test
|
48
|
-
# include Splash::Helpers
|
60
|
+
# include Splash::Helpers
|
49
61
|
# private :daemonize
|
50
62
|
# def initialize
|
51
63
|
# @loop = Proc::new do
|
@@ -62,7 +74,7 @@ module Splash
|
|
62
74
|
#
|
63
75
|
# @example usage block
|
64
76
|
# class Test
|
65
|
-
# include Splash::Helpers
|
77
|
+
# include Splash::Helpers
|
66
78
|
# include Dorsal::Privates
|
67
79
|
# private :daemonize
|
68
80
|
# def initialize
|
@@ -88,15 +100,17 @@ module Splash
|
|
88
100
|
fork do
|
89
101
|
#Process.daemon
|
90
102
|
File.open(options[:pid_file],"w"){|f| f.puts Process.pid } if options[:pid_file]
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
103
|
+
if options[:daemon_user] and options[:daemon_group] then
|
104
|
+
uid = Etc.getpwnam(options[:daemon_user]).uid
|
105
|
+
gid = Etc.getgrnam(options[:daemon_group]).gid
|
106
|
+
Process::UID.change_privilege(uid)
|
107
|
+
# Process::GID.change_privilege(gid)
|
108
|
+
end
|
109
|
+
$stdout.reopen(options[:stdout_trace], "w") if options[:stdout_trace]
|
110
|
+
$stderr.reopen(options[:stderr_trace], "w") if options[:stderr_trace]
|
97
111
|
|
98
112
|
#$0 = options[:description]
|
99
|
-
Process.setproctitle options[:description]
|
113
|
+
Process.setproctitle options[:description] if options[:description]
|
100
114
|
|
101
115
|
yield
|
102
116
|
|
@@ -110,6 +124,9 @@ module Splash
|
|
110
124
|
# @param [Hash] options
|
111
125
|
# @option options [String] :source le chemin source du fichier
|
112
126
|
# @option options [String] :target le chemin cible du fichier
|
127
|
+
# @option options [String] :mode les droits du fichier du type Octal "XXX"
|
128
|
+
# @option options [String] :owner le owner du fichier
|
129
|
+
# @option options [String] :group le groupe du fichier
|
113
130
|
def install_file(options = {})
|
114
131
|
#begin
|
115
132
|
FileUtils::copy options[:source], options[:target] #unless File::exist? options[:target]
|
@@ -124,6 +141,9 @@ module Splash
|
|
124
141
|
# facilité de création de répertoire
|
125
142
|
# @param [Hash] options
|
126
143
|
# @option options [String] :path le répertoire à créer (relatif ou absolut)
|
144
|
+
# @option options [String] :mode les droits du fichier du type Octal "XXX"
|
145
|
+
# @option options [String] :owner le owner du fichier
|
146
|
+
# @option options [String] :group le groupe du fichier
|
127
147
|
def make_folder(options = {})
|
128
148
|
begin
|
129
149
|
FileUtils::mkdir_p options[:path] unless File::exist? options[:path]
|
data/lib/splash/logs.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
1
3
|
module Splash
|
2
4
|
class LogScanner
|
3
5
|
include Splash::Constants
|
@@ -5,10 +7,14 @@ module Splash
|
|
5
7
|
|
6
8
|
def initialize
|
7
9
|
@logs_target = get_config.logs
|
8
|
-
|
10
|
+
@config = get_config
|
9
11
|
@registry = Prometheus::Client.registry
|
10
|
-
@
|
11
|
-
@
|
12
|
+
@metric_count = Prometheus::Client::Gauge.new(:logerrors, docstring: 'SPLASH metric log error', labels: [:log ])
|
13
|
+
@metric_missing = Prometheus::Client::Gauge.new(:logmissing, docstring: 'SPLASH metric log missing', labels: [:log ])
|
14
|
+
@metric_lines = Prometheus::Client::Gauge.new(:loglines, docstring: 'SPLASH metric log lines numbers', labels: [:log ])
|
15
|
+
@registry.register(@metric_count)
|
16
|
+
@registry.register(@metric_missing)
|
17
|
+
@registry.register(@metric_lines)
|
12
18
|
end
|
13
19
|
|
14
20
|
def analyse
|
@@ -18,8 +24,9 @@ module Splash
|
|
18
24
|
if File.exist?(record[:log]) then
|
19
25
|
record[:count] = File.readlines(record[:log]).grep(/#{record[:pattern]}/).size
|
20
26
|
record[:status] = :matched if record[:count] > 0
|
27
|
+
record[:lines] = `wc -l "#{record[:log]}"`.strip.split(/\s+/)[0].to_i unless record[:status] == :missing
|
21
28
|
else
|
22
|
-
record[:status] = :
|
29
|
+
record[:status] = :missing
|
23
30
|
end
|
24
31
|
end
|
25
32
|
end
|
@@ -29,10 +36,24 @@ module Splash
|
|
29
36
|
end
|
30
37
|
|
31
38
|
def notify
|
39
|
+
unless verify_service host: @config.prometheus_pushgateway_host ,port: @config.prometheus_pushgateway_port then
|
40
|
+
$stderr.puts "Prometheus PushGateway Service IS NOT running"
|
41
|
+
$stderr.puts "Exit without notification."
|
42
|
+
exit 30
|
43
|
+
end
|
44
|
+
puts "Sending metrics to Prometheus Pushgateway"
|
32
45
|
@logs_target.each do |item|
|
33
|
-
|
46
|
+
missing = (item[:status] == :missing)? 1 : 0
|
47
|
+
puts " * Sending metrics for #{item[:log]}"
|
48
|
+
@metric_count.set(item[:count], labels: { log: item[:log] })
|
49
|
+
@metric_missing.set(missing, labels: { log: item[:log] })
|
50
|
+
lines = (item[:lines])? item[:lines] : 0
|
51
|
+
@metric_lines.set(lines, labels: { log: item[:log] })
|
34
52
|
end
|
35
|
-
|
53
|
+
hostname = Socket.gethostname
|
54
|
+
url = "http://#{@config.prometheus_pushgateway_host}:#{@config.prometheus_pushgateway_port}"
|
55
|
+
Prometheus::Client::Push.new('Splash',hostname, url).add(@registry)
|
56
|
+
puts "Sending done."
|
36
57
|
end
|
37
58
|
|
38
59
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Splash
|
2
|
+
module Orchestrator
|
3
|
+
|
4
|
+
module SchedulerHooks
|
5
|
+
def on_pre_trigger(job, trigger_time)
|
6
|
+
|
7
|
+
end
|
8
|
+
|
9
|
+
def on_post_trigger(job, trigger_time)
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
def init_log
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
class Scheduler
|
20
|
+
include Splash::Constants
|
21
|
+
include Splash::Helpers
|
22
|
+
include Splash::Config
|
23
|
+
def initialize
|
24
|
+
@server = Rufus::Scheduler::new
|
25
|
+
@server.extend SchedulerHooks
|
26
|
+
@server.init_log
|
27
|
+
@result = LogScanner::new
|
28
|
+
@server.every '20s' do
|
29
|
+
begin
|
30
|
+
puts "Notify"
|
31
|
+
@result.analyse
|
32
|
+
@result.notify
|
33
|
+
$stdout.flush
|
34
|
+
rescue Errno::ECONNREFUSED
|
35
|
+
$stderr.puts "PushGateway seems to be done, please start it."
|
36
|
+
end
|
37
|
+
end
|
38
|
+
@server.join
|
39
|
+
end
|
40
|
+
|
41
|
+
def terminate
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Dir[File.dirname(__FILE__) + '/transports/*.rb'].each {|file| require file }
|
2
|
+
|
3
|
+
module Splash
|
4
|
+
module Transports
|
5
|
+
include Splash::Config
|
6
|
+
|
7
|
+
def get_default_subscriber
|
8
|
+
aclass = "Splash::Transports::#{get_config[:transports][:active].to_s.capitalize}::Suscriber"
|
9
|
+
return Kernel.const_get(aclass)::new
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_default_client
|
13
|
+
aclass = "Splash::Transports::#{get_config[:transports][:active].to_s.capitalize}::Client"
|
14
|
+
return Kernel.const_get(aclass)::new
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'bunny'
|
2
|
+
module Splash
|
3
|
+
module Transports
|
4
|
+
module RabbitMQ
|
5
|
+
|
6
|
+
class Subscriber
|
7
|
+
def initialize
|
8
|
+
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
class Client
|
14
|
+
def initialize
|
15
|
+
@connection = Bunny.new
|
16
|
+
@connection.start
|
17
|
+
@channel = @connection.create_channel
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def publish(options ={})
|
22
|
+
return @channel.default_exchange.publish(options[:message], :routing_key => options[:queue])
|
23
|
+
end
|
24
|
+
|
25
|
+
def ack
|
26
|
+
return @channel.acknowledge(delivery_info.delivery_tag, false)
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def get(options ={})
|
31
|
+
queue = @channel.queue(options[:queue])
|
32
|
+
delivery_info, properties, payload = queue.pop
|
33
|
+
res = {:message => payload}
|
34
|
+
res[:ack] = delivery_info.delivery_tag if options[:noack]
|
35
|
+
return res
|
36
|
+
end
|
37
|
+
|
38
|
+
def close
|
39
|
+
@connection.close
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/prometheus-splash.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Splash::Constants::VERSION
|
9
9
|
spec.authors = [Splash::Constants::AUTHOR]
|
10
10
|
spec.email = [Splash::Constants::EMAIL]
|
11
|
-
spec.description = %q{Prometheus Logs and Batchs supervision over PushGateway}
|
12
|
-
spec.summary = %q{Supervision with Prometheus of Logs and Asynchronous tasks for Services or Hosts }
|
11
|
+
spec.description = %q{Prometheus Logs and Batchs supervision over PushGateway and commands orchestration}
|
12
|
+
spec.summary = %q{Supervision with Prometheus of Logs and Asynchronous tasks orchestration for Services or Hosts }
|
13
13
|
spec.homepage = "http://www.ultragreen.net"
|
14
14
|
spec.license = Splash::Constants::LICENSE
|
15
15
|
spec.require_paths << 'bin'
|
@@ -21,6 +21,8 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
spec.add_runtime_dependency 'thor','~> 1.0.1'
|
23
23
|
spec.add_runtime_dependency 'prometheus-client','~> 2.0.0'
|
24
|
+
spec.add_runtime_dependency 'rufus-scheduler','~> 3.6.0'
|
25
|
+
spec.add_runtime_dependency 'redis','~> 4.1.3'
|
24
26
|
spec.add_development_dependency 'rake', '~> 13.0.1'
|
25
27
|
spec.add_development_dependency 'rspec', '~> 3.9.0'
|
26
28
|
spec.add_development_dependency 'yard', '~> 0.9.24'
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require_relative '../lib/splash/templates'
|
2
|
+
|
3
|
+
include Splash::Templates
|
4
|
+
|
5
|
+
describe Template do
|
6
|
+
$template_file = "/tmp/template.txt"
|
7
|
+
$nonexistantfile = "/tmp/nonexistant.txt"
|
8
|
+
$template = "Hello %%NAME%% !!"
|
9
|
+
$result = "Hello Romain !!"
|
10
|
+
$goodtoken = :name
|
11
|
+
$badtoken = :surname
|
12
|
+
$value = 'Romain'
|
13
|
+
before :all do
|
14
|
+
`echo "#{$template}" > #{$template_file}`
|
15
|
+
File::unlink($nonexistantfile) if File::exist?($nonexistantfile)
|
16
|
+
end
|
17
|
+
subject { Template }
|
18
|
+
specify { should be_an_instance_of Class }
|
19
|
+
context "Exception case" do
|
20
|
+
it "should raise NoTemplateFile if template file not exist" do
|
21
|
+
expect { Template::new({:list_token => [$goodtoken] , :template_file => $nonexistantfile}) }.to raise_error NoTemplateFile
|
22
|
+
end
|
23
|
+
it "should raise NotAToken if try to send ##{$badtoken} and '#{$badtoken}' not a valid token" do
|
24
|
+
expect { Template::new({ :list_token => [$goodtoken], :template_file => $template_file}).send($badtoken.to_sym)}.to raise_error NotAToken
|
25
|
+
end
|
26
|
+
it "should raise InvalidTokenList if initialized with ['#{$goodtoken}','#{$badtoken}'] tokens list" do
|
27
|
+
expect { Template::new({ :list_token => [$goodtoken,$badtoken] , :template_file => $template_file})}.to raise_error InvalidTokenList
|
28
|
+
end
|
29
|
+
it "should raise NoTemplateFile if template file not exist AND initialized with ['#{$goodtoken}','#{$badtoken}'] tokens list" do
|
30
|
+
expect { Template::new({ :list_token => [$goodtoken,$badtoken] , :template_file => $nonexistantfile})}.to raise_error NoTemplateFile
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "Template <execution> with token '#{$goodtoken}', content = '#{$content}', and ##{$goodtoken}='#{$value}'" do
|
35
|
+
before :all do
|
36
|
+
$test = Template::new({ :list_token => [$goodtoken] , :template_file => $template_file})
|
37
|
+
end
|
38
|
+
context "#content" do
|
39
|
+
specify {expect($test.content).to be_an_instance_of String }
|
40
|
+
it "should have '#{$template}' in #content" do
|
41
|
+
expect( $test.content).to eq($template)
|
42
|
+
end
|
43
|
+
specify {expect($test).to_not respond_to 'content=' }
|
44
|
+
end
|
45
|
+
context "#token_list" do
|
46
|
+
specify {expect($test.list_token).to be_an_instance_of Array }
|
47
|
+
it "should have ['#{$goodtoken}'] in #list_token" do
|
48
|
+
expect($test.list_token).to eq([$goodtoken])
|
49
|
+
end
|
50
|
+
specify { expect($test).to_not respond_to 'token_list=' }
|
51
|
+
end
|
52
|
+
context "virtual (methode_missing) #{$goodtoken}" do
|
53
|
+
specify { expect($test.send($goodtoken.to_sym)).to be_an_instance_of String }
|
54
|
+
specify { expect($test).to respond_to(:name) }
|
55
|
+
specify { $test.name = $value; expect($test.name).to eq($value) }
|
56
|
+
it "should raise ArgumentError if virtual methode '#name=' got non String arguement (accessor)" do
|
57
|
+
expect { $test.name = 1 }.to raise_error ArgumentError
|
58
|
+
end
|
59
|
+
end
|
60
|
+
context "#output" do
|
61
|
+
it "should #output 'Hello Romain !!' if set #name = 'Romain' and msg send #output" do
|
62
|
+
$test.name = 'Romain'
|
63
|
+
expect( $test.output).to eq('Hello Romain !!')
|
64
|
+
end
|
65
|
+
specify { expect($test).to_not respond_to 'output=' }
|
66
|
+
end
|
67
|
+
after :all do
|
68
|
+
File::unlink($template_file) if File::exist?($template_file)
|
69
|
+
$test = nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/templates/report.txt
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prometheus-splash
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Romain GEORGES
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-04-
|
11
|
+
date: 2020-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -38,6 +38,34 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 2.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rufus-scheduler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.6.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.6.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: redis
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 4.1.3
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 4.1.3
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
70
|
name: rake
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,7 +164,8 @@ dependencies:
|
|
136
164
|
- - "~>"
|
137
165
|
- !ruby/object:Gem::Version
|
138
166
|
version: '0.1'
|
139
|
-
description: Prometheus Logs and Batchs supervision over PushGateway
|
167
|
+
description: Prometheus Logs and Batchs supervision over PushGateway and commands
|
168
|
+
orchestration
|
140
169
|
email:
|
141
170
|
- gems@ultragreen.net
|
142
171
|
executables:
|
@@ -150,14 +179,21 @@ files:
|
|
150
179
|
- Rakefile
|
151
180
|
- bin/splash
|
152
181
|
- config/splash.yml
|
182
|
+
- lib/splash/backends.rb
|
183
|
+
- lib/splash/backends/file.rb
|
184
|
+
- lib/splash/backends/redis.rb
|
153
185
|
- lib/splash/commands.rb
|
154
186
|
- lib/splash/config.rb
|
155
187
|
- lib/splash/constants.rb
|
156
188
|
- lib/splash/controller.rb
|
157
189
|
- lib/splash/helpers.rb
|
158
190
|
- lib/splash/logs.rb
|
191
|
+
- lib/splash/orchestrator.rb
|
159
192
|
- lib/splash/templates.rb
|
193
|
+
- lib/splash/transports.rb
|
194
|
+
- lib/splash/transports/rabbitmq.rb
|
160
195
|
- prometheus-splash.gemspec
|
196
|
+
- spec/templates_spec.rb
|
161
197
|
- templates/report.txt
|
162
198
|
- templates/splashd.service
|
163
199
|
- ultragreen_roodi_coding_convention.yml
|
@@ -183,6 +219,7 @@ requirements: []
|
|
183
219
|
rubygems_version: 3.1.2
|
184
220
|
signing_key:
|
185
221
|
specification_version: 4
|
186
|
-
summary: Supervision with Prometheus of Logs and Asynchronous tasks
|
187
|
-
Hosts
|
188
|
-
test_files:
|
222
|
+
summary: Supervision with Prometheus of Logs and Asynchronous tasks orchestration
|
223
|
+
for Services or Hosts
|
224
|
+
test_files:
|
225
|
+
- spec/templates_spec.rb
|