sensu_generator 0.0.25

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.
@@ -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: