Skip to content

Commit

Permalink
feat: watch Querier TLS resources
Browse files Browse the repository at this point in the history
  • Loading branch information
vyzigold committed Sep 30, 2024
1 parent 0056b46 commit 6c33c58
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 4 deletions.
22 changes: 19 additions & 3 deletions pkg/controllers/monitoring/thanos-querier/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@ import (
"github.com/rhobs/observability-operator/pkg/reconciler"
)

func thanosComponentReconcilers(thanos *msoapi.ThanosQuerier, sidecarUrls []string, thanosCfg ThanosConfiguration) []reconciler.Reconciler {
func thanosComponentReconcilers(
thanos *msoapi.ThanosQuerier,
sidecarUrls []string,
thanosCfg ThanosConfiguration,
tlsHashes map[string]string,
) []reconciler.Reconciler {
name := "thanos-querier-" + thanos.Name
return []reconciler.Reconciler{
reconciler.NewUpdater(newServiceAccount(name, thanos.Namespace), thanos),
reconciler.NewUpdater(newThanosQuerierDeployment(name, thanos, sidecarUrls, thanosCfg), thanos),
reconciler.NewUpdater(newThanosQuerierDeployment(name, thanos, sidecarUrls, thanosCfg, tlsHashes), thanos),
reconciler.NewUpdater(newService(name, thanos.Namespace), thanos),
reconciler.NewUpdater(newServiceMonitor(name, thanos.Namespace, thanos), thanos),
reconciler.NewOptionalUpdater(newHttpConfConfigMap(name, thanos), thanos, thanos.Spec.WebTLSConfig != nil),
Expand Down Expand Up @@ -47,7 +52,13 @@ tls_server_config:
return httpConf
}

func newThanosQuerierDeployment(name string, spec *msoapi.ThanosQuerier, sidecarUrls []string, thanosCfg ThanosConfiguration) *appsv1.Deployment {
func newThanosQuerierDeployment(
name string,
spec *msoapi.ThanosQuerier,
sidecarUrls []string,
thanosCfg ThanosConfiguration,
tlsHashes map[string]string,
) *appsv1.Deployment {
httpConfCMName := fmt.Sprintf("%s-http-conf", name)

args := []string{
Expand Down Expand Up @@ -178,6 +189,11 @@ func newThanosQuerierDeployment(name string, spec *msoapi.ThanosQuerier, sidecar
ReadOnly: true,
},
}...)
tlsAnnotations := map[string]string{}
for name, hash := range tlsHashes {
tlsAnnotations[fmt.Sprintf("monitoring.openshift.io/%s-hash", name)] = hash
}
thanos.ObjectMeta.Annotations = tlsAnnotations
}

return thanos
Expand Down
121 changes: 120 additions & 1 deletion pkg/controllers/monitoring/thanos-querier/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ package thanos_querier

import (
"context"
"crypto/sha256"
"fmt"
"time"

Expand All @@ -22,9 +23,11 @@ import (
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/rand"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -51,6 +54,12 @@ type Options struct {
Thanos ThanosConfiguration
}

const (
thanosTLSPrivateKeySecretNameField = ".spec.webTLSConfig.privateKey.name"
thanosTLSCertificateSecretNameField = ".spec.webTLSConfig.certificate.name"
thanosTLSCertificateAuthoritySecretNameField = ".spec.webTLSConfig.certificateAuthority.name"
)

// RBAC for watching monitoring stacks
//+kubebuilder:rbac:groups=monitoring.rhobs,resources=monitoringstacks,verbs=list;watch

Expand All @@ -64,6 +73,7 @@ type Options struct {

// RBAC for managing core resources
//+kubebuilder:rbac:groups=core,resources=services;serviceaccounts;configmaps,verbs=list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=core,resources=secrets,verbs=list;watch

// RBAC for managing Prometheus Operator CRs
//+kubebuilder:rbac:groups=monitoring.rhobs,resources=servicemonitors,verbs=list;watch;create;update;patch;delete
Expand All @@ -79,17 +89,56 @@ func RegisterWithManager(mgr ctrl.Manager, opts Options) error {
thanos: opts.Thanos,
}

if err := mgr.GetFieldIndexer().IndexField(context.Background(), &msoapi.ThanosQuerier{}, thanosTLSPrivateKeySecretNameField, func(rawObj client.Object) []string {
// Extract the secret name from the spec, if one is provided
cr := rawObj.(*msoapi.ThanosQuerier)
if cr.Spec.WebTLSConfig == nil {
return nil
}
return []string{cr.Spec.WebTLSConfig.PrivateKey.Name}
}); err != nil {
return err
}

if err := mgr.GetFieldIndexer().IndexField(context.Background(), &msoapi.ThanosQuerier{}, thanosTLSCertificateSecretNameField, func(rawObj client.Object) []string {
// Extract the secret name from the spec, if one is provided
cr := rawObj.(*msoapi.ThanosQuerier)
if cr.Spec.WebTLSConfig == nil {
return nil
}
return []string{cr.Spec.WebTLSConfig.Certificate.Name}
}); err != nil {
return err
}

if err := mgr.GetFieldIndexer().IndexField(context.Background(), &msoapi.ThanosQuerier{}, thanosTLSCertificateAuthoritySecretNameField, func(rawObj client.Object) []string {
// Extract the secret name from the spec, if one is provided
cr := rawObj.(*msoapi.ThanosQuerier)
if cr.Spec.WebTLSConfig == nil {
return nil
}
return []string{cr.Spec.WebTLSConfig.CertificateAuthority.Name}
}); err != nil {
return err
}

p := predicate.GenerationChangedPredicate{}
return ctrl.NewControllerManagedBy(mgr).
For(&msoapi.ThanosQuerier{}).
Owns(&appsv1.Deployment{}).WithEventFilter(p).
Owns(&corev1.ServiceAccount{}).WithEventFilter(p).
Owns(&corev1.Service{}).WithEventFilter(p).
Owns(&corev1.ConfigMap{}).WithEventFilter(p).
Watches(
&msoapi.MonitoringStack{},
handler.EnqueueRequestsFromMapFunc(rm.findQueriersForMonitoringStack),
builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}),
).
Watches(
&corev1.Secret{},
handler.EnqueueRequestsFromMapFunc(rm.findQueriersForTLSSecrets),
builder.WithPredicates(predicate.GenerationChangedPredicate{}),
).
Complete(rm)
}

Expand All @@ -113,7 +162,23 @@ func (rm resourceManager) Reconcile(ctx context.Context, req ctrl.Request) (ctrl
return ctrl.Result{RequeueAfter: 10 * time.Second}, err
}

reconcilers := thanosComponentReconcilers(querier, sidecarServices, rm.thanos)
tlsHashes := map[string]string{}
if querier.Spec.WebTLSConfig != nil {
secretSelectors := []msoapi.SecretKeySelector{
querier.Spec.WebTLSConfig.CertificateAuthority,
querier.Spec.WebTLSConfig.Certificate,
querier.Spec.WebTLSConfig.PrivateKey,
}
for _, secretSelector := range secretSelectors {
hash, err := rm.hashOfTLSSecret(secretSelector, querier.Namespace)
if err != nil {
return ctrl.Result{}, err
}
tlsHashes[fmt.Sprintf("%s-%s", secretSelector.Name, secretSelector.Key)] = hash
}
}

reconcilers := thanosComponentReconcilers(querier, sidecarServices, rm.thanos, tlsHashes)
for _, reconciler := range reconcilers {
err := reconciler.Reconcile(ctx, rm, rm.scheme)
// handle creation / updation errors that can happen due to a stale cache by
Expand Down Expand Up @@ -156,6 +221,20 @@ func (rm resourceManager) findSidecarServices(ctx context.Context, tQuerier *mso
return sidecarUrls, nil
}

func (rm resourceManager) hashOfTLSSecret(selector msoapi.SecretKeySelector, namespace string) (string, error) {
var secret corev1.Secret
err := rm.Get(context.Background(), types.NamespacedName{
Name: selector.Name,
Namespace: namespace,
}, &secret)
if err != nil {
return "", fmt.Errorf("Couldn't get TLS secret %s: %s", selector.Name, err)
}

hash := sha256.Sum256(secret.Data[selector.Key])
return rand.SafeEncodeString(fmt.Sprint(hash)), nil
}

// Given a Service object, return a url to use as value for --store/--endpoint.
func getEndpointUrl(serviceName string, namespace string) string {
return fmt.Sprintf("dnssrv+_grpc._tcp.%s.%s.svc.cluster.local", serviceName, namespace)
Expand Down Expand Up @@ -191,3 +270,43 @@ func (rm resourceManager) findQueriersForMonitoringStack(ctx context.Context, ms
}
return requests
}

// Find all ThanosQueriers, whose TLS secrets fit the given Secret and
// return a list of reconcile requests, one for each ThanosQuerier.
func (rm resourceManager) findQueriersForTLSSecrets(ctx context.Context, src client.Object) []reconcile.Request {
requests := []reconcile.Request{}

logger := rm.logger.WithValues("Secret", src.GetNamespace()+"/"+src.GetName())
logger.Info("watched Secret changed, checking for matching querier")

thanosWatchFields := []string{
thanosTLSCertificateAuthoritySecretNameField,
thanosTLSCertificateSecretNameField,
thanosTLSPrivateKeySecretNameField,
}

for _, field := range thanosWatchFields {
crList := &msoapi.ThanosQuerierList{}
listOps := &client.ListOptions{
FieldSelector: fields.OneTermEqualSelector(field, src.GetName()),
Namespace: src.GetNamespace(),
}
err := rm.Client.List(ctx, crList, listOps)
if err != nil {
logger.Error(err, "Failed to list Thanosqueriers")
return []reconcile.Request{}
}

for _, item := range crList.Items {
logger.Info("Found querier, scheduling sync")
requests = append(requests, reconcile.Request{
NamespacedName: types.NamespacedName{
Name: item.GetName(),
Namespace: item.GetNamespace(),
},
})
}
}

return requests
}

0 comments on commit 6c33c58

Please sign in to comment.