Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c7c0a470f |
+4
-50
@@ -1,59 +1,13 @@
|
|||||||
---
|
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
name: test
|
name: test
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: build
|
||||||
|
image: golang:1.12
|
||||||
|
commands:
|
||||||
|
- make build
|
||||||
|
|
||||||
- name: test
|
- name: test
|
||||||
image: golang:1.12
|
image: golang:1.12
|
||||||
commands:
|
commands:
|
||||||
- make build
|
|
||||||
- make test
|
- make test
|
||||||
|
|
||||||
- name: check
|
|
||||||
image: python:3
|
|
||||||
commands:
|
|
||||||
- pip install pre-commit==1.20.0
|
|
||||||
- make check
|
|
||||||
|
|
||||||
- name: notify
|
|
||||||
image: drillster/drone-email
|
|
||||||
settings:
|
|
||||||
host:
|
|
||||||
from_secret: SMTP_HOST
|
|
||||||
username:
|
|
||||||
from_secret: SMTP_USER
|
|
||||||
password:
|
|
||||||
from_secret: SMTP_PASS
|
|
||||||
from: drone@iamthefij.com
|
|
||||||
when:
|
|
||||||
status: [changed, failure]
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: publish
|
|
||||||
|
|
||||||
depends_on:
|
|
||||||
- test
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- tag
|
|
||||||
refs:
|
|
||||||
- refs/heads/master
|
|
||||||
- refs/tags/v*
|
|
||||||
|
|
||||||
steps:
|
|
||||||
|
|
||||||
# Might consider moving this step into the previous pipeline
|
|
||||||
- name: push image
|
|
||||||
image: plugins/docker
|
|
||||||
settings:
|
|
||||||
repo: iamthefij/minitor-go
|
|
||||||
dockerfile: Dockerfile.multi-stage
|
|
||||||
auto_tag: true
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
repos:
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
||||||
rev: v2.4.0
|
|
||||||
hooks:
|
|
||||||
- id: check-added-large-files
|
|
||||||
- id: check-yaml
|
|
||||||
args:
|
|
||||||
- --allow-multiple-documents
|
|
||||||
- id: trailing-whitespace
|
|
||||||
- id: end-of-file-fixer
|
|
||||||
- id: check-merge-conflict
|
|
||||||
- repo: git://github.com/dnephin/pre-commit-golang
|
|
||||||
rev: v0.3.5
|
|
||||||
hooks:
|
|
||||||
- id: go-fmt
|
|
||||||
- id: go-imports
|
|
||||||
# - id: gometalinter
|
|
||||||
# - id: golangci-lint
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
.PHONY: all
|
|
||||||
DOCKER_TAG ?= minitor-go-${USER}
|
DOCKER_TAG ?= minitor-go-${USER}
|
||||||
|
|
||||||
.PHONY: default
|
.PHONY: test
|
||||||
default: test
|
default: test
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
@@ -15,10 +14,6 @@ minitor-go:
|
|||||||
run: minitor-go build
|
run: minitor-go build
|
||||||
./minitor-go -debug
|
./minitor-go -debug
|
||||||
|
|
||||||
.PHONY: run-metrics
|
|
||||||
run-metrics: minitor-go build
|
|
||||||
./minitor-go -debug -metrics
|
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
go test -coverprofile=coverage.out
|
go test -coverprofile=coverage.out
|
||||||
@@ -29,15 +24,6 @@ test:
|
|||||||
@go tool cover -func=coverage.out | awk -v target=80.0% \
|
@go tool cover -func=coverage.out | awk -v target=80.0% \
|
||||||
'/^total:/ { print "Total coverage: " $$3 " Minimum coverage: " target; if ($$3+0.0 >= target+0.0) print "ok"; else { print "fail"; exit 1; } }'
|
'/^total:/ { print "Total coverage: " $$3 " Minimum coverage: " target; if ($$3+0.0 >= target+0.0) print "ok"; else { print "fail"; exit 1; } }'
|
||||||
|
|
||||||
# Installs pre-commit hooks
|
|
||||||
.PHONY: install-hooks
|
|
||||||
install-hooks:
|
|
||||||
pre-commit install --install-hooks
|
|
||||||
|
|
||||||
# Checks files for encryption
|
|
||||||
.PHONY: check
|
|
||||||
check:
|
|
||||||
pre-commit run --all-files
|
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
A reimplementation of [Minitor](https://git.iamthefij/iamthefij/minitor) in Go
|
A reimplementation of [Minitor](https://git.iamthefij/iamthefij/minitor) in Go
|
||||||
|
|
||||||
Minitor is already a minimal monitoring tool. Python 3 was a quick way to get something live, but Python itself comes with a large footprint. Thus Go feels like a better fit for the project, longer term.
|
Minitor is already a very minimal monitoring tool. Python 3 was a quick way to get something live, but Python itself comes with a very large footprint.Thus Go feels like a better fit for the project, longer term.
|
||||||
|
|
||||||
Initial target is meant to be roughly compatible requiring only minor changes to configuration. Future iterations may diverge to take advantage of Go specific features.
|
Initial target is meant to be roughly compatible requiring only minor changes to configuration. Future iterations may diverge to take advantage of Go specific features.
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ monitors:
|
|||||||
command_shell: echo 'test'
|
command_shell: echo 'test'
|
||||||
```
|
```
|
||||||
|
|
||||||
Second, templating for Alert messages has been updated. In the Python version, `str.format(...)` was used with certain keys passed in that could be used to format messages. In the Go version, we use a struct, `AlertNotice` defined in `alert.go` and the built in Go templating format. Eg.
|
Second, templating for Alert messages has been updated. In the Python version, `str.format(...)` was used with certain keys passed in that could be used to format messages. In the Go version, we use a struct containing Alert info and the built in Go templating format. Eg.
|
||||||
|
|
||||||
minitor-py:
|
minitor-py:
|
||||||
```yaml
|
```yaml
|
||||||
@@ -38,7 +38,7 @@ alerts:
|
|||||||
log_command:
|
log_command:
|
||||||
command: ['echo', '{monitor_name}']
|
command: ['echo', '{monitor_name}']
|
||||||
log_shell:
|
log_shell:
|
||||||
command_shell: 'echo {monitor_name}'
|
command_shell: "echo {monitor_name}"
|
||||||
```
|
```
|
||||||
|
|
||||||
minitor-go:
|
minitor-go:
|
||||||
@@ -47,7 +47,7 @@ alerts:
|
|||||||
log_command:
|
log_command:
|
||||||
command: ['echo', '{{.MonitorName}}']
|
command: ['echo', '{{.MonitorName}}']
|
||||||
log_shell:
|
log_shell:
|
||||||
command_shell: 'echo {{.MonitorName}}'
|
command_shell: "echo {{.MonitorName}}"
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, newlines in a shell command don't terminate a particular command. Semicolons must be used and continuations should not.
|
Finally, newlines in a shell command don't terminate a particular command. Semicolons must be used and continuations should not.
|
||||||
@@ -84,11 +84,10 @@ Pairity:
|
|||||||
- [x] Run alert commands
|
- [x] Run alert commands
|
||||||
- [x] Run alert commands in a shell
|
- [x] Run alert commands in a shell
|
||||||
- [x] Allow templating of alert commands
|
- [x] Allow templating of alert commands
|
||||||
- [x] Implement Prometheus client to export metrics
|
- [ ] Implement Prometheus client to export metrics
|
||||||
- [x] Test coverage
|
- [ ] Test coverage
|
||||||
- [ ] Integration testing (manual or otherwise)
|
|
||||||
|
|
||||||
Improvement (potentially breaking):
|
Improvement:
|
||||||
|
|
||||||
- [ ] Implement leveled logging (maybe glog or logrus)
|
- [ ] Implement leveled logging (maybe glog or logrus)
|
||||||
- [ ] Consider switching from YAML to TOML
|
- [ ] Consider switching from YAML to TOML
|
||||||
@@ -96,4 +95,3 @@ Improvement (potentially breaking):
|
|||||||
- [ ] Consider dropping `alert_up` and `alert_down` in favor of using Go templates that offer more control of messaging
|
- [ ] Consider dropping `alert_up` and `alert_down` in favor of using Go templates that offer more control of messaging
|
||||||
- [ ] Async checking
|
- [ ] Async checking
|
||||||
- [ ] Use durations rather than seconds checked in event loop
|
- [ ] Use durations rather than seconds checked in event loop
|
||||||
- [ ] Revisit metrics and see if they all make sense
|
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config type is contains all provided user configuration
|
// Config type is contains all provided user configuration
|
||||||
@@ -85,9 +84,7 @@ func LoadConfig(filePath string) (config Config, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if LogDebug {
|
log.Printf("config:\n%v\n", config)
|
||||||
log.Printf("DEBUG: Config values:\n%v\n", config)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !config.IsValid() {
|
if !config.IsValid() {
|
||||||
err = errors.New("Invalid configuration")
|
err = errors.New("Invalid configuration")
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,10 +15,6 @@ var (
|
|||||||
|
|
||||||
// ExportMetrics will track whether or not we want to export metrics to prometheus
|
// ExportMetrics will track whether or not we want to export metrics to prometheus
|
||||||
ExportMetrics = false
|
ExportMetrics = false
|
||||||
// MetricsPort is the port to expose metrics on
|
|
||||||
MetricsPort = 8080
|
|
||||||
// Metrics contains all active metrics
|
|
||||||
Metrics = NewMetrics()
|
|
||||||
|
|
||||||
// version of minitor being run
|
// version of minitor being run
|
||||||
version = "dev"
|
version = "dev"
|
||||||
@@ -25,13 +23,7 @@ var (
|
|||||||
func checkMonitors(config *Config) error {
|
func checkMonitors(config *Config) error {
|
||||||
for _, monitor := range config.Monitors {
|
for _, monitor := range config.Monitors {
|
||||||
if monitor.ShouldCheck() {
|
if monitor.ShouldCheck() {
|
||||||
success, alertNotice := monitor.Check()
|
_, alertNotice := monitor.Check()
|
||||||
|
|
||||||
hasAlert := alertNotice != nil
|
|
||||||
|
|
||||||
// Track status metrics
|
|
||||||
Metrics.SetMonitorStatus(monitor.Name, success)
|
|
||||||
Metrics.CountCheck(monitor.Name, success, hasAlert)
|
|
||||||
|
|
||||||
// Should probably consider refactoring everything below here
|
// Should probably consider refactoring everything below here
|
||||||
if alertNotice != nil {
|
if alertNotice != nil {
|
||||||
@@ -63,9 +55,6 @@ func checkMonitors(config *Config) error {
|
|||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count alert metrics
|
|
||||||
Metrics.CountAlert(monitor.Name, alert.Name)
|
|
||||||
} else {
|
} else {
|
||||||
// This case should never actually happen since we validate against it
|
// This case should never actually happen since we validate against it
|
||||||
log.Printf("ERROR: Unknown alert for monitor %s: %s", alertNotice.MonitorName, alertName)
|
log.Printf("ERROR: Unknown alert for monitor %s: %s", alertNotice.MonitorName, alertName)
|
||||||
@@ -79,6 +68,11 @@ func checkMonitors(config *Config) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func serveMetrics() {
|
||||||
|
http.Handle("/metrics", promhttp.Handler())
|
||||||
|
_ = http.ListenAndServe(":8080", nil)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Get debug flag
|
// Get debug flag
|
||||||
flag.BoolVar(&LogDebug, "debug", false, "Enables debug logs (default: false)")
|
flag.BoolVar(&LogDebug, "debug", false, "Enables debug logs (default: false)")
|
||||||
@@ -101,7 +95,7 @@ func main() {
|
|||||||
// Serve metrics exporter, if specified
|
// Serve metrics exporter, if specified
|
||||||
if ExportMetrics {
|
if ExportMetrics {
|
||||||
log.Println("INFO: Exporting metrics to Prometheus")
|
log.Println("INFO: Exporting metrics to Prometheus")
|
||||||
go ServeMetrics()
|
go serveMetrics()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start main loop
|
// Start main loop
|
||||||
|
|||||||
-101
@@ -1,101 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: Not sure if this is the best way to handle. A global instance for
|
|
||||||
// metrics isn't bad, but it might be nice to curry versions of the metrics
|
|
||||||
// for each monitor. Especially since every monitor has it's own. Perhaps
|
|
||||||
// another new function that essentially curries each metric for a given
|
|
||||||
// monitor name would do. This could be run when validating monitors and
|
|
||||||
// initializing alert templates.
|
|
||||||
|
|
||||||
// MinitorMetrics contains all counters and metrics that Minitor will need to access
|
|
||||||
type MinitorMetrics struct {
|
|
||||||
alertCount *prometheus.CounterVec
|
|
||||||
checkCount *prometheus.CounterVec
|
|
||||||
monitorStatus *prometheus.GaugeVec
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMetrics creates and initializes all metrics
|
|
||||||
func NewMetrics() *MinitorMetrics {
|
|
||||||
// Initialize all metrics
|
|
||||||
metrics := &MinitorMetrics{
|
|
||||||
alertCount: prometheus.NewCounterVec(
|
|
||||||
prometheus.CounterOpts{
|
|
||||||
Name: "minitor_alert_total",
|
|
||||||
Help: "Number of Minitor alerts",
|
|
||||||
},
|
|
||||||
[]string{"alert", "monitor"},
|
|
||||||
),
|
|
||||||
checkCount: prometheus.NewCounterVec(
|
|
||||||
prometheus.CounterOpts{
|
|
||||||
Name: "minitor_check_total",
|
|
||||||
Help: "Number of Minitor checks",
|
|
||||||
},
|
|
||||||
[]string{"monitor", "status", "is_alert"},
|
|
||||||
),
|
|
||||||
monitorStatus: prometheus.NewGaugeVec(
|
|
||||||
prometheus.GaugeOpts{
|
|
||||||
Name: "minitor_monitor_up_count",
|
|
||||||
Help: "Status of currently responsive monitors",
|
|
||||||
},
|
|
||||||
[]string{"monitor"},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register newly created metrics
|
|
||||||
prometheus.MustRegister(metrics.alertCount)
|
|
||||||
prometheus.MustRegister(metrics.checkCount)
|
|
||||||
prometheus.MustRegister(metrics.monitorStatus)
|
|
||||||
|
|
||||||
return metrics
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMonitorStatus sets the current status of Monitor
|
|
||||||
func (metrics *MinitorMetrics) SetMonitorStatus(monitor string, isUp bool) {
|
|
||||||
val := 0.0
|
|
||||||
if isUp {
|
|
||||||
val = 1.0
|
|
||||||
}
|
|
||||||
metrics.monitorStatus.With(prometheus.Labels{"monitor": monitor}).Set(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountCheck counts the result of a particular Monitor check
|
|
||||||
func (metrics *MinitorMetrics) CountCheck(monitor string, isSuccess bool, isAlert bool) {
|
|
||||||
status := "failure"
|
|
||||||
if isSuccess {
|
|
||||||
status = "success"
|
|
||||||
}
|
|
||||||
|
|
||||||
alertVal := "false"
|
|
||||||
if isAlert {
|
|
||||||
alertVal = "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
metrics.checkCount.With(
|
|
||||||
prometheus.Labels{"monitor": monitor, "status": status, "is_alert": alertVal},
|
|
||||||
).Inc()
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountAlert counts an alert
|
|
||||||
func (metrics *MinitorMetrics) CountAlert(monitor string, alert string) {
|
|
||||||
metrics.alertCount.With(
|
|
||||||
prometheus.Labels{
|
|
||||||
"alert": alert,
|
|
||||||
"monitor": monitor,
|
|
||||||
},
|
|
||||||
).Inc()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeMetrics starts an http server with a Prometheus metrics handler
|
|
||||||
func ServeMetrics() {
|
|
||||||
http.Handle("/metrics", promhttp.Handler())
|
|
||||||
host := fmt.Sprintf(":%d", MetricsPort)
|
|
||||||
_ = http.ListenAndServe(host, nil)
|
|
||||||
}
|
|
||||||
@@ -6,3 +6,4 @@ monitors:
|
|||||||
alert_down: [ 'alert_down', 'log_shell', 'log_command' ]
|
alert_down: [ 'alert_down', 'log_shell', 'log_command' ]
|
||||||
# alert_every: -1
|
# alert_every: -1
|
||||||
alert_every: 0
|
alert_every: 0
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user