Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

External etcd Support for Karmada Operator - Part 1 #5699

Merged
merged 1 commit into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions charts/karmada-operator/crds/operator.karmada.io_karmadas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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: |-
Expand Down
24 changes: 21 additions & 3 deletions operator/config/crds/operator.karmada.io_karmadas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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: |-
Expand Down
19 changes: 16 additions & 3 deletions operator/pkg/apis/operator/v1alpha1/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions operator/pkg/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ const (

// APIServiceName defines the karmada aggregated apiserver APIService resource name.
APIServiceName = "v1alpha1.cluster.karmada.io"

// KarmadaApiserverEtcdClientCertNameSuffix defines the suffix for the Karmada API server etcd client cert name
KarmadaApiserverEtcdClientCertNameSuffix = "karmada-apiserver-etcd-client-cert"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed at the community meeting, use it as a suffix or a whole name waiting for further exploration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RainbowMango , to clarify, I'm fine with whatever naming convention is used . What I'm actually interested in is the support for running multiple Karmada instances in the same namespace and if that will break. Today, the operator has support for that, and we've been relying on that support for some uses cases we have. I added some data on this thread to showcase a scenario where that's been working for us in one of our management clusters.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why you want to run multiple instances in the same namespace here?

@chaunceyjiang are you using it this way?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to clarify, I'm fine with whatever naming convention is used . What I'm actually interested in is the support for running multiple Karmada instances in the same namespace and if that will break. Today, the operator has support for that, and we've been relying on that support for some uses cases we have.

Since we now support running multiple instances in the same namespace, there is no reason to disable it. I'm just curious why users might want to manage them in the same namespace, as I think putting each Karmada instance in a separate namespace is a simpler approach. E.g. each instance belongs to one team, which will help manage the quotas.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some data on this thread to showcase a scenario where that's been working for us in one of our management clusters.

That was helpful and gave me a lot of confidence. In the historical discussions, I remember we talked more about having one instance per namespace. Thanks.

)

var (
Expand Down
38 changes: 38 additions & 0 deletions operator/pkg/controller/karmada/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
)

Expand All @@ -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 = "ValidationError"
)

// Controller controls the Karmada resource.
Expand Down Expand Up @@ -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]
Expand All @@ -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
}
Comment on lines +112 to +135
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks good. It covers what we discussed perfectly.

Just put some thoughts.
In my opinion, we probably need to have further validation logic for the other part of the Karmada object.
So, my suggestions are:

  • put the validation part into a separate file, e.g. validation.go.
  • abstract the etcd validation with a separate function.

But it is no big deal, we can refactor it in the future too. It depends on you.


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)
Expand Down