1
0
Fork 0
mirror of https://github.com/prometheus-operator/prometheus-operator.git synced 2025-04-20 19:29:10 +00:00

prometheus-watcher: initial commit

Watching a configmap which contains a list of configmap names and
requesting those configmaps and writing them transactionally to disk
works. Watching general prometheus config remounts still to do and
reloading prometheus also to be done.
This commit is contained in:
Frederic Branczyk 2017-03-14 16:19:54 +01:00
parent 4d916cf4a8
commit e56f496d1d
No known key found for this signature in database
GPG key ID: CA14788B1E48B256
6 changed files with 347 additions and 0 deletions

1
contrib/prometheus-watcher/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
prometheus-watcher

View file

@ -0,0 +1,5 @@
FROM quay.io/prometheus/busybox:latest
ADD prometheus-watcher /bin/prometheus-watcher
ENTRYPOINT ["/bin/prometheus-watcher"]

View file

@ -0,0 +1,22 @@
all: build
FLAGS =
ENVVAR = GOOS=linux GOARCH=amd64 CGO_ENABLED=0
NAME = prometheus-watcher
REPO = quay.io/coreos/$(NAME)
TAG = v0.0.1
IMAGE = $(REPO):$(TAG)
build:
$(ENVVAR) go build -o $(NAME) main.go
image: build
docker build -t $(IMAGE) .
push: image
docker push $(IMAGE)
clean:
rm -f $(NAME)
.PHONY: all build image push clean

View file

@ -0,0 +1,50 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-watcher-example
data:
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: prometheus-watcher
spec:
replicas: 1
template:
metadata:
labels:
app: prometheus-watcher
spec:
volumes:
- name: rule-files
emptyDir: {}
- name: configmap-source
configMap:
name: prometheus-watcher-example
containers:
- name: ubuntu
image: ubuntu
command: ["sleep", "3600"]
volumeMounts:
- name: rule-files
mountPath: /var/rules
readOnly: true
- name: prometheus-watcher
image: quay.io/coreos/prometheus-watcher:v0.0.1
args:
- '-configmap-file=/var/configmaps/list'
- '-rule-file-dir=/var/rules'
- '-namespace=default'
resources:
requests:
memory: "16Mi"
cpu: "50m"
limits:
memory: "32Mi"
cpu: "100m"
volumeMounts:
- name: configmap-source
mountPath: /var/configmaps
- name: rule-files
mountPath: /var/rules

View file

@ -0,0 +1,33 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-watcher-example
data:
list: |+
test0
test1
test2
---
apiVersion: v1
kind: ConfigMap
metadata:
name: test0
data:
test.rules: |+
test0
---
apiVersion: v1
kind: ConfigMap
metadata:
name: test1
data:
test.rules: |+
test1
---
apiVersion: v1
kind: ConfigMap
metadata:
name: test2
data:
test.rules: |+
test2

View file

@ -0,0 +1,236 @@
// Copyright 2016 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"bufio"
"context"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
fsnotify "gopkg.in/fsnotify.v1"
"github.com/ericchiang/k8s"
)
type config struct {
configMapFile string
ruleFileDir string
namespace string
}
type volumeWatcher struct {
client *k8s.Client
cfg config
}
func newVolumeWatcher(client *k8s.Client, cfg config) *volumeWatcher {
return &volumeWatcher{
client: client,
cfg: cfg,
}
}
func (w *volumeWatcher) UpdateRuleFiles() error {
file, err := os.Open(w.cfg.configMapFile)
if err != nil {
return err
}
defer file.Close()
tmpdir, err := ioutil.TempDir(w.cfg.ruleFileDir, "prometheus-rule-files")
if err != nil {
return err
}
defer os.RemoveAll(tmpdir)
scanner := bufio.NewScanner(file)
i := 0
for scanner.Scan() {
err := w.writeRuleConfigMap(tmpdir, i, scanner.Text())
if err != nil {
log.Println("err", err)
}
i++
}
if err := scanner.Err(); err != nil {
return err
}
err = w.placeNewRuleFiles(tmpdir, w.cfg.ruleFileDir)
if err != nil {
return err
}
return nil
}
func (w *volumeWatcher) placeNewRuleFiles(tmpdir, ruleFileDir string) error {
err := os.MkdirAll(ruleFileDir, os.ModePerm)
if err != nil {
return err
}
err = w.removeOldRuleFiles(ruleFileDir, tmpdir)
if err != nil {
return err
}
d, err := os.Open(tmpdir)
if err != nil {
return err
}
defer d.Close()
names, err := d.Readdirnames(-1)
if err != nil {
return err
}
for _, name := range names {
err = os.Rename(filepath.Join(tmpdir, name), filepath.Join(ruleFileDir, name))
if err != nil {
return err
}
}
return nil
}
func (w *volumeWatcher) removeOldRuleFiles(dir string, tmpdir string) error {
d, err := os.Open(dir)
if err != nil {
return err
}
defer d.Close()
names, err := d.Readdirnames(-1)
if err != nil {
return err
}
for _, name := range names {
s := filepath.Join(dir, name)
if s != tmpdir {
err = os.RemoveAll(s)
if err != nil {
return err
}
}
}
return nil
}
func (w *volumeWatcher) writeRuleConfigMap(rulesDir string, index int, configMapName string) error {
cm, err := w.client.CoreV1().GetConfigMap(context.TODO(), configMapName, w.cfg.namespace)
if err != nil {
return err
}
dir := filepath.Join(rulesDir, fmt.Sprintf("rules-%d", index))
err = os.MkdirAll(dir, os.ModePerm)
if err != nil {
return err
}
for filename, content := range cm.Data {
err = w.writeConfigMapFile(filepath.Join(dir, filename), content)
if err != nil {
return err
}
}
return nil
}
func (w *volumeWatcher) writeConfigMapFile(filename, content string) error {
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
_, err = f.Write([]byte(content))
return err
}
func (w *volumeWatcher) Run() {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
done := make(chan bool)
go func() {
for {
select {
case event := <-watcher.Events:
if event.Op&fsnotify.Create == fsnotify.Create {
if filepath.Base(event.Name) == "..data" {
log.Println("ConfigMap modified. Updating rule files...")
err := w.UpdateRuleFiles()
if err != nil {
log.Println("err", err)
}
log.Println("Rule files updated.")
}
}
case err := <-watcher.Errors:
log.Println("err", err)
}
}
}()
log.Println("Starting...")
err = watcher.Add(filepath.Dir(w.cfg.configMapFile))
if err != nil {
log.Fatal(err)
}
<-done
}
func main() {
cfg := config{}
flags := flag.NewFlagSet("prometheus-watcher", flag.ExitOnError)
flags.StringVar(&cfg.configMapFile, "configmap-file", "", "A file containing a list of ConfigMap names.")
flags.StringVar(&cfg.ruleFileDir, "rule-file-dir", "", "A directory where rule files will be written to.")
flags.StringVar(&cfg.namespace, "namespace", "", "The namespace to get ConfigMaps from.")
flags.Parse(os.Args[1:])
if cfg.configMapFile == "" {
log.Println("Missing file to watch for changes\n")
flag.Usage()
os.Exit(1)
}
if cfg.ruleFileDir == "" {
log.Println("Missing directory to write rule files into\n")
flag.Usage()
os.Exit(1)
}
if cfg.namespace == "" {
log.Println("Missing namespace to get ConfigMaps from\n")
flag.Usage()
os.Exit(1)
}
client, err := k8s.NewInClusterClient()
if err != nil {
log.Fatal(err)
}
newVolumeWatcher(client, cfg).Run()
}