Forráskód Böngészése

feat: add credential reconciler

Thomas Zhang 2 hónapja
szülő
commit
562c56f9fb
1 módosított fájl, 99 hozzáadás és 0 törlés
  1. 99 0
      internal/reconciler/credential.go

+ 99 - 0
internal/reconciler/credential.go

@@ -0,0 +1,99 @@
+/*
+Copyright 2026 LocoStack.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package reconciler
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/LocoStack/loco-operator/api/v1alpha1"
+	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/runtime"
+	"k8s.io/apimachinery/pkg/types"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+)
+
+// reconcileCredentials checks that all referenced Secrets exist and contain the
+// required keys. Returns true when all credentials are resolved.
+func ReconcileCredentials(ctx context.Context, c client.Client, scheme *runtime.Scheme, authSpec *v1alpha1.AuthSpec, obj client.Object) (*metav1.Condition, error) {
+	log := logf.FromContext(ctx)
+
+	if authSpec == nil {
+		return &metav1.Condition{
+			Type:               "CredentialsResolved",
+			Status:             metav1.ConditionTrue,
+			Reason:             "NoCredentialsRequired",
+			Message:            "No auth configured",
+			ObservedGeneration: obj.GetGeneration(),
+		}, nil
+	}
+
+	secretRefs := make([]*corev1.SecretKeySelector, 0)
+
+	if authSpec.APIKey != nil {
+		secretRefs = append(secretRefs, &authSpec.APIKey.SecretRef)
+	}
+	if authSpec.BearerToken != nil {
+		secretRefs = append(secretRefs, authSpec.BearerToken)
+	}
+	for _, hdr := range authSpec.Headers {
+		if hdr.ValueFrom.SecretKeyRef != nil {
+			secretRefs = append(secretRefs, hdr.ValueFrom.SecretKeyRef)
+		}
+	}
+
+	for _, ref := range secretRefs {
+		if reason, msg, err := checkSecretKey(ctx, c, obj.GetNamespace(), ref.Name, ref.Key); err != nil {
+			log.Error(err, "Failed to check API key secret", "namespace", obj.GetNamespace(), "name", obj.GetName(), "secretName", ref.Name, "secretKey", ref.Key)
+			return nil, err
+		} else {
+			if reason != "" {
+				return &metav1.Condition{
+					Type:    "CredentialsResolved",
+					Status:  metav1.ConditionTrue,
+					Reason:  reason,
+					Message: msg,
+				}, nil
+			}
+		}
+	}
+
+	return &metav1.Condition{
+		Type:    "CredentialsResolved",
+		Status:  metav1.ConditionTrue,
+		Reason:  "CredentialsFound",
+		Message: "All referenced secrets exist and contain the required keys",
+	}, nil
+}
+
+// checkSecretKey verifies that a Secret exists and contains the given key.
+func checkSecretKey(ctx context.Context, c client.Client, namespace, secretName, key string) (string, string, error) {
+	secret := &corev1.Secret{}
+	if err := c.Get(ctx, types.NamespacedName{Name: secretName, Namespace: namespace}, secret); err != nil {
+		if apierrors.IsNotFound(err) {
+			return "SecretNotFound", fmt.Sprintf("Secret %q not found", secretName), nil
+		}
+		return "", "", err
+	}
+	if _, ok := secret.Data[key]; !ok {
+		return "SecretKeyMissing", fmt.Sprintf("Secret %q does not contain key %q", secretName, key), nil
+	}
+	return "", "", nil
+}