Skip to content
This repository was archived by the owner on Apr 19, 2023. It is now read-only.

Commit 4eb2a66

Browse files
authored
Merge pull request #45 from integr8ly/add-blackbox-exporter
Add blackbox exporter
2 parents a574826 + fc6bf46 commit 4eb2a66

File tree

11 files changed

+224
-5
lines changed

11 files changed

+224
-5
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,26 @@ The following resources are supported:
1414

1515
Triggers the installation of the monitoring stack when created. This is achieved by deploying two other operators that install Prometheus and Grafana respectively.
1616

17+
The Application Monitoring CR accepts the following properties in the spec:
18+
19+
* *labelSelector*: The value of the `middleware-monitoring` label that has to be present on all imported resources (prometheus rules, service monitors, grafana dashboards).
20+
* *blackboxTargets*: A list of targets for the blackbox exporter to probe.
21+
22+
The `blackboxTargets` should be provided as an array in the form of:
23+
24+
```yaml
25+
blackboxTargets:
26+
- service: example
27+
url: https://example.com
28+
module: http_extern_2xx
29+
```
30+
31+
where `service` will be added as a label to the metric, `url` is the URL of the route to probe and `module` can be one of:
32+
33+
* *http_2xx*: Probe http or https targets via GET using the cluster certificates
34+
* *http_post_2xx*: Probe http or https targets via POST using the cluster certificates
35+
* *http_extern_2xx*: Probe http or https targets via GET relying on a valid external certificate
36+
1737
## PrometheusRule
1838

1939
Represents a set of alert rules for Prometheus/Alertmanager. See the [https://github.com/coreos/prometheus-operator/blob/f9bc0aa0fd9aa936f500d9d241098863c60d873d/Documentation/user-guides/alerting.md#alerting](prometheus operator docs) for more details about this resource.

deploy/crds/ApplicationMonitoring.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ spec:
1818
spec:
1919
required: ["labelSelector"]
2020
properties:
21+
blackboxTargets:
22+
type: array
23+
items:
24+
type: object
25+
description: A target to be probed by the blackbox exporter
2126
labelSelector:
2227
type: string
2328
minimum: 1

deploy/examples/ApplicationMonitoring.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,7 @@ metadata:
44
name: example-applicationmonitoring
55
spec:
66
labelSelector: "middleware"
7+
blackboxTargets:
8+
- service: example
9+
url: https://example.com
10+
module: http_extern_2xx

pkg/apis/applicationmonitoring/v1alpha1/applicationmonitoring_types.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,20 @@ import (
77
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
88
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
99

10+
// A target (url, module and service name) to be probed by the
11+
// blackbox exporter
12+
type BlackboxTarget struct {
13+
Url string `json:"url"`
14+
Service string `json:"service"`
15+
Module string `json:"module"`
16+
}
17+
1018
// ApplicationMonitoringSpec defines the desired state of ApplicationMonitoring
1119
type ApplicationMonitoringSpec struct {
1220
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
1321
// Important: Run "operator-sdk generate k8s" to regenerate code after modifying this file
14-
LabelSelector string `json:"labelSelector"`
22+
LabelSelector string `json:"labelSelector"`
23+
BlackboxTargets []BlackboxTarget `json:"blackboxTargets,omitempty"`
1524
}
1625

1726
// ApplicationMonitoringStatus defines the observed state of ApplicationMonitoring

pkg/apis/applicationmonitoring/v1alpha1/zz_generated.deepcopy.go

Lines changed: 22 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/controller/applicationmonitoring/applicationmonitoring_controller.go

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const (
3939
PhaseCreateGrafanaCR
4040
PhaseFinalizer
4141
PhaseDone
42+
PhaseReconcileConfig
4243
)
4344

4445
// Add creates a new ApplicationMonitoring Controller and adds it to the Manager. The Manager will set fields on the Controller
@@ -144,15 +145,29 @@ func (r *ReconcileApplicationMonitoring) Reconcile(request reconcile.Request) (r
144145
return r.setFinalizer(instanceCopy)
145146
case PhaseDone:
146147
log.Info("Finished installing application monitoring")
148+
return r.updatePhase(instanceCopy, PhaseReconcileConfig)
149+
case PhaseReconcileConfig:
150+
return r.reconcileConfig(instanceCopy)
147151
}
148152

149153
return reconcile.Result{}, nil
150154
}
151155

156+
func (r *ReconcileApplicationMonitoring) reconcileConfig(cr *applicationmonitoringv1alpha1.ApplicationMonitoring) (reconcile.Result, error) {
157+
log.Info("Phase: Reconciling Config")
158+
return reconcile.Result{}, r.createOrUpdateAdditionalScrapeConfig(cr)
159+
}
160+
152161
func (r *ReconcileApplicationMonitoring) installPrometheusOperator(cr *applicationmonitoringv1alpha1.ApplicationMonitoring) (reconcile.Result, error) {
153162
log.Info("Phase: Install PrometheusOperator")
154163

155-
for _, resourceName := range []string{PrometheusOperatorServiceAccountName, PrometheusOperatorName, PrometheusProxySecretsName} {
164+
err := r.createOrUpdateAdditionalScrapeConfig(cr)
165+
if err != nil {
166+
log.Error(err, "Failed to create additional scrape config")
167+
return reconcile.Result{}, err
168+
}
169+
170+
for _, resourceName := range []string{PrometheusOperatorServiceAccountName, PrometheusOperatorName, PrometheusProxySecretsName, BlackboxExporterConfigmapName} {
156171
if _, err := r.createResource(cr, resourceName); err != nil {
157172
log.Info(fmt.Sprintf("Error in InstallPrometheusOperator, resourceName=%s : err=%s", resourceName, err))
158173
// Requeue so it can be attempted again
@@ -340,6 +355,54 @@ func (r *ReconcileApplicationMonitoring) getHostFromRoute(namespacedName types.N
340355
return host, nil
341356
}
342357

358+
// Create a secret that contains additional prometheus scrape configurations
359+
// Used to configure the blackbox exporter
360+
func (r *ReconcileApplicationMonitoring) createOrUpdateAdditionalScrapeConfig(cr *applicationmonitoringv1alpha1.ApplicationMonitoring) error {
361+
t := newTemplateHelper(cr, nil)
362+
job, err := t.loadTemplate(BlackboxExporterJobName)
363+
364+
if err != nil {
365+
return errors.Wrap(err, "error loading blackbox exporter template")
366+
}
367+
368+
selector := types.NamespacedName{
369+
Namespace: cr.Namespace,
370+
Name: ScrapeConfigSecretName,
371+
}
372+
373+
update := true
374+
scrapeConfigSecret := corev1.Secret{}
375+
376+
// Check if the secret already exists
377+
err = r.client.Get(context.TODO(), selector, &scrapeConfigSecret)
378+
if err != nil {
379+
if !kerrors.IsNotFound(err) {
380+
return err
381+
}
382+
update = false
383+
}
384+
385+
scrapeConfigSecret.ObjectMeta = v1.ObjectMeta{
386+
Name: ScrapeConfigSecretName,
387+
Namespace: cr.Namespace,
388+
}
389+
390+
scrapeConfigSecret.Data = map[string][]byte{
391+
"blackbox-exporter.yaml": []byte(job),
392+
}
393+
394+
if update {
395+
return r.client.Update(context.TODO(), &scrapeConfigSecret)
396+
} else {
397+
err = controllerutil.SetControllerReference(cr, &scrapeConfigSecret, r.scheme)
398+
if err != nil {
399+
return errors.Wrap(err, "error setting controller reference")
400+
}
401+
402+
return r.client.Create(context.TODO(), &scrapeConfigSecret)
403+
}
404+
}
405+
343406
func (r *ReconcileApplicationMonitoring) getPrometheusOperatorReady(cr *applicationmonitoringv1alpha1.ApplicationMonitoring) (bool, error) {
344407
resource := v1beta1.Deployment{}
345408

pkg/controller/applicationmonitoring/templateHelper.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"io/ioutil"
99
"os"
10+
"strings"
1011
"text/template"
1112

1213
applicationmonitoring "github.com/integr8ly/application-monitoring-operator/pkg/apis/applicationmonitoring/v1alpha1"
@@ -39,6 +40,9 @@ const (
3940
AlertManagerRouteName = "alertmanager-route"
4041
GrafanaServiceMonitorName = "grafana-servicemonitor"
4142
GrafanaServiceName = "grafana-service"
43+
BlackboxExporterConfigmapName = "blackbox-exporter-config"
44+
BlackboxExporterJobName = "blackbox-exporter-job"
45+
ScrapeConfigSecretName = "additional-scrape-configs"
4246
)
4347

4448
type Parameters struct {
@@ -66,6 +70,9 @@ type Parameters struct {
6670
PrometheusServiceMonitorName string
6771
MonitoringKey string
6872
GrafanaServiceName string
73+
BlackboxExporterConfigmapName string
74+
ScrapeConfigSecretName string
75+
BlackboxTargets []applicationmonitoring.BlackboxTarget
6976
ExtraParams map[string]string
7077
}
7178

@@ -103,6 +110,9 @@ func newTemplateHelper(cr *applicationmonitoring.ApplicationMonitoring, extraPar
103110
GrafanaServiceMonitorName: GrafanaServiceMonitorName,
104111
PrometheusServiceMonitorName: PrometheusServiceMonitorName,
105112
MonitoringKey: cr.Spec.LabelSelector,
113+
BlackboxExporterConfigmapName: BlackboxExporterConfigmapName,
114+
BlackboxTargets: cr.Spec.BlackboxTargets,
115+
ScrapeConfigSecretName: ScrapeConfigSecretName,
106116
ExtraParams: extraParams,
107117
}
108118

@@ -131,6 +141,16 @@ func PopulateSessionProxySecret() string {
131141
return p
132142
}
133143

144+
// Takes a list of strings, wraps each string in double quotes and joins them
145+
// Used for building yaml arrays
146+
func joinQuote(values []applicationmonitoring.BlackboxTarget) string {
147+
var result []string
148+
for _, s := range values {
149+
result = append(result, fmt.Sprintf("\"%v@%v@%v\"", s.Module, s.Service, s.Url))
150+
}
151+
return strings.Join(result, ", ")
152+
}
153+
134154
// load a templates from a given resource name. The templates must be located
135155
// under ./templates and the filename must be <resource-name>.yaml
136156
func (h *TemplateHelper) loadTemplate(name string) ([]byte, error) {
@@ -140,7 +160,12 @@ func (h *TemplateHelper) loadTemplate(name string) ([]byte, error) {
140160
return nil, err
141161
}
142162

143-
parsed, err := template.New("application-monitoring").Parse(string(tpl))
163+
parser := template.New("application-monitoring")
164+
parser.Funcs(template.FuncMap{
165+
"JoinQuote": joinQuote,
166+
})
167+
168+
parsed, err := parser.Parse(string(tpl))
144169
if err != nil {
145170
return nil, err
146171
}

templates/alertmanager.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ spec:
77
baseImage: registry.redhat.io/openshift3/prometheus-alertmanager
88
tag: v3.11
99
externalUrl: https://{{ index .ExtraParams "alertmanagerHost" }}
10-
listenLocal: false
10+
listenLocal: true
1111
serviceAccountName: alertmanager
1212
containers:
1313
- args:
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
apiVersion: v1
2+
data:
3+
blackbox.yml: |-
4+
modules:
5+
http_extern_2xx:
6+
prober: http
7+
http:
8+
preferred_ip_protocol: ip4
9+
http_2xx:
10+
prober: http
11+
http:
12+
preferred_ip_protocol: ip4
13+
tls_config:
14+
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
15+
cert_file: /etc/tls/private/tls.crt
16+
key_file: /etc/tls/private/tls.key
17+
http_post_2xx:
18+
prober: http
19+
http:
20+
method: POST
21+
preferred_ip_protocol: ip4
22+
tls_config:
23+
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
24+
cert_file: /etc/tls/private/tls.crt
25+
key_file: /etc/tls/private/tls.key
26+
kind: ConfigMap
27+
metadata:
28+
name: {{ .BlackboxExporterConfigmapName }}
29+
namespace: {{ .Namespace }}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
- job_name: 'blackbox'
2+
metrics_path: /probe
3+
static_configs:
4+
- targets: [{{ JoinQuote .BlackboxTargets }}]
5+
relabel_configs:
6+
- source_labels: [__address__]
7+
regex: (.*)\@(.*)\@(.*)
8+
target_label: __param_module
9+
replacement: ${1}
10+
- source_labels: [__address__]
11+
regex: (.*)\@(.*)\@(.*)
12+
target_label: service
13+
replacement: ${2}
14+
- source_labels: [__address__]
15+
regex: (.*)\@(.*)\@(.*)
16+
target_label: __param_target
17+
replacement: ${3}
18+
- source_labels: [__param_target]
19+
target_label: instance
20+
- target_label: __address__
21+
replacement: 127.0.0.1:9115

0 commit comments

Comments
 (0)