Files
mariadb-operator/internal/controller/maxscale_controller_metrics.go
2025-04-15 12:56:36 +02:00

178 lines
5.6 KiB
Go

package controller
import (
"bytes"
"context"
"errors"
"fmt"
"time"
mariadbv1alpha1 "github.com/mariadb-operator/mariadb-operator/api/v1alpha1"
"github.com/mariadb-operator/mariadb-operator/pkg/builder"
labels "github.com/mariadb-operator/mariadb-operator/pkg/builder/labels"
builderpki "github.com/mariadb-operator/mariadb-operator/pkg/builder/pki"
"github.com/mariadb-operator/mariadb-operator/pkg/controller/secret"
"github.com/mariadb-operator/mariadb-operator/pkg/hash"
"github.com/mariadb-operator/mariadb-operator/pkg/metadata"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/log"
)
func (r *MaxScaleReconciler) reconcileMetrics(ctx context.Context, req *requestMaxScale) (ctrl.Result, error) {
if !req.mxs.AreMetricsEnabled() {
return ctrl.Result{}, nil
}
exist, err := r.Discovery.ServiceMonitorExist()
if err != nil {
return ctrl.Result{}, err
}
if !exist {
r.Recorder.Event(req.mxs, corev1.EventTypeWarning, mariadbv1alpha1.ReasonCRDNotFound,
"Unable to reconcile metrics: ServiceMonitor CRD not installed in the cluster")
log.FromContext(ctx).Error(errors.New("ServiceMonitor CRD not installed in the cluster"), "Unable to reconcile metrics")
return ctrl.Result{RequeueAfter: 10 * time.Second}, nil
}
if !req.mxs.IsReady() {
return ctrl.Result{RequeueAfter: 1 * time.Second}, nil
}
if err := r.reconcileExporterConfig(ctx, req); err != nil {
return ctrl.Result{}, err
}
if err := r.reconcileExporterDeployment(ctx, req.mxs); err != nil {
return ctrl.Result{}, err
}
if err := r.reconcileExporterService(ctx, req.mxs); err != nil {
return ctrl.Result{}, err
}
if err := r.reconcileServiceMonitor(ctx, req.mxs); err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
func (r *MaxScaleReconciler) reconcileExporterConfig(ctx context.Context, req *requestMaxScale) error {
secretKeyRef := req.mxs.MetricsConfigSecretKeyRef()
password, err := r.RefResolver.SecretKeyRef(ctx, req.mxs.Spec.Auth.MetricsPasswordSecretKeyRef.SecretKeySelector, req.mxs.Namespace)
if err != nil {
return fmt.Errorf("error getting metrics password Secret: %v", err)
}
tpl := createTpl(secretKeyRef.Key, `[maxscale_exporter]
maxscale_username={{ .User }}
maxscale_password={{ .Password }}
{{- if .TLSEnabled }}
tls_insecure_skip_verify=false
tls_ca_cert_file={{ .TLSCACertPath }}
tls_private_key_file={{ .TLSKeyPath }}
tls_key_cert_file={{ .TLSCertPath }}
{{- end }}
`)
buf := new(bytes.Buffer)
err = tpl.Execute(buf, struct {
User string
Password string
TLSEnabled bool
TLSCACertPath string
TLSKeyPath string
TLSCertPath string
}{
User: req.mxs.Spec.Auth.MetricsUsername,
Password: password,
TLSEnabled: req.mxs.IsTLSEnabled(),
TLSCACertPath: builderpki.CACertPath,
TLSKeyPath: builderpki.AdminKeyPath,
TLSCertPath: builderpki.AdminCertPath,
})
if err != nil {
return fmt.Errorf("error rendering exporter config: %v", err)
}
secretReq := secret.SecretRequest{
Owner: req.mxs,
Metadata: []*mariadbv1alpha1.Metadata{req.mxs.Spec.InheritMetadata},
Key: types.NamespacedName{
Name: secretKeyRef.Name,
Namespace: req.mxs.Namespace,
},
Data: map[string][]byte{
secretKeyRef.Key: buf.Bytes(),
},
}
return r.SecretReconciler.Reconcile(ctx, &secretReq)
}
func (r *MaxScaleReconciler) reconcileExporterDeployment(ctx context.Context, mxs *mariadbv1alpha1.MaxScale) error {
podAnnotations, err := r.getExporterPodAnnotations(ctx, mxs)
if err != nil {
return fmt.Errorf("error getting exporter Pod annotations: %v", err)
}
desiredDeploy, err := r.Builder.BuildMaxScaleExporterDeployment(mxs, podAnnotations)
if err != nil {
return fmt.Errorf("error building exporter Deployment: %v", err)
}
return r.DeploymentReconciler.Reconcile(ctx, desiredDeploy)
}
func (r *MaxScaleReconciler) getExporterPodAnnotations(ctx context.Context, mxs *mariadbv1alpha1.MaxScale) (map[string]string, error) {
config, err := r.RefResolver.SecretKeyRef(ctx, mxs.MetricsConfigSecretKeyRef().SecretKeySelector, mxs.Namespace)
if err != nil {
return nil, fmt.Errorf("error getting metrics config Secret: %v", err)
}
podAnnotations := map[string]string{
metadata.ConfigAnnotation: hash.Hash(config),
}
if mxs.IsTLSEnabled() {
tlsAnnotations, err := r.getTLSAdminAnnotations(ctx, mxs)
if err != nil {
return nil, fmt.Errorf("error getting TLS annotations: %v", err)
}
for k, v := range tlsAnnotations {
podAnnotations[k] = v
}
}
return podAnnotations, nil
}
func (r *MaxScaleReconciler) reconcileExporterService(ctx context.Context, mxs *mariadbv1alpha1.MaxScale) error {
key := mxs.MetricsKey()
selectorLabels :=
labels.NewLabelsBuilder().
WithMetricsSelectorLabels(key).
Build()
opts := builder.ServiceOpts{
ServiceTemplate: mariadbv1alpha1.ServiceTemplate{
Metadata: &mariadbv1alpha1.Metadata{
Labels: selectorLabels,
},
},
Ports: []corev1.ServicePort{
{
Name: builder.MetricsPortName,
Port: mxs.Spec.Metrics.Exporter.Port,
},
},
SelectorLabels: selectorLabels,
ExtraMeta: mxs.Spec.InheritMetadata,
}
desiredSvc, err := r.Builder.BuildService(key, mxs, opts)
if err != nil {
return fmt.Errorf("error building exporter Service: %v", err)
}
return r.ServiceReconciler.Reconcile(ctx, desiredSvc)
}
func (r *MaxScaleReconciler) reconcileServiceMonitor(ctx context.Context, mxs *mariadbv1alpha1.MaxScale) error {
desiredSvcMonitor, err := r.Builder.BuildMaxScaleServiceMonitor(mxs)
if err != nil {
return fmt.Errorf("error building Service Monitor: %v", err)
}
return r.ServiceMonitorReconciler.Reconcile(ctx, desiredSvcMonitor)
}