From 62d8ae319d542e2b2f2a06901543c996b3376285 Mon Sep 17 00:00:00 2001 From: Joe Nathan Abellard Date: Tue, 15 Oct 2024 15:58:43 -0400 Subject: [PATCH] Onwards! Signed-off-by: Joe Nathan Abellard --- .../crds/operator.karmada.io_karmadas.yaml | 24 ++++++++++-- .../crds/operator.karmada.io_karmadas.yaml | 24 ++++++++++-- operator/pkg/apis/operator/v1alpha1/type.go | 19 ++++++++-- .../v1alpha1/zz_generated.deepcopy.go | 1 + operator/pkg/constants/constants.go | 3 ++ operator/pkg/controller/karmada/controller.go | 38 +++++++++++++++++++ 6 files changed, 100 insertions(+), 9 deletions(-) diff --git a/charts/karmada-operator/crds/operator.karmada.io_karmadas.yaml b/charts/karmada-operator/crds/operator.karmada.io_karmadas.yaml index 11a15e79305c..f191d3d18ee5 100644 --- a/charts/karmada-operator/crds/operator.karmada.io_karmadas.yaml +++ b/charts/karmada-operator/crds/operator.karmada.io_karmadas.yaml @@ -65,12 +65,14 @@ spec: description: |- CAData is an SSL Certificate Authority file used to secure etcd communication. Required if using a TLS connection. + Deprecated: This field is deprecated and will be removed in a future version. Use SecretRef for providing client connection credentials. format: byte type: string certData: description: |- CertData is an SSL certification file used to secure etcd communication. Required if using a TLS connection. + Deprecated: This field is deprecated and will be removed in a future version. Use SecretRef for providing client connection credentials. format: byte type: string endpoints: @@ -82,13 +84,29 @@ spec: description: |- KeyData is an SSL key file used to secure etcd communication. Required if using a TLS connection. + Deprecated: This field is deprecated and will be removed in a future version. Use SecretRef for providing client connection credentials. format: byte type: string + secretRef: + description: |- + SecretRef references a Kubernetes secret containing the etcd connection credentials. + The secret must contain the following data keys: + ca.crt: The Certificate Authority (CA) certificate data. + tls.crt: The TLS certificate data used for verifying the etcd server's certificate. + tls.key: The TLS private key. + Required to configure the connection to an external etcd cluster. + properties: + name: + description: Name is the name of resource being referenced. + type: string + namespace: + description: Namespace is the namespace for the resource + being referenced. + type: string + type: object required: - - caData - - certData - endpoints - - keyData + - secretRef type: object local: description: |- diff --git a/operator/config/crds/operator.karmada.io_karmadas.yaml b/operator/config/crds/operator.karmada.io_karmadas.yaml index 11a15e79305c..f191d3d18ee5 100644 --- a/operator/config/crds/operator.karmada.io_karmadas.yaml +++ b/operator/config/crds/operator.karmada.io_karmadas.yaml @@ -65,12 +65,14 @@ spec: description: |- CAData is an SSL Certificate Authority file used to secure etcd communication. Required if using a TLS connection. + Deprecated: This field is deprecated and will be removed in a future version. Use SecretRef for providing client connection credentials. format: byte type: string certData: description: |- CertData is an SSL certification file used to secure etcd communication. Required if using a TLS connection. + Deprecated: This field is deprecated and will be removed in a future version. Use SecretRef for providing client connection credentials. format: byte type: string endpoints: @@ -82,13 +84,29 @@ spec: description: |- KeyData is an SSL key file used to secure etcd communication. Required if using a TLS connection. + Deprecated: This field is deprecated and will be removed in a future version. Use SecretRef for providing client connection credentials. format: byte type: string + secretRef: + description: |- + SecretRef references a Kubernetes secret containing the etcd connection credentials. + The secret must contain the following data keys: + ca.crt: The Certificate Authority (CA) certificate data. + tls.crt: The TLS certificate data used for verifying the etcd server's certificate. + tls.key: The TLS private key. + Required to configure the connection to an external etcd cluster. + properties: + name: + description: Name is the name of resource being referenced. + type: string + namespace: + description: Namespace is the namespace for the resource + being referenced. + type: string + type: object required: - - caData - - certData - endpoints - - keyData + - secretRef type: object local: description: |- diff --git a/operator/pkg/apis/operator/v1alpha1/type.go b/operator/pkg/apis/operator/v1alpha1/type.go index fdee55ae2fec..954f95c571b5 100644 --- a/operator/pkg/apis/operator/v1alpha1/type.go +++ b/operator/pkg/apis/operator/v1alpha1/type.go @@ -239,19 +239,32 @@ type VolumeData struct { // operator has no knowledge of where certificate files live, and they must be supplied. type ExternalEtcd struct { // Endpoints of etcd members. Required for ExternalEtcd. + // +required Endpoints []string `json:"endpoints"` // CAData is an SSL Certificate Authority file used to secure etcd communication. // Required if using a TLS connection. - CAData []byte `json:"caData"` + // Deprecated: This field is deprecated and will be removed in a future version. Use SecretRef for providing client connection credentials. + CAData []byte `json:"caData,omitempty"` // CertData is an SSL certification file used to secure etcd communication. // Required if using a TLS connection. - CertData []byte `json:"certData"` + // Deprecated: This field is deprecated and will be removed in a future version. Use SecretRef for providing client connection credentials. + CertData []byte `json:"certData,omitempty"` // KeyData is an SSL key file used to secure etcd communication. // Required if using a TLS connection. - KeyData []byte `json:"keyData"` + // Deprecated: This field is deprecated and will be removed in a future version. Use SecretRef for providing client connection credentials. + KeyData []byte `json:"keyData,omitempty"` + + // SecretRef references a Kubernetes secret containing the etcd connection credentials. + // The secret must contain the following data keys: + // ca.crt: The Certificate Authority (CA) certificate data. + // tls.crt: The TLS certificate data used for verifying the etcd server's certificate. + // tls.key: The TLS private key. + // Required to configure the connection to an external etcd cluster. + // +required + SecretRef LocalSecretReference `json:"secretRef"` } // KarmadaAPIServer holds settings to kube-apiserver component of the kubernetes. diff --git a/operator/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go b/operator/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go index 3e125e0eef17..5f727b7c73b7 100644 --- a/operator/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go +++ b/operator/pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go @@ -139,6 +139,7 @@ func (in *ExternalEtcd) DeepCopyInto(out *ExternalEtcd) { *out = make([]byte, len(*in)) copy(*out, *in) } + out.SecretRef = in.SecretRef return } diff --git a/operator/pkg/constants/constants.go b/operator/pkg/constants/constants.go index ff7382ef52b8..e2ca65f53d99 100644 --- a/operator/pkg/constants/constants.go +++ b/operator/pkg/constants/constants.go @@ -125,6 +125,9 @@ const ( // APIServiceName defines the karmada aggregated apiserver APIService resource name. APIServiceName = "v1alpha1.cluster.karmada.io" + + // KarmadaApiserverEtcdClientCertNameSuffix defines the suffix for Karmada operator etcd client cert secret name + KarmadaApiserverEtcdClientCertNameSuffix = "karmada-apiserver-etcd-client-cert" ) var ( diff --git a/operator/pkg/controller/karmada/controller.go b/operator/pkg/controller/karmada/controller.go index bd56007acf4d..d8796ae74b7e 100644 --- a/operator/pkg/controller/karmada/controller.go +++ b/operator/pkg/controller/karmada/controller.go @@ -18,11 +18,15 @@ package karmada import ( "context" + "fmt" "reflect" "strconv" "time" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/rest" "k8s.io/client-go/tools/record" @@ -36,6 +40,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" operatorv1alpha1 "github.com/karmada-io/karmada/operator/pkg/apis/operator/v1alpha1" + "github.com/karmada-io/karmada/operator/pkg/constants" operatorscheme "github.com/karmada-io/karmada/operator/pkg/scheme" ) @@ -48,6 +53,9 @@ const ( // DisableCascadingDeletionLabel is the label that determine whether to perform cascade deletion DisableCascadingDeletionLabel = "operator.karmada.io/disable-cascading-deletion" + + // ValidationErrorReason is the reason for a validation error + ValidationErrorReason = "ValidationErrorReason" ) // Controller controls the Karmada resource. @@ -77,6 +85,11 @@ func (ctrl *Controller) Reconcile(ctx context.Context, req controllerruntime.Req return controllerruntime.Result{}, err } + if err := ctrl.validateKarmada(karmada); err != nil { + klog.Error(err, "Validation failed for karmada") + return controllerruntime.Result{}, nil + } + // The object is being deleted if !karmada.DeletionTimestamp.IsZero() { val, ok := karmada.Labels[DisableCascadingDeletionLabel] @@ -96,6 +109,31 @@ func (ctrl *Controller) Reconcile(ctx context.Context, req controllerruntime.Req return controllerruntime.Result{}, ctrl.syncKarmada(karmada) } +// validateKarmada ensures the Karmada resource adheres to validation rules +func (ctrl *Controller) validateKarmada(karmada *operatorv1alpha1.Karmada) error { + if karmada.Spec.Components.Etcd != nil && karmada.Spec.Components.Etcd.External != nil { + expectedSecretName := fmt.Sprintf("%s-%s", karmada.Name, constants.KarmadaApiserverEtcdClientCertNameSuffix) + if karmada.Spec.Components.Etcd.External.SecretRef.Name != expectedSecretName { + errorMessage := fmt.Sprintf("Secret name for external etcd client must be %s, but got %s", expectedSecretName, karmada.Spec.Components.Etcd.External.SecretRef.Name) + ctrl.EventRecorder.Event(karmada, corev1.EventTypeWarning, ValidationErrorReason, errorMessage) + + newCondition := metav1.Condition{ + Type: string(operatorv1alpha1.Ready), + Status: metav1.ConditionFalse, + Reason: ValidationErrorReason, + Message: errorMessage, + LastTransitionTime: metav1.Now(), + } + meta.SetStatusCondition(&karmada.Status.Conditions, newCondition) + if err := ctrl.Status().Update(context.TODO(), karmada); err != nil { + return err + } + return fmt.Errorf(errorMessage) + } + } + return nil +} + func (ctrl *Controller) syncKarmada(karmada *operatorv1alpha1.Karmada) error { klog.V(2).InfoS("Reconciling karmada", "name", karmada.Name) planner, err := NewPlannerFor(karmada, ctrl.Client, ctrl.Config)