prometheus-config-builder 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/README.md +22 -0
- data/Rakefile +35 -0
- data/bin/prometheus-config-builder +93 -0
- data/lib/prometheus-config-builder.rb +1 -0
- data/lib/prometheus-config-builder/logger.rb +15 -0
- data/lib/prometheus-config-builder/prometheus-config-builder.rb +247 -0
- data/lib/prometheus-config-builder/scrape_ecs.rb +130 -0
- data/lib/prometheus-config-builder/scrape_passthrough.rb +11 -0
- data/lib/prometheus-config-builder/version.rb +4 -0
- data/prometheus-config-builder.gemspec +24 -0
- data/test/configdiscovery.rb +121 -0
- data/test/data/prometheus.yaml +22 -0
- data/test/data/test1.yaml +17 -0
- data/test/data/test2.yaml +17 -0
- data/test/test_helper.rb +4 -0
- metadata +135 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7b091e64ec338a673b726872bf72db278470f2dc
|
4
|
+
data.tar.gz: 3b2ec91cd1437b01b04cc4f221bacfd6a01f953c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9d77a5e66080e86f60637b161a4aa5ef8c5880a1af2f43eedce67418ab95cb4d941e675148856510e95f6164217df1567a6ad3eb3bd1f1af55e38822f60a7145
|
7
|
+
data.tar.gz: 9e862045c6aa38b43a8312715b8d6f4abc064e36a6f59af19ddbd64b23faffadee9f409b376b43ad2fd92dd68fdf490b3577ca21aec349704d5652bf1e6d08e6
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
## Description
|
3
|
+
|
4
|
+
prometheus-config-builder can be used to build main prometheus.yaml and related rules yaml files from a combination of different sources such as files in S3 buckets.
|
5
|
+
|
6
|
+
It also supports some extended ways to build scrape_configs such as fetching AWS ECS service endpoints.
|
7
|
+
|
8
|
+
## Quickstart
|
9
|
+
|
10
|
+
1) Install via Rubygems:
|
11
|
+
|
12
|
+
$ gem install prometheus-config-builder
|
13
|
+
|
14
|
+
2) Create a prometheus.yaml to be used as a base template
|
15
|
+
|
16
|
+
3) Create one or more source yaml files (See test/data/test*.yaml as examples) to be included into your prometheus configuration.
|
17
|
+
|
18
|
+
4) Start the program: prometheus-config-builder --prometheus-src=test/data/prometheus.yaml --dst-dir=tmp --path=test/data/test*.yaml
|
19
|
+
|
20
|
+
The program will re-create the config files every n seconds. If you set --pgrep=[STRING] it will try to find your Prometheus instance pid and send a SIGHUP to Prometheus so that it reloads new configurations. Try the --pgrep search string with "pgrep -f [STRING]" to be use you will get just one correct result.
|
21
|
+
|
22
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rubygems/tasks'
|
5
|
+
Gem::Tasks.new
|
6
|
+
rescue LoadError => e
|
7
|
+
warn e.message
|
8
|
+
end
|
9
|
+
|
10
|
+
begin
|
11
|
+
require 'yard'
|
12
|
+
YARD::Rake::YardocTask.new
|
13
|
+
task :doc => :yard
|
14
|
+
rescue LoadError => e
|
15
|
+
warn e.message
|
16
|
+
end
|
17
|
+
|
18
|
+
task :default => :test
|
19
|
+
|
20
|
+
Rake::TestTask.new do |t|
|
21
|
+
t.libs = ['lib', 'test']
|
22
|
+
t.name = 'test'
|
23
|
+
t.warning = true
|
24
|
+
t.test_files = FileList['test/*.rb']
|
25
|
+
end
|
26
|
+
|
27
|
+
FileList['test/*.rb'].each do |p|
|
28
|
+
name = p.split('/').last.split('.').first
|
29
|
+
Rake::TestTask.new do |t|
|
30
|
+
t.libs = ['lib', 'test']
|
31
|
+
t.name = "test:#{name}"
|
32
|
+
t.warning = true
|
33
|
+
t.test_files = [p]
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
|
5
|
+
require 'logger'
|
6
|
+
require 'optparse'
|
7
|
+
require 'prometheus-config-builder'
|
8
|
+
require 'pp'
|
9
|
+
require 'yaml'
|
10
|
+
require 'json'
|
11
|
+
|
12
|
+
config = {}
|
13
|
+
config[:paths] ||= []
|
14
|
+
config[:root] ||= 'test/data'
|
15
|
+
config[:every] = 60
|
16
|
+
config[:pgrep] = nil
|
17
|
+
|
18
|
+
# Defaults
|
19
|
+
op = OptionParser.new do |o|
|
20
|
+
o.banner = "Usage: #{$PROGRAM_NAME} [OPTIONS]"
|
21
|
+
o.on('--prometheus-src=[FILE]', 'Location of prometheus.yaml source config') do |arg|
|
22
|
+
config[:prometheus_src] = arg
|
23
|
+
end
|
24
|
+
o.on('--dst-dir=[FILE]', 'Destination directory') do |arg|
|
25
|
+
config[:dst_dir] = arg
|
26
|
+
end
|
27
|
+
o.on('--path=[PATH]', 'Directory or S3 location for individual config yaml files. Can be set multiple times') do |arg|
|
28
|
+
config[:paths] << arg
|
29
|
+
end
|
30
|
+
o.on('--http PORT', Integer,
|
31
|
+
'Port to listen on for http for Prometheus /metrics api') do |port|
|
32
|
+
abort 'Invalid port' if port < 1 || port > 65535
|
33
|
+
config[:http] = http
|
34
|
+
end
|
35
|
+
o.on('-v', '--verbose', 'Enable verbose output') do
|
36
|
+
config[:verbose] = true
|
37
|
+
end
|
38
|
+
o.on('--every=[seconds]', 'Rerun every n seconds') do |seconds|
|
39
|
+
config[:every] = seconds
|
40
|
+
end
|
41
|
+
o.on('--pgrep=[string]', 'pgrep -f [string] for finding Prometheus process for SIGHUP') do |str|
|
42
|
+
config[:pgrep] = str
|
43
|
+
end
|
44
|
+
end
|
45
|
+
op.parse!
|
46
|
+
|
47
|
+
if config[:verbose]
|
48
|
+
PP.pp(config, STDERR)
|
49
|
+
end
|
50
|
+
|
51
|
+
abort "You need to set --prometheus-src" if !config[:prometheus_src]
|
52
|
+
abort "You need to set --dst_dir" if !config[:dst_dir]
|
53
|
+
|
54
|
+
log = Logger.new(STDOUT)
|
55
|
+
log.level = config[:verbose] ? Logger::DEBUG : Logger::INFO
|
56
|
+
log.formatter = lambda do |s, d, p, m|
|
57
|
+
"#{d.strftime('%Y-%m-%d %H:%M:%S.%3N')} | #{s.ljust(5)} | #{m}\n"
|
58
|
+
end
|
59
|
+
config[:logger] = log
|
60
|
+
PrometheusConfigBuilderLogger.logger = log
|
61
|
+
|
62
|
+
if !File.exist?(config[:dst_dir])
|
63
|
+
log.fatal("The --dst-dir #{config[:dst_dir]} does not exists. Create it first.")
|
64
|
+
exit!(1)
|
65
|
+
end
|
66
|
+
|
67
|
+
builder = PrometheusConfigBuilder::Builder.new(config[:prometheus_src], config[:dst_dir])
|
68
|
+
|
69
|
+
config[:paths].each do |path|
|
70
|
+
builder.add_path(path)
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
loop do
|
75
|
+
something_changed = builder.write_out()
|
76
|
+
|
77
|
+
# Send a SIGHUP signal to Prometheus so that it knows to reload config files
|
78
|
+
if something_changed && config[:pgrep] != nil
|
79
|
+
pid = `pgrep -f "#{config[:pgrep]}"`.split("\n").first.to_i
|
80
|
+
if pid
|
81
|
+
log.info("Sending SIGHUP signal to Prometheus at pid #{pid}.")
|
82
|
+
Process.kill "HUP", pid
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
log.info("Sleeping for #{config[:every]} second and starting then again.")
|
87
|
+
if log.level == Logger::INFO
|
88
|
+
log.info("Setting log level to WARN for the rest of the program execution. Use -v to overwrite this.")
|
89
|
+
log.level = Logger::WARN
|
90
|
+
end
|
91
|
+
|
92
|
+
sleep(config[:every])
|
93
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'prometheus-config-builder/prometheus-config-builder'
|
@@ -0,0 +1,247 @@
|
|
1
|
+
# After https://www.ietf.org/rfc/rfc1350.txt
|
2
|
+
#
|
3
|
+
|
4
|
+
require 'erb'
|
5
|
+
require 'net/http'
|
6
|
+
require 'uri'
|
7
|
+
require 'json'
|
8
|
+
require 'pp'
|
9
|
+
require 'yaml'
|
10
|
+
require 'time'
|
11
|
+
require 'fileutils'
|
12
|
+
require 'digest'
|
13
|
+
require 'logger'
|
14
|
+
require_relative './logger.rb'
|
15
|
+
require_relative './scrape_passthrough.rb'
|
16
|
+
require_relative './scrape_ecs.rb'
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
module PrometheusConfigBuilder
|
21
|
+
|
22
|
+
|
23
|
+
class ConfigFile
|
24
|
+
include PrometheusConfigBuilderLogger
|
25
|
+
@data = {}
|
26
|
+
|
27
|
+
attr_reader :basename
|
28
|
+
|
29
|
+
def open(filename)
|
30
|
+
filename = File.expand_path(filename)
|
31
|
+
@source = "file://#{filename}"
|
32
|
+
@basename = File.basename(filename)
|
33
|
+
@data = YAML.load_file(filename)
|
34
|
+
end
|
35
|
+
|
36
|
+
def load(file)
|
37
|
+
@source = "file://#{file[:filename]}"
|
38
|
+
@basename = file[:filename]
|
39
|
+
@data = YAML.load(file[:contents])
|
40
|
+
end
|
41
|
+
|
42
|
+
def config_rules
|
43
|
+
@data['config_rules']
|
44
|
+
end
|
45
|
+
|
46
|
+
def scrape_configs
|
47
|
+
@data['scrape_configs']
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_scrape_configs(dst_prefix)
|
51
|
+
configs = []
|
52
|
+
|
53
|
+
@data['scrape_configs'].each do |config|
|
54
|
+
case config["type"]
|
55
|
+
when "passthrough"
|
56
|
+
configs << ScrapeConfigPassthrough::handle(config)
|
57
|
+
when "ecs-tasks"
|
58
|
+
configs << ScrapeConfigECS::handle(@basename, config, dst_prefix)
|
59
|
+
else
|
60
|
+
raise "Unknown scrape_config type #{config["type"]}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
return configs
|
65
|
+
end
|
66
|
+
|
67
|
+
def write_rules(path)
|
68
|
+
filename = path + "/" + @basename
|
69
|
+
File.open(filename, "w") do |file|
|
70
|
+
file.write("# AUTOGENERATED FILE, DO NOT MODIFY\n")
|
71
|
+
file.write("#\n")
|
72
|
+
file.write("# This file was automatically generated by prometheus-config-from-s3 at #{Time.now.utc.iso8601}\n")
|
73
|
+
file.write("# based on #{@source}\n")
|
74
|
+
file.write("#\n\n")
|
75
|
+
file.write(config_rules.to_yaml)
|
76
|
+
end
|
77
|
+
|
78
|
+
return Digest::SHA256.digest config_rules.to_yaml
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class ConfigDiscover
|
83
|
+
include PrometheusConfigBuilderLogger
|
84
|
+
def discover(glob)
|
85
|
+
|
86
|
+
files = []
|
87
|
+
if m = glob.match(/s3:\/\/([^\/]+)\/(.*)/)
|
88
|
+
bucket_name = m[1]
|
89
|
+
prefix = m[2]
|
90
|
+
|
91
|
+
x = $VERBOSE
|
92
|
+
$VERBOSE = nil
|
93
|
+
require 'aws-sdk'
|
94
|
+
s3 = Aws::S3::Client.new
|
95
|
+
$VERBOSE = x
|
96
|
+
key = []
|
97
|
+
resp = s3.list_objects_v2({
|
98
|
+
bucket: bucket_name,
|
99
|
+
prefix: prefix
|
100
|
+
})
|
101
|
+
|
102
|
+
resp.contents.each do |file|
|
103
|
+
contents = s3.get_object({
|
104
|
+
key: file.key,
|
105
|
+
bucket: bucket_name
|
106
|
+
})
|
107
|
+
|
108
|
+
logger.debug("Found file \"#{file.key}\" from S3 bucket #{bucket_name}")
|
109
|
+
files << {
|
110
|
+
filename: File.basename(file.key),
|
111
|
+
contents: contents.body.string,
|
112
|
+
}
|
113
|
+
end
|
114
|
+
else
|
115
|
+
Dir.glob(glob).each do |file|
|
116
|
+
files << {
|
117
|
+
filename: File.basename(file),
|
118
|
+
contents: File.read(file)
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
return files
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
class ConfigFiles
|
130
|
+
|
131
|
+
def initialize(prometheus_yaml_filename)
|
132
|
+
@files = []
|
133
|
+
@prometheus_yaml_filename = prometheus_yaml_filename
|
134
|
+
@rules_dir = nil
|
135
|
+
@scrape_files_dir = nil
|
136
|
+
end
|
137
|
+
|
138
|
+
def add(file)
|
139
|
+
@files << file
|
140
|
+
end
|
141
|
+
|
142
|
+
def write_rules(path)
|
143
|
+
|
144
|
+
hash = ""
|
145
|
+
@files.each do |file|
|
146
|
+
hash += file.write_rules(path)
|
147
|
+
end
|
148
|
+
|
149
|
+
return hash
|
150
|
+
end
|
151
|
+
|
152
|
+
def set_rules_dir(rules_dir)
|
153
|
+
@rules_dir = rules_dir
|
154
|
+
end
|
155
|
+
|
156
|
+
def set_scrape_files_dir(scrape_files_dir)
|
157
|
+
@scrape_files_dir = scrape_files_dir
|
158
|
+
end
|
159
|
+
|
160
|
+
def write_prometheus_yaml(destination, scrape_files_dir)
|
161
|
+
data = YAML.load_file(@prometheus_yaml_filename)
|
162
|
+
|
163
|
+
if !data["scrape_configs"]
|
164
|
+
data["scrape_configs"] = []
|
165
|
+
end
|
166
|
+
|
167
|
+
FileUtils.mkdir_p(scrape_files_dir) if !File.exist?(scrape_files_dir)
|
168
|
+
|
169
|
+
@files.each do |file|
|
170
|
+
configs = file.get_scrape_configs(scrape_files_dir + "/" + File.basename(file.basename, ".*"))
|
171
|
+
data["scrape_configs"].push(*configs)
|
172
|
+
end
|
173
|
+
|
174
|
+
if !data["rule_files"] or data["rule_files"].class != Array
|
175
|
+
data["rule_files"] = []
|
176
|
+
end
|
177
|
+
|
178
|
+
if @rules_dir != nil && !data["rule_files"].include?(@rules_dir)
|
179
|
+
data["rule_files"] << File.expand_path(@rules_dir) + "/*.yaml"
|
180
|
+
end
|
181
|
+
|
182
|
+
File.open(destination, "w") do |file|
|
183
|
+
file.write("# AUTOGENERATED FILE, DO NOT MODIFY\n")
|
184
|
+
file.write("#\n")
|
185
|
+
file.write("# This file was automatically generated by prometheus-config-from-s3 at #{Time.now.utc.iso8601}\n")
|
186
|
+
file.write("# based on #{@prometheus_yaml_filename}\n")
|
187
|
+
file.write("#\n\n")
|
188
|
+
file.write(data.to_yaml)
|
189
|
+
end
|
190
|
+
|
191
|
+
return Digest::SHA256.digest data.to_yaml
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class Builder
|
196
|
+
include PrometheusConfigBuilderLogger
|
197
|
+
|
198
|
+
def initialize(prometheus_src, dst_dir)
|
199
|
+
@prometheus_src = prometheus_src
|
200
|
+
@dst_dir = dst_dir
|
201
|
+
|
202
|
+
@discoverer = ConfigDiscover.new
|
203
|
+
@paths = []
|
204
|
+
@last_hash = ""
|
205
|
+
end
|
206
|
+
|
207
|
+
def add_path(path)
|
208
|
+
@paths << path
|
209
|
+
end
|
210
|
+
|
211
|
+
def write_out
|
212
|
+
hash = ""
|
213
|
+
cfs = ConfigFiles.new(@prometheus_src)
|
214
|
+
files = []
|
215
|
+
@paths.each do |path|
|
216
|
+
found_files = @discoverer.discover(path)
|
217
|
+
logger.info("Found #{files.length} files from path #{path}")
|
218
|
+
files.push(*found_files)
|
219
|
+
end
|
220
|
+
|
221
|
+
files.each do |file|
|
222
|
+
cf = ConfigFile.new
|
223
|
+
cf.load(file)
|
224
|
+
cfs.add(cf)
|
225
|
+
end
|
226
|
+
|
227
|
+
rules_dir = @dst_dir + "/rules"
|
228
|
+
scrape_files_dir = @dst_dir + "/scrape_files"
|
229
|
+
cfs.set_rules_dir(rules_dir)
|
230
|
+
cfs.set_scrape_files_dir(scrape_files_dir)
|
231
|
+
hash += cfs.write_prometheus_yaml(@dst_dir + "/prometheus.yaml", scrape_files_dir)
|
232
|
+
|
233
|
+
FileUtils.mkdir_p(rules_dir) unless File.exist?(rules_dir)
|
234
|
+
FileUtils.mkdir_p(scrape_files_dir) unless File.exist?(scrape_files_dir)
|
235
|
+
hash += cfs.write_rules(rules_dir)
|
236
|
+
|
237
|
+
# Determine if something had changed since the last write_out based on the generated hashes
|
238
|
+
if hash != @last_hash
|
239
|
+
@last_hash = hash
|
240
|
+
return true
|
241
|
+
else
|
242
|
+
return false
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require_relative './logger.rb'
|
3
|
+
|
4
|
+
module PrometheusConfigBuilder
|
5
|
+
|
6
|
+
class ScrapeConfigECS
|
7
|
+
extend PrometheusConfigBuilderLogger
|
8
|
+
|
9
|
+
def self.handle(basename, config, dst_prefix)
|
10
|
+
|
11
|
+
x = $VERBOSE
|
12
|
+
$VERBOSE = nil
|
13
|
+
require 'aws-sdk'
|
14
|
+
|
15
|
+
if !config["region"]
|
16
|
+
logger.warn("File #{basename}: the scrape_configs of type:ecs-tasks doesn't have \"region\" field set. Ignoring!")
|
17
|
+
return nil
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
@@ecs = Aws::ECS::Client.new({
|
22
|
+
region:config["region"]
|
23
|
+
})
|
24
|
+
$VERBOSE = x
|
25
|
+
tasks = get_tasks(config["cluster"], config["service"])
|
26
|
+
ips = get_task_ips(config["cluster"], tasks)
|
27
|
+
|
28
|
+
targets = []
|
29
|
+
ips.each do |ip|
|
30
|
+
if config["metrics_port"]
|
31
|
+
targets << ip + ":" + config["metrics_port"].to_s
|
32
|
+
else
|
33
|
+
targets << ip
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
data = [
|
38
|
+
{
|
39
|
+
"targets" => targets,
|
40
|
+
"labels" => config["labels"]
|
41
|
+
}
|
42
|
+
]
|
43
|
+
|
44
|
+
if !config["job_name"]
|
45
|
+
logger.warn("File #{basename}: the scrape_configs of type:ecs-tasks doesn't have \"job_name\" field set. Ignoring!")
|
46
|
+
return nil
|
47
|
+
end
|
48
|
+
|
49
|
+
file = File.expand_path(dst_prefix + "_" + config["job_name"] + ".json")
|
50
|
+
File.open(file, "w") do |file|
|
51
|
+
file.write(data.to_json)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Make copy of the settings and remove our custom properties from it.
|
55
|
+
# The rest user can set just as he wants according to the Prometheus schema.
|
56
|
+
# See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config
|
57
|
+
settings = config.clone
|
58
|
+
settings.delete("type")
|
59
|
+
settings.delete("cluster")
|
60
|
+
settings.delete("service")
|
61
|
+
settings.delete("metrics_port")
|
62
|
+
settings.delete("labels")
|
63
|
+
settings.delete("region")
|
64
|
+
settings["file_sd_configs"] = [
|
65
|
+
"files" => [
|
66
|
+
file
|
67
|
+
]
|
68
|
+
]
|
69
|
+
|
70
|
+
return settings
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.get_tasks(cluster, service_name)
|
74
|
+
tasks = []
|
75
|
+
last_result = Aws::ECS::Types::ListTasksResponse.new
|
76
|
+
last_result.next_token = nil
|
77
|
+
loop do
|
78
|
+
begin
|
79
|
+
options = {
|
80
|
+
cluster: cluster,
|
81
|
+
service_name: service_name,
|
82
|
+
next_token: last_result.next_token
|
83
|
+
}
|
84
|
+
last_result = @@ecs.list_tasks(options)
|
85
|
+
if last_result && last_result.task_arns
|
86
|
+
tasks.push(*last_result.task_arns)
|
87
|
+
end
|
88
|
+
rescue Aws::ECS::Errors::ServiceError => e
|
89
|
+
print "Error listing ecs tasks: #{e}"
|
90
|
+
sleep(1)
|
91
|
+
next
|
92
|
+
end
|
93
|
+
break if !last_result.next_token
|
94
|
+
end
|
95
|
+
|
96
|
+
return tasks
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.get_task_ips(cluster, tasks)
|
100
|
+
ips = []
|
101
|
+
task_chunk = tasks.pop(100)
|
102
|
+
loop do
|
103
|
+
begin
|
104
|
+
options = {
|
105
|
+
cluster: cluster,
|
106
|
+
tasks: task_chunk,
|
107
|
+
}
|
108
|
+
result = @@ecs.describe_tasks(options)
|
109
|
+
if result && result.tasks
|
110
|
+
result.tasks.each do |task|
|
111
|
+
# FIXME: This assumes somewhat on the ip structure.
|
112
|
+
ip = task.containers[0].network_interfaces[0].private_ipv_4_address
|
113
|
+
ips << ip
|
114
|
+
end
|
115
|
+
end
|
116
|
+
rescue Aws::ECS::Errors::ServiceError => e
|
117
|
+
print "Error listing ecs tasks: #{e}\n"
|
118
|
+
sleep(1)
|
119
|
+
next
|
120
|
+
end
|
121
|
+
break if tasks.length == 0
|
122
|
+
task_chunk = tasks.pop(100)
|
123
|
+
end
|
124
|
+
return ips
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
#
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'prometheus-config-builder'
|
6
|
+
s.version = '0.0.11'
|
7
|
+
s.date = Time.now
|
8
|
+
|
9
|
+
s.summary = %q{Template based config generation}
|
10
|
+
s.files = `git ls-files`.split("\n")
|
11
|
+
s.executables = ['prometheus-config-builder']
|
12
|
+
s.test_files = s.files.grep(%r{^test/})
|
13
|
+
s.require_paths = ['lib']
|
14
|
+
s.authors = "Juho Mäkinen juho.makinen@gmail.com"
|
15
|
+
|
16
|
+
s.required_ruby_version = '>= 2.0.0'
|
17
|
+
|
18
|
+
s.add_dependency 'aws-sdk', '~> 3'
|
19
|
+
|
20
|
+
s.add_development_dependency 'rubygems-tasks', '~> 0.2'
|
21
|
+
s.add_development_dependency 'minitest', '~> 5.4'
|
22
|
+
s.add_development_dependency 'minitest-reporters'
|
23
|
+
s.add_development_dependency 'rake', '~> 10.0'
|
24
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
|
3
|
+
require 'prometheus-config-builder'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
class ConfigDiscoveryTest < Minitest::Test
|
7
|
+
|
8
|
+
class MockDiscovery < PrometheusConfigBuilder::ConfigFile
|
9
|
+
|
10
|
+
attr_accessor :files
|
11
|
+
@files = []
|
12
|
+
|
13
|
+
def get_files()
|
14
|
+
@files
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_can_get_rules_from_file
|
19
|
+
f = PrometheusConfigBuilder::ConfigFile.new()
|
20
|
+
f.open('test/data/test1.yaml')
|
21
|
+
|
22
|
+
assert_equal "from_terraform.generic.rules", f.config_rules[0]['name']
|
23
|
+
assert_equal "passthrough", f.scrape_configs[0]['type']
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_scrape_config
|
27
|
+
f = PrometheusConfigBuilder::ConfigFile.new()
|
28
|
+
f.open('test/data/test1.yaml')
|
29
|
+
|
30
|
+
configs = f.get_scrape_configs('tmp/test')
|
31
|
+
|
32
|
+
assert_equal "discovery 1", configs[0]["job_name"]
|
33
|
+
assert_equal "localhost:9090", configs[0]["static_configs"][0]["targets"][0]
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_can_write_rules_to_file
|
38
|
+
f = PrometheusConfigBuilder::ConfigFile.new()
|
39
|
+
f.open('test/data/test1.yaml')
|
40
|
+
|
41
|
+
filename = './tmp/test1.yaml'
|
42
|
+
FileUtils.mkdir('tmp') if not File.directory?('tmp')
|
43
|
+
File.delete(filename) if File.exist?(filename)
|
44
|
+
f.write_rules('./tmp')
|
45
|
+
|
46
|
+
contents = YAML.load_file(filename)
|
47
|
+
|
48
|
+
assert_equal "from_terraform.generic.rules", contents[0]["name"]
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_can_get_all_files_from_mock
|
53
|
+
|
54
|
+
f1 = PrometheusConfigBuilder::ConfigFile.new
|
55
|
+
f2 = PrometheusConfigBuilder::ConfigFile.new
|
56
|
+
f1.open('test/data/test1.yaml')
|
57
|
+
f2.open('test/data/test2.yaml')
|
58
|
+
|
59
|
+
d = PrometheusConfigBuilder::ConfigFiles.new('test/data/prometheus.yaml')
|
60
|
+
d.add(f1)
|
61
|
+
d.add(f2)
|
62
|
+
|
63
|
+
FileUtils.rm_r('tmp/test_can_get_all_files_from_mock') if File.exist?('tmp/test_can_get_all_files_from_mock')
|
64
|
+
FileUtils.mkdir_p('tmp/test_can_get_all_files_from_mock')
|
65
|
+
|
66
|
+
d.write_rules('./tmp/test_can_get_all_files_from_mock')
|
67
|
+
|
68
|
+
# Test over all files
|
69
|
+
filename = './tmp/test_can_get_all_files_from_mock/test1.yaml'
|
70
|
+
contents = YAML.load_file(filename)
|
71
|
+
assert_equal "from_terraform.generic.rules", contents[0]["name"]
|
72
|
+
|
73
|
+
filename = './tmp/test_can_get_all_files_from_mock/test2.yaml'
|
74
|
+
contents = YAML.load_file(filename)
|
75
|
+
assert_equal "something_else", contents[0]["name"]
|
76
|
+
|
77
|
+
# Write main prometheus.yaml
|
78
|
+
filename = './tmp/test_can_get_all_files_from_mock/prometheus.yaml'
|
79
|
+
d.write_prometheus_yaml(filename, 'tmp/')
|
80
|
+
|
81
|
+
contents = YAML.load_file(filename)
|
82
|
+
assert_equal "15s", contents["global"]["scrape_interval"]
|
83
|
+
assert_equal "prometheus", contents["scrape_configs"][0]["job_name"]
|
84
|
+
assert_equal "alertmanager", contents["scrape_configs"][1]["job_name"]
|
85
|
+
|
86
|
+
assert_equal "discovery 1", contents["scrape_configs"][2]["job_name"]
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_discover_config_files
|
91
|
+
x = PrometheusConfigBuilder::ConfigDiscover.new
|
92
|
+
|
93
|
+
files = x.discover("./test/data/test*.yaml")
|
94
|
+
assert_equal 2, files.length
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_config_builder
|
99
|
+
x = PrometheusConfigBuilder::Builder.new("./test/data/prometheus.yaml", "tmp/")
|
100
|
+
x.add_path("./test/data/test*.yaml")
|
101
|
+
|
102
|
+
ret = x.write_out()
|
103
|
+
assert_equal true, ret # true as in config files were modified or at least rewritten
|
104
|
+
|
105
|
+
assert_equal true, File.exist?("tmp/prometheus.yaml")
|
106
|
+
assert_equal true, File.exist?("tmp/rules/test1.yaml")
|
107
|
+
assert_equal true, File.exist?("tmp/rules/test2.yaml")
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_config_builder_knows_when_to_refresh
|
112
|
+
x = PrometheusConfigBuilder::Builder.new("./test/data/prometheus.yaml", "tmp/")
|
113
|
+
x.add_path("./test/data/test*.yaml")
|
114
|
+
|
115
|
+
x.write_out()
|
116
|
+
ret = x.write_out()
|
117
|
+
assert_equal false, ret # Nothing had changed, no need to rewrite
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
global:
|
2
|
+
scrape_interval: 15s
|
3
|
+
evaluation_interval: 15s
|
4
|
+
|
5
|
+
alerting:
|
6
|
+
alertmanagers:
|
7
|
+
- static_configs:
|
8
|
+
- targets:
|
9
|
+
- localhost:9093
|
10
|
+
|
11
|
+
rule_files:
|
12
|
+
- "/mnt/data/prometheus/rules/*.yaml"
|
13
|
+
|
14
|
+
scrape_configs:
|
15
|
+
- job_name: prometheus
|
16
|
+
static_configs:
|
17
|
+
- targets:
|
18
|
+
- localhost:9090
|
19
|
+
- job_name: alertmanager
|
20
|
+
static_configs:
|
21
|
+
- targets:
|
22
|
+
- localhost:9093
|
@@ -0,0 +1,17 @@
|
|
1
|
+
scrape_configs:
|
2
|
+
- type: passthrough
|
3
|
+
job_name: discovery 1
|
4
|
+
static_configs:
|
5
|
+
- targets: ['localhost:9090']
|
6
|
+
|
7
|
+
|
8
|
+
config_rules:
|
9
|
+
- name: from_terraform.generic.rules
|
10
|
+
rules:
|
11
|
+
- alert: DeadMansSwitch
|
12
|
+
annotations:
|
13
|
+
description: This is a DeadMansSwitch meant to ensure that the entire Alerting pipeline is functional.
|
14
|
+
summary: Alerting DeadMansSwitch
|
15
|
+
expr: vector(1)
|
16
|
+
labels:
|
17
|
+
severity: none
|
@@ -0,0 +1,17 @@
|
|
1
|
+
scrape_configs:
|
2
|
+
- type: passthrough
|
3
|
+
job_name: discovery 2
|
4
|
+
static_configs:
|
5
|
+
- targets: ['localhost:9090']
|
6
|
+
|
7
|
+
|
8
|
+
config_rules:
|
9
|
+
- name: something_else
|
10
|
+
rules:
|
11
|
+
- alert: DeadMansSwitch
|
12
|
+
annotations:
|
13
|
+
description: This is a DeadMansSwitch meant to ensure that the entire Alerting pipeline is functional.
|
14
|
+
summary: Alerting DeadMansSwitch
|
15
|
+
expr: vector(1)
|
16
|
+
labels:
|
17
|
+
severity: none
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: prometheus-config-builder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.11
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Juho Mäkinen juho.makinen@gmail.com
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-09-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aws-sdk
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubygems-tasks
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.2'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.4'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.4'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest-reporters
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
description:
|
84
|
+
email:
|
85
|
+
executables:
|
86
|
+
- prometheus-config-builder
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- Gemfile
|
92
|
+
- README.md
|
93
|
+
- Rakefile
|
94
|
+
- bin/prometheus-config-builder
|
95
|
+
- lib/prometheus-config-builder.rb
|
96
|
+
- lib/prometheus-config-builder/logger.rb
|
97
|
+
- lib/prometheus-config-builder/prometheus-config-builder.rb
|
98
|
+
- lib/prometheus-config-builder/scrape_ecs.rb
|
99
|
+
- lib/prometheus-config-builder/scrape_passthrough.rb
|
100
|
+
- lib/prometheus-config-builder/version.rb
|
101
|
+
- prometheus-config-builder.gemspec
|
102
|
+
- test/configdiscovery.rb
|
103
|
+
- test/data/prometheus.yaml
|
104
|
+
- test/data/test1.yaml
|
105
|
+
- test/data/test2.yaml
|
106
|
+
- test/test_helper.rb
|
107
|
+
homepage:
|
108
|
+
licenses: []
|
109
|
+
metadata: {}
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 2.0.0
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
requirements: []
|
125
|
+
rubyforge_project:
|
126
|
+
rubygems_version: 2.5.2
|
127
|
+
signing_key:
|
128
|
+
specification_version: 4
|
129
|
+
summary: Template based config generation
|
130
|
+
test_files:
|
131
|
+
- test/configdiscovery.rb
|
132
|
+
- test/data/prometheus.yaml
|
133
|
+
- test/data/test1.yaml
|
134
|
+
- test/data/test2.yaml
|
135
|
+
- test/test_helper.rb
|