sensu_generator 0.0.25

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,97 @@
1
+ require 'json'
2
+ require 'erb'
3
+ require 'fileutils'
4
+
5
+ module SensuGenerator
6
+ class Generator
7
+ def initialize(services = [])
8
+ @services = services
9
+ @config = Application.config
10
+ @trigger = Application.trigger
11
+ @logger = Application.logger
12
+ end
13
+
14
+ attr_writer :services
15
+ attr_accessor :logger, :config
16
+ attr_reader :connection
17
+
18
+ def generate!
19
+ @processed_files = []
20
+ @services.each do |svc|
21
+ next unless svc.changed?
22
+ svc.checks.each do |check|
23
+ next if check.nil?
24
+ begin
25
+ if check.class == String
26
+ templates_for(check).each do |src|
27
+ filename = config.file_prefix + "#{svc.name}-#{File.basename(src).gsub(/\.(?:.*)/, '.json')}"
28
+ result = merge_with_default_parameters(
29
+ JSON.parse(
30
+ process(template: src, namespace: binding),
31
+ symbolize_names: true
32
+ )
33
+ )
34
+
35
+ if result
36
+ write(filename: filename, data: result)
37
+ @processed_files << filename
38
+ end
39
+ end
40
+ else
41
+ #TODO
42
+ # Implement json parameters parsing
43
+ end
44
+ rescue => e
45
+ logger.warn e
46
+ next
47
+ end
48
+ end
49
+ end
50
+ @processed_files
51
+ end
52
+
53
+ def flush_results
54
+ CheckFile.remove_all_with(config.file_prefix)
55
+ end
56
+
57
+ private
58
+
59
+ def merge_with_default_parameters(hash)
60
+ {}.tap do |res|
61
+ res[:checks] = {}
62
+ hash[:checks].map do |k, v|
63
+ res[:checks][k] = v.merge(config.get[:sensu][:check_default_params])
64
+ end
65
+ end
66
+ end
67
+
68
+ def process(template:, namespace:)
69
+ logger.debug "Processing template #{template}"
70
+ ERB.new(File.read(template)).result(namespace)
71
+ rescue ::Exception => e # Catch all ERB errors
72
+ raise GeneratorError, "Failed to process ERB file #{template}.\n #{e.to_s} \n#{e.backtrace}"
73
+ end
74
+
75
+ def templates_for(check)
76
+ Dir.glob("#{File.expand_path(templates_dir)}/#{check}*")
77
+ end
78
+
79
+ def templates_dir
80
+ config.get[:templates_dir]
81
+ end
82
+
83
+ def write(filename:, data:)
84
+ if config.get[:mode] == 'server'
85
+ CheckFile.new(filename).write(JSON.pretty_generate(data))
86
+ else
87
+ json = JSON.fast_generate({ :filename => filename, :data => data })
88
+ cl = Client.new
89
+ cl.write_file(json)
90
+ cl.close
91
+ end
92
+ rescue
93
+ sleep 1
94
+ retry
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,6 @@
1
+ class ::Hash
2
+ def deep_merge(second)
3
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
4
+ self.merge(second, &merger)
5
+ end
6
+ end
@@ -0,0 +1,28 @@
1
+ require 'logger'
2
+
3
+ module SensuGenerator
4
+ class Logger < ::Logger
5
+ def initialize(params)
6
+ @params = params
7
+ super(@params[:file])
8
+ end
9
+
10
+ def level
11
+ super(@params[:log_level])
12
+ end
13
+
14
+ %w(debug info warn error fatal).each do |level|
15
+ define_method(level) do |msg|
16
+ begin
17
+ if @params[:notify_level] == level
18
+ self.debug "Notifying with msg: #{msg}"
19
+ Application.notifier.notify msg
20
+ end
21
+ rescue => e
22
+ super(e)
23
+ end
24
+ super(msg)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,15 @@
1
+ require 'slack-notifier'
2
+
3
+ module SensuGenerator
4
+ class Notifier
5
+ def initialize(params = {})
6
+ @notifier = if !params.any? { |k,v| v == "" || v.nil? }
7
+ Slack::Notifier.new(params[:url], channel: params[:channel])
8
+ end
9
+ end
10
+
11
+ def notify(msg)
12
+ @notifier.ping msg.to_s if @notifier
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,55 @@
1
+ require 'ruby-supervisor'
2
+ require 'rsync'
3
+
4
+ module SensuGenerator
5
+ class Restarter
6
+ attr_accessor :logger, :config
7
+
8
+ def initialize(servers)
9
+ @delay = 0
10
+ @delay_inc = 600
11
+ @config = Application.config
12
+ @trigger = Application.trigger
13
+ @logger = Application.logger
14
+ @servers = servers
15
+ end
16
+
17
+ def perform_restart
18
+ servers_updated = []
19
+
20
+ @servers.each do |server|
21
+ begin
22
+ if @servers.size < config.get[:sensu][:minimal_to_restart]
23
+ msg = "Sensu-servers count < #{config.get[:sensu][:minimal_to_restart]}. Restart will not be performed. Next try after #{@delay + @delay_inc}s."
24
+ raise RestarterError, msg
25
+ @delay += @delay_inc if @delay < 3600
26
+ sleep @delay
27
+ break
28
+ end
29
+
30
+ server.sync && server.restart && servers_updated << server.address
31
+
32
+ if server == @servers.last
33
+ if servers_updated.size == @servers.size
34
+ @trigger.clear
35
+ else
36
+ raise RestarterError, "Could not synchronize or restart #{(@servers.map(&:address) - servers_updated).join(',')}"
37
+ end
38
+ end
39
+ rescue => e
40
+ logger.error "Restarter error: #{e.inspect} #{e.backtrace}"
41
+ next
42
+ end
43
+ end
44
+ end
45
+
46
+ def need_to_apply_new_configs?
47
+ result = (@trigger.difference_between_touches > 120 || @trigger.last_touch_age > 120) && @trigger.modified_since_last_update?
48
+ logger.debug "\n Trigger:\n\tdifference_between_touches: #{@trigger.difference_between_touches}"\
49
+ "\n\tlast_touch_age: #{@trigger.last_touch_age}"\
50
+ "\n\tmodified_since_last_update?: #{@trigger.modified_since_last_update?}"\
51
+ "\n\tNeeds to apply configuration to Sensu? #{result}"
52
+ result
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,70 @@
1
+ require 'ruby-supervisor'
2
+ require 'rsync'
3
+
4
+ module SensuGenerator
5
+ class SensuServer
6
+ attr_reader :address
7
+ attr_accessor :logger, :config
8
+
9
+ def initialize(address:, config: Application.config, logger: Application.logger)
10
+ @address = address
11
+ @config = config
12
+ @logger = logger
13
+ end
14
+
15
+ def process
16
+ unless @process
17
+ client = RubySupervisor::Client.new(address, 9001,
18
+ user: config.get[:sensu][:supervisor][:user],
19
+ password: config.get[:sensu][:supervisor][:password]
20
+ )
21
+ @process = client.process('sensu-server')
22
+ end
23
+ @process
24
+ end
25
+
26
+ def restart
27
+ process.restart
28
+ logger.info "Send restart command to sensu-server #{address}"
29
+ running?
30
+ end
31
+
32
+ def running?
33
+ 10.times do |t|
34
+ logger.info "Trying to restart server #{address}. Attempt=#{t}."
35
+ if process.state.to_s == 'running'
36
+ logger.info "Sensu-server #{address} was successfully restarted"
37
+ return true
38
+ else
39
+ sleep 1
40
+ if t == 10
41
+ raise SensuServerError, "Sensu-server #{address} restart FAILED"
42
+ end
43
+ end
44
+ end
45
+ rescue SensuServerError
46
+ false
47
+ end
48
+
49
+ def state
50
+ process.state
51
+ end
52
+
53
+ def sync
54
+ begin
55
+ res = Rsync.run("#{config.result_dir}/", "rsync://#{address}/#{config.get[:sensu][:rsync_repo]}", "--delete --recursive")
56
+ status = res.success?
57
+ if status
58
+ msg = "synced"
59
+ logger.info "Sensu-server #{address}: #{msg}"
60
+ else
61
+ msg = "sync FAILED, out: #{res.inspect}"
62
+ raise SensuServerError, "Sensu-server #{address}: #{msg}"
63
+ end
64
+ rescue SensuServerError
65
+ status = false
66
+ end
67
+ status
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,69 @@
1
+ require 'json'
2
+ require 'socket'
3
+
4
+ module SensuGenerator
5
+ class Server
6
+ attr_reader :logger, :config
7
+
8
+ def initialize
9
+ @config = Application.config
10
+ @logger = Application.logger
11
+ @trigger = Application.trigger
12
+ logger.info "Server: starting server."
13
+ listen_and_serve
14
+ end
15
+
16
+ def listen_and_serve
17
+ @server = TCPServer.new(server_addr, server_port)
18
+ logger.info "Server: started server on #{@server.addr}"
19
+
20
+ while client = @server.accept
21
+ logger.info "Server: client #{client.addr} connected"
22
+ Thread.start(client) do
23
+ begin
24
+ data = client.read
25
+ process data
26
+ client.close
27
+ rescue => e
28
+ client&.close
29
+ raise ServerError, "Server: error occured #{e.inspect} #{e.backtrace}\n"
30
+ end
31
+ end
32
+ end
33
+ rescue
34
+ close
35
+ end
36
+
37
+ def close
38
+ @server.close
39
+ @server = nil
40
+ end
41
+
42
+ private
43
+
44
+ def process(data)
45
+ hash = JSON.parse data
46
+
47
+ if hash.key?? 'FLUSH_WITH_PREFIX'
48
+ logger.info "Server: removing files with prefix #{hash['FLUSH_WITH_PREFIX']}"
49
+ CheckFile.remove_all_with(hash['FLUSH_WITH_PREFIX'])
50
+ elsif hash.key?? 'filename'
51
+ filename = hash['filename']
52
+ data = JSON.pretty_generate hash['data']
53
+
54
+ logger.info "Server: received file #{filename}"
55
+ CheckFile.new(filename).write(data)
56
+ end
57
+ rescue
58
+ logger.info "Server: not json data received. Ignoring."
59
+ end
60
+
61
+ def server_port
62
+ @server_port ||= config.get[:server][:port]
63
+ end
64
+
65
+ def server_addr
66
+ @server_addr ||= config.get[:server][:addr]
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,40 @@
1
+ module SensuGenerator
2
+ class Trigger
3
+ attr_reader :last, :previous
4
+
5
+ def initialize
6
+ init_value = Time.now.to_f
7
+ @previous = init_value
8
+ @last = init_value
9
+ end
10
+
11
+ def touch
12
+ logger.info "Touch trigger!"
13
+ @previous = @last
14
+ @last = Time.now.to_f
15
+ end
16
+
17
+ def difference_between_touches
18
+ @last - @previous
19
+ end
20
+
21
+ def last_touch_age
22
+ Time.now.to_f - @last
23
+ end
24
+
25
+ def clear
26
+ logger.info "Clear trigger!"
27
+ time = Time.now.to_f
28
+ @previous = time
29
+ @last = time
30
+ end
31
+
32
+ def modified_since_last_update?
33
+ @previous != @last
34
+ end
35
+
36
+ def logger
37
+ @logger ||= Application.logger
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module SensuGenerator
2
+ VERSION = "0.0.25"
3
+ end
@@ -0,0 +1,18 @@
1
+ require 'sensu_generator/hash'
2
+
3
+ require 'sensu_generator/exceptions'
4
+ require 'sensu_generator/application'
5
+ require 'sensu_generator/config'
6
+ require 'sensu_generator/trigger'
7
+ require 'sensu_generator/logger'
8
+ require 'sensu_generator/notifier'
9
+ require 'sensu_generator/check_file'
10
+ require 'sensu_generator/client'
11
+ require 'sensu_generator/server'
12
+ require 'sensu_generator/sensu_server'
13
+ require 'sensu_generator/version'
14
+ require 'sensu_generator/generator'
15
+ require 'sensu_generator/restarter'
16
+ require 'sensu_generator/consul'
17
+ require 'sensu_generator/consul/consul_state'
18
+ require 'sensu_generator/consul/consul_service'
@@ -0,0 +1,28 @@
1
+ {
2
+ "sensu": {
3
+ "check_default_params": {
4
+ "refresh": 86400,
5
+ "interval": 60,
6
+ "aggregate": true
7
+ },
8
+ "service": "sensu-server",
9
+ "supervisor": {
10
+ "user": "sensu",
11
+ "password": "PASSWORD"
12
+ }
13
+ },
14
+ "slack": {
15
+ "url": "URL",
16
+ "channel": "sensu"
17
+ },
18
+ "logger": {
19
+ "notify_level": "error",
20
+ "log_level": "debug"
21
+ },
22
+ "kv_tags_path":"checks",
23
+ "consul": {
24
+ "url": "http://consul.service.consul:8500"
25
+ },
26
+ "templates_dir": "work/templates",
27
+ "result_dir": "work/result",
28
+ }
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sensu_generator/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sensu_generator"
8
+ spec.version = SensuGenerator::VERSION
9
+ spec.authors = ["Grigory Aksentyev"]
10
+ spec.email = ["grigory.aksentiev@gmail.com"]
11
+
12
+ spec.summary = %q{Sensu check config generator}
13
+ spec.description = %q{Generate sensu check configurations within consul state.}
14
+ spec.homepage = "https://github.com/aksentyev/sensu_generator"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "diplomat", "~> 0.17.0"
22
+ spec.add_dependency "slack-notifier", "~> 1.5.1"
23
+ spec.add_dependency "ruby-supervisor", "~> 0.0.2"
24
+ spec.add_dependency "rsync", "~> 1.0.9"
25
+ spec.add_dependency "daemons", "~> 1.2.3"
26
+
27
+ spec.required_ruby_version = ">= 2.0.0"
28
+
29
+ spec.add_development_dependency "pry", "~> 0.10.3"
30
+ spec.add_development_dependency "bundler", "~> 1.11"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "rspec", "~> 3.0"
33
+ end
metadata ADDED
@@ -0,0 +1,200 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sensu_generator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.25
5
+ platform: ruby
6
+ authors:
7
+ - Grigory Aksentyev
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-08-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: diplomat
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.17.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.17.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: slack-notifier
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 1.5.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 1.5.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: ruby-supervisor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.0.2
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.0.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: rsync
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.9
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.9
69
+ - !ruby/object:Gem::Dependency
70
+ name: daemons
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.2.3
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.2.3
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.10.3
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.10.3
97
+ - !ruby/object:Gem::Dependency
98
+ name: bundler
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.11'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.11'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '10.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '10.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.0'
139
+ description: Generate sensu check configurations within consul state.
140
+ email:
141
+ - grigory.aksentiev@gmail.com
142
+ executables:
143
+ - sensu-generator
144
+ extensions: []
145
+ extra_rdoc_files: []
146
+ files:
147
+ - ".gitignore"
148
+ - ".rspec"
149
+ - ".travis.yml"
150
+ - Gemfile
151
+ - README.md
152
+ - Rakefile
153
+ - bin/console
154
+ - bin/setup
155
+ - exe/sensu-generator
156
+ - lib/sensu_generator.rb
157
+ - lib/sensu_generator/application.rb
158
+ - lib/sensu_generator/check_file.rb
159
+ - lib/sensu_generator/client.rb
160
+ - lib/sensu_generator/config.rb
161
+ - lib/sensu_generator/consul.rb
162
+ - lib/sensu_generator/consul/consul_service.rb
163
+ - lib/sensu_generator/consul/consul_state.rb
164
+ - lib/sensu_generator/exceptions.rb
165
+ - lib/sensu_generator/generator.rb
166
+ - lib/sensu_generator/hash.rb
167
+ - lib/sensu_generator/logger.rb
168
+ - lib/sensu_generator/notifier.rb
169
+ - lib/sensu_generator/restarter.rb
170
+ - lib/sensu_generator/sensu_server.rb
171
+ - lib/sensu_generator/server.rb
172
+ - lib/sensu_generator/trigger.rb
173
+ - lib/sensu_generator/version.rb
174
+ - sensu-generator.config.example
175
+ - sensu_generator.gemspec
176
+ homepage: https://github.com/aksentyev/sensu_generator
177
+ licenses: []
178
+ metadata: {}
179
+ post_install_message:
180
+ rdoc_options: []
181
+ require_paths:
182
+ - lib
183
+ required_ruby_version: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: 2.0.0
188
+ required_rubygems_version: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: '0'
193
+ requirements: []
194
+ rubyforge_project:
195
+ rubygems_version: 2.6.3
196
+ signing_key:
197
+ specification_version: 4
198
+ summary: Sensu check config generator
199
+ test_files: []
200
+ has_rdoc: