Skip to content

Commit

Permalink
Add default_module and target options
Browse files Browse the repository at this point in the history
If default_module is set then the exporter will use that when the module
parameter isn't set.

If target is set for a module then the module will use that target,
ignoring the target parameter completely.
  • Loading branch information
ribbybibby committed Dec 23, 2021
1 parent 087c407 commit 02d6183
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 7 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,11 @@ configuration file with `--config.file`. The file is written in yaml format,
defined by the schema below.

```
# The default module to use. If omitted, then the module must be provided by the
# 'module' query parameter
default_module: <string>
# Module configuration
modules: [<module>]
```

Expand All @@ -257,6 +262,10 @@ modules: [<module>]
# The type of probe (https, tcp, file, kubernetes, kubeconfig)
prober: <prober_string>
# The probe target. If set, then the 'target' query parameter is ignored.
# If omitted, then the 'target' query parameter is required.
target: <string>
# How long the probe will wait before giving up.
[ timeout: <duration> ]
Expand Down
7 changes: 5 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ var (
// DefaultConfig is the default configuration that is used when no
// configuration file is provided
DefaultConfig = &Config{
map[string]Module{
DefaultModule: "tcp",
Modules: map[string]Module{
"tcp": Module{
Prober: "tcp",
},
Expand Down Expand Up @@ -59,12 +60,14 @@ func LoadConfig(confFile string) (*Config, error) {

// Config configures the exporter
type Config struct {
Modules map[string]Module `yaml:"modules"`
DefaultModule string `yaml:"default_module"`
Modules map[string]Module `yaml:"modules"`
}

// Module configures a prober
type Module struct {
Prober string `yaml:"prober,omitempty"`
Target string `yaml:"target,omitempty"`
Timeout time.Duration `yaml:"timeout,omitempty"`
TLSConfig TLSConfig `yaml:"tls_config,omitempty"`
HTTPS HTTPSProbe `yaml:"https,omitempty"`
Expand Down
4 changes: 4 additions & 0 deletions examples/ssl_exporter.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
default_module: https
modules:
https:
prober: https
Expand Down Expand Up @@ -34,6 +35,9 @@ modules:
starttls: smtp
file:
prober: file
file_ca_certificates:
prober: file
target: /etc/ssl/certs/ca-certificates.crt
kubernetes:
prober: kubernetes
kubernetes_kubeconfig:
Expand Down
15 changes: 11 additions & 4 deletions ssl_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ const (
func probeHandler(logger log.Logger, w http.ResponseWriter, r *http.Request, conf *config.Config) {
moduleName := r.URL.Query().Get("module")
if moduleName == "" {
moduleName = "tcp"
moduleName = conf.DefaultModule
if moduleName == "" {
http.Error(w, "Module parameter must be set", http.StatusBadRequest)
return
}
}
module, ok := conf.Modules[moduleName]
if !ok {
Expand Down Expand Up @@ -60,10 +64,13 @@ func probeHandler(logger log.Logger, w http.ResponseWriter, r *http.Request, con
ctx, cancel := context.WithTimeout(r.Context(), timeout)
defer cancel()

target := r.URL.Query().Get("target")
target := module.Target
if target == "" {
http.Error(w, "Target parameter is missing", http.StatusBadRequest)
return
target = r.URL.Query().Get("target")
if target == "" {
http.Error(w, "Target parameter is missing", http.StatusBadRequest)
return
}
}

probeFunc, ok := prober.Probers[module.Prober]
Expand Down
129 changes: 128 additions & 1 deletion ssl_exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func TestProbeHandler(t *testing.T) {
}
}

// TestProbeHandler tests that the probe handler sets the ssl_probe_success and
// TestProbeHandlerFail tests that the probe handler sets the ssl_probe_success and
// ssl_prober metrics correctly when the probe fails
func TestProbeHandlerFail(t *testing.T) {
rr, err := probe("localhost:6666", "", config.DefaultConfig)
Expand All @@ -70,6 +70,133 @@ func TestProbeHandlerFail(t *testing.T) {
}
}

// TestProbeHandlerDefaultModule tests the default module is used correctly
func TestProbeHandlerDefaultModule(t *testing.T) {
server, _, _, caFile, teardown, err := test.SetupHTTPSServer()
if err != nil {
t.Fatalf(err.Error())
}
defer teardown()

server.StartTLS()
defer server.Close()

conf := &config.Config{
DefaultModule: "https",
Modules: map[string]config.Module{
"tcp": config.Module{
Prober: "tcp",
TLSConfig: config.TLSConfig{
CAFile: caFile,
},
},
"https": config.Module{
Prober: "https",
TLSConfig: config.TLSConfig{
CAFile: caFile,
},
},
},
}

rr, err := probe(server.URL, "", conf)
if err != nil {
t.Fatalf(err.Error())
}

// Should have used the https prober
if ok := strings.Contains(rr.Body.String(), "ssl_prober{prober=\"https\"} 1"); !ok {
t.Errorf("expected `ssl_prober{prober=\"https\"} 1`")
}

conf.DefaultModule = ""

rr, err = probe(server.URL, "", conf)
if err != nil {
t.Fatalf(err.Error())
}

// It should fail when there's no default module
if rr.Code != http.StatusBadRequest {
t.Errorf("expected code: %d, got: %d", http.StatusBadRequest, rr.Code)
}

}

// TestProbeHandlerTarget tests the target module parameter is used correctly
func TestProbeHandlerDefaultTarget(t *testing.T) {
server, _, _, caFile, teardown, err := test.SetupHTTPSServer()
if err != nil {
t.Fatalf(err.Error())
}
defer teardown()

server.StartTLS()
defer server.Close()

conf := &config.Config{
Modules: map[string]config.Module{
"https": config.Module{
Prober: "https",
Target: server.URL,
TLSConfig: config.TLSConfig{
CAFile: caFile,
},
},
},
}

// Should use the target in the module configuration
rr, err := probe("", "https", conf)
if err != nil {
t.Fatalf(err.Error())
}

// Check probe success
if ok := strings.Contains(rr.Body.String(), "ssl_probe_success 1"); !ok {
t.Errorf("expected `ssl_probe_success 1`")
}

// Check prober metric
if ok := strings.Contains(rr.Body.String(), "ssl_prober{prober=\"https\"} 1"); !ok {
t.Errorf("expected `ssl_prober{prober=\"https\"} 1`")
}

// Should ignore a different target in the target parameter
rr, err = probe("localhost:6666", "https", conf)
if err != nil {
t.Fatalf(err.Error())
}

// Check probe success
if ok := strings.Contains(rr.Body.String(), "ssl_probe_success 1"); !ok {
t.Errorf("expected `ssl_probe_success 1`")
}

// Check prober metric
if ok := strings.Contains(rr.Body.String(), "ssl_prober{prober=\"https\"} 1"); !ok {
t.Errorf("expected `ssl_prober{prober=\"https\"} 1`")
}

conf.Modules["tcp"] = config.Module{
Prober: "tcp",
TLSConfig: config.TLSConfig{
CAFile: caFile,
},
}

rr, err = probe("", "tcp", conf)
if err != nil {
t.Fatalf(err.Error())
}

// It should fail when there's no target in the module configuration or
// the query parameters
if rr.Code != http.StatusBadRequest {
t.Errorf("expected code: %d, got: %d", http.StatusBadRequest, rr.Code)
}
}

func probe(target, module string, conf *config.Config) (*httptest.ResponseRecorder, error) {
uri := "/probe?target=" + target
if module != "" {
Expand Down

0 comments on commit 02d6183

Please sign in to comment.