--- /dev/null
+package main
+
+import (
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "net/http"
+ "time"
+)
+
+func getEarliestCertExpiry(state *tls.ConnectionState) time.Time {
+ earliest := time.Time{}
+ for _, cert := range state.PeerCertificates {
+ if (earliest.IsZero() || cert.NotAfter.Before(earliest)) && !cert.NotAfter.IsZero() {
+ earliest = cert.NotAfter
+ }
+ }
+ return earliest
+}
+
+func probeHTTP(target string, w http.ResponseWriter, module Module) (success bool) {
+ var isSSL, redirects int
+ config := module.HTTP
+
+ client := &http.Client{
+ Timeout: module.Timeout,
+ }
+
+ client.CheckRedirect = func(_ *http.Request, via []*http.Request) error {
+ redirects = len(via)
+ if redirects > 10 || config.NoFollowRedirects {
+ return errors.New("Don't follow redirects")
+ } else {
+ return nil
+ }
+ }
+
+ resp, err := client.Get(target)
+ if err == nil {
+ defer resp.Body.Close()
+ if len(config.ValidStatusCodes) != 0 {
+ for _, code := range config.ValidStatusCodes {
+ if resp.StatusCode == code {
+ success = true
+ break
+ }
+ }
+ } else if 200 >= resp.StatusCode && resp.StatusCode < 300 {
+ success = true
+ }
+ }
+
+ if resp == nil {
+ resp = &http.Response{}
+ }
+
+ if resp.TLS != nil {
+ isSSL = 1
+ fmt.Fprintf(w, "probe_ssl_earliest_cert_expiry %f\n",
+ float64(getEarliestCertExpiry(resp.TLS).UnixNano())/1e9)
+ if config.FailIfSSL {
+ success = false
+ }
+ } else if config.FailIfNotSSL {
+ success = false
+ }
+ fmt.Fprintf(w, "probe_http_status_code %d\n", resp.StatusCode)
+ fmt.Fprintf(w, "probe_http_content_length %d\n", resp.ContentLength)
+ fmt.Fprintf(w, "probe_http_redirects %d\n", redirects)
+ fmt.Fprintf(w, "probe_http_ssl %d\n", isSSL)
+ return
+}
import (
"flag"
"fmt"
+ "gopkg.in/yaml.v2"
+ "io/ioutil"
"net/http"
"time"
"github.com/prometheus/client_golang/prometheus"
-
- "github.com/brian-brazil/blackbox_exporter/probers"
+ "github.com/prometheus/log"
)
var addr = flag.String("web.listen-address", ":8080", "The address to listen on for HTTP requests.")
+var configFile = flag.String("config.file", "blackbox.yml", "Blackbox exporter configuration file.")
type Config struct {
- Modules map[string]Module `yaml:"modules"`
+ Modules map[string]Module `yaml:"modules"`
}
type Module struct {
- Prober string `yaml:"prober"`
- Timeout time.Duration `yaml:"timeout"`
- Config interface{} `yaml:"config"`
+ Prober string `yaml:"prober"`
+ Timeout time.Duration `yaml:"timeout"`
+ HTTP HTTPProbe `yaml:"http"`
+ TCP TCPProbe `yaml:"tcp"`
+}
+
+type HTTPProbe struct {
+ // Defaults to 2xx.
+ ValidStatusCodes []int `yaml:"valid_status_codes"`
+ NoFollowRedirects bool `yaml:"no_follow_redirects"`
+ FailIfSSL bool `yaml:"fail_if_ssl"`
+ FailIfNotSSL bool `yaml:"fail_if_not_ssl"`
+}
+
+type TCPProbe struct {
+}
+
+var Probers = map[string]func(string, http.ResponseWriter, Module) bool{
+ "http": probeHTTP,
+ "tcp": probeTCP,
}
-func probeHandler(w http.ResponseWriter, r *http.Request) {
+func probeHandler(w http.ResponseWriter, r *http.Request, config *Config) {
params := r.URL.Query()
target := params.Get("target")
- module := params.Get("module")
+ moduleName := params.Get("module")
if target == "" {
http.Error(w, "Target parameter is missing", 400)
return
}
- if module == "" {
- module = "http2xx"
+ if moduleName == "" {
+ moduleName = "http2xx"
+ }
+ module, ok := config.Modules[moduleName]
+ if !ok {
+ http.Error(w, fmt.Sprintf("Unkown module %s", moduleName), 400)
+ return
}
start := time.Now()
- success := probers.Probers["http"](target, w)
+ success := Probers[module.Prober](target, w, module)
fmt.Fprintf(w, "probe_duration_seconds %f\n", float64(time.Now().Sub(start))/1e9)
if success {
fmt.Fprintf(w, "probe_success %d\n", 1)
func main() {
flag.Parse()
+
+ yamlFile, err := ioutil.ReadFile(*configFile)
+
+ if err != nil {
+ log.Fatalf("Error reading config file: %s", err)
+ }
+
+ config := Config{}
+
+ err = yaml.Unmarshal(yamlFile, &config)
+ if err != nil {
+ log.Fatalf("Error parsing config file: %s", err)
+ }
+
http.Handle("/metrics", prometheus.Handler())
- http.HandleFunc("/probe", probeHandler)
+ http.HandleFunc("/probe",
+ func(w http.ResponseWriter, r *http.Request) {
+ probeHandler(w, r, &config)
+ })
http.ListenAndServe(*addr, nil)
}
+++ /dev/null
-package probers
-
-import (
- "net/http"
- "fmt"
-)
-
-func init() {
- Probers["http"] = probeHTTP
-}
-
-type HTTPProbe struct {
- // Defaults to 2xx.
- ValidStatusCodes []int `yaml:"valid_status_codes"`
- FollowRedirects bool `yaml:"follow_redirects"`
- FailIfSSL bool `yaml:"fail_if_ssl"`
- FailIfNotSSL bool `yaml:"fail_if_not_ssl"`
-}
-
-func probeHTTP(target string, w http.ResponseWriter) (success bool) {
- var isSSL int
- resp, err := http.Get(target)
- if err == nil {
- if 200 >= resp.StatusCode && resp.StatusCode < 300 {
- success = true
- }
- defer resp.Body.Close()
- }
- if resp.TLS != nil {
- isSSL = 1
- }
- fmt.Fprintf(w, "probe_http_status_code %d\n", resp.StatusCode)
- fmt.Fprintf(w, "probe_http_content_length %d\n", resp.ContentLength)
- fmt.Fprintf(w, "probe_http_ssl %d\n", isSSL)
- return
-}