Skip to content

Commit 7e7b3d7

Browse files
authored
Add support for externally managed members (#1214)
1 parent abdd30f commit 7e7b3d7

36 files changed

Lines changed: 758 additions & 369 deletions

api/core/v1alpha1/constants.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ const (
3939
// This value will only be effective if etcd-druid is not configured with auto-reconciliation of Etcd resource specification via
4040
// --enable-etcd-spec-auto-reconcile CLI flag.
4141
DruidOperationReconcile = "reconcile"
42-
// DisableEtcdRuntimeComponentCreationAnnotation is an annotation set by an operator to disable the creation and management of
43-
// runtime components of the etcd cluster such as pods, PVCs, leases, RBAC resources, PDBs, services, etc.
44-
DisableEtcdRuntimeComponentCreationAnnotation = "druid.gardener.cloud/disable-etcd-runtime-component-creation"
4542
)
4643

4744
// Compaction Job/Pod reasons that are used to set the reason for a pod condition in the status of an Etcd resource.

api/core/v1alpha1/crds/druid.gardener.cloud_etcds.yaml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -668,13 +668,22 @@ spec:
668668
snapshotCount:
669669
description: |-
670670
SnapshotCount defines the number of applied Raft entries to hold in-memory before compaction.
671-
More info: https://etcd.io/docs/v3.4/op-guide/maintenance/#raft-log-retention
671+
More info: https://etcd.io/docs/v3.5/op-guide/maintenance/#raft-log-retention
672672
format: int64
673673
type: integer
674674
wrapperPort:
675675
format: int32
676676
type: integer
677677
type: object
678+
externallyManagedMemberAddresses:
679+
description: |-
680+
ExternallyManagedMemberAddresses defines the list of addresses of externally managed etcd members. Specifying this
681+
will disable components that are involved in management of etcd members like Pods, Services and PDBs.
682+
Allowed values include: IPv4/IPv6 addresses and hostnames. Protocol or port shall not be specified.
683+
items:
684+
type: string
685+
type: array
686+
x-kubernetes-list-type: set
678687
labels:
679688
additionalProperties:
680689
type: string
@@ -1901,10 +1910,18 @@ spec:
19011910
- replicas
19021911
type: object
19031912
x-kubernetes-validations:
1904-
- message: etcd.spec.storageClass is an immutable field.
1913+
- message: etcd.spec.storageClass field cannot be added or removed dynamically.
19051914
rule: has(oldSelf.storageClass) == has(self.storageClass)
1906-
- message: etcd.spec.volumeClaimTemplate is an immutable field.
1915+
- message: etcd.spec.volumeClaimTemplate field cannot be added or removed
1916+
dynamically.
19071917
rule: has(oldSelf.volumeClaimTemplate) == has(self.volumeClaimTemplate)
1918+
- message: etcd.spec.replicas must be equal to length of etcd.spec.externallyManagedMemberAddresses
1919+
when etcd.spec.externallyManagedMemberAddresses is specified.
1920+
rule: 'has(self.externallyManagedMemberAddresses) ? self.replicas ==
1921+
self.externallyManagedMemberAddresses.size() : true'
1922+
- message: etcd.spec.externallyManagedMemberAddresses field cannot be
1923+
added or removed dynamically.
1924+
rule: has(oldSelf.externallyManagedMemberAddresses) == has(self.externallyManagedMemberAddresses)
19081925
status:
19091926
description: EtcdStatus defines the observed state of Etcd.
19101927
properties:

api/core/v1alpha1/crds/druid.gardener.cloud_etcds_without_cel.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,13 +601,22 @@ spec:
601601
snapshotCount:
602602
description: |-
603603
SnapshotCount defines the number of applied Raft entries to hold in-memory before compaction.
604-
More info: https://etcd.io/docs/v3.4/op-guide/maintenance/#raft-log-retention
604+
More info: https://etcd.io/docs/v3.5/op-guide/maintenance/#raft-log-retention
605605
format: int64
606606
type: integer
607607
wrapperPort:
608608
format: int32
609609
type: integer
610610
type: object
611+
externallyManagedMemberAddresses:
612+
description: |-
613+
ExternallyManagedMemberAddresses defines the list of addresses of externally managed etcd members. Specifying this
614+
will disable components that are involved in management of etcd members like Pods, Services and PDBs.
615+
Allowed values include: IPv4/IPv6 addresses and hostnames. Protocol or port shall not be specified.
616+
items:
617+
type: string
618+
type: array
619+
x-kubernetes-list-type: set
611620
labels:
612621
additionalProperties:
613622
type: string

api/core/v1alpha1/etcd.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ type EtcdConfig struct {
220220
// +optional
221221
Quota *resource.Quantity `json:"quota,omitempty"`
222222
// SnapshotCount defines the number of applied Raft entries to hold in-memory before compaction.
223-
// More info: https://etcd.io/docs/v3.4/op-guide/maintenance/#raft-log-retention
223+
// More info: https://etcd.io/docs/v3.5/op-guide/maintenance/#raft-log-retention
224224
// +optional
225225
SnapshotCount *int64 `json:"snapshotCount,omitempty"`
226226
// EnableGRPCGateway enables the gRPC-Gateway proxy for etcd.
@@ -310,8 +310,10 @@ type SchedulingConstraints struct {
310310
}
311311

312312
// EtcdSpec defines the desired state of Etcd
313-
// +kubebuilder:validation:XValidation:message="etcd.spec.storageClass is an immutable field.",rule="has(oldSelf.storageClass) == has(self.storageClass)"
314-
// +kubebuilder:validation:XValidation:message="etcd.spec.volumeClaimTemplate is an immutable field.",rule="has(oldSelf.volumeClaimTemplate) == has(self.volumeClaimTemplate)"
313+
// +kubebuilder:validation:XValidation:message="etcd.spec.storageClass field cannot be added or removed dynamically.",rule="has(oldSelf.storageClass) == has(self.storageClass)"
314+
// +kubebuilder:validation:XValidation:message="etcd.spec.volumeClaimTemplate field cannot be added or removed dynamically.",rule="has(oldSelf.volumeClaimTemplate) == has(self.volumeClaimTemplate)"
315+
// +kubebuilder:validation:XValidation:message="etcd.spec.replicas must be equal to length of etcd.spec.externallyManagedMemberAddresses when etcd.spec.externallyManagedMemberAddresses is specified.",rule="has(self.externallyManagedMemberAddresses) ? self.replicas == self.externallyManagedMemberAddresses.size() : true"
316+
// +kubebuilder:validation:XValidation:message="etcd.spec.externallyManagedMemberAddresses field cannot be added or removed dynamically.",rule="has(oldSelf.externallyManagedMemberAddresses) == has(self.externallyManagedMemberAddresses)"
315317
type EtcdSpec struct {
316318
// selector is a label query over pods that should match the replica count.
317319
// It must match the pod template's labels.
@@ -358,6 +360,12 @@ type EtcdSpec struct {
358360
// run as root. By default, they run as non-root with user 'nobody'.
359361
// +optional
360362
RunAsRoot *bool `json:"runAsRoot,omitempty"`
363+
// ExternallyManagedMemberAddresses defines the list of addresses of externally managed etcd members. Specifying this
364+
// will disable components that are involved in management of etcd members like Pods, Services and PDBs.
365+
// Allowed values include: IPv4/IPv6 addresses and hostnames. Protocol or port shall not be specified.
366+
// +optional
367+
// +listType=set
368+
ExternallyManagedMemberAddresses []string `json:"externallyManagedMemberAddresses,omitempty"`
361369
}
362370

363371
// CrossVersionObjectReference contains enough information to let you identify the referred resource.

api/core/v1alpha1/helper.go

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
package v1alpha1
66

77
import (
8+
"crypto/rand"
89
"fmt"
10+
"math/big"
911

1012
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1113
"k8s.io/apimachinery/pkg/types"
@@ -24,6 +26,22 @@ func GetClientServiceName(etcdObjMeta metav1.ObjectMeta) string {
2426
return fmt.Sprintf("%s-client", etcdObjMeta.Name)
2527
}
2628

29+
// GetClientHostname returns the hostname of the client endpoint for the Etcd cluster. This is the client service hostname when the Etcd members
30+
// are managed by etcd-druid, else it is a randomly selected member address out of the externally managed member addresses.
31+
func GetClientHostname(etcd *Etcd) string {
32+
if ArePodsManagedByEtcdDruid(etcd) {
33+
return fmt.Sprintf("%s.%s.svc", GetClientServiceName(etcd.ObjectMeta), etcd.Namespace)
34+
} else {
35+
n, err := rand.Int(rand.Reader, big.NewInt(int64(len(etcd.Spec.ExternallyManagedMemberAddresses))))
36+
if err != nil {
37+
// Fallback to first member address in case of an error
38+
return etcd.Spec.ExternallyManagedMemberAddresses[0]
39+
}
40+
randomIndex := int(n.Int64())
41+
return etcd.Spec.ExternallyManagedMemberAddresses[randomIndex]
42+
}
43+
}
44+
2745
// GetServiceAccountName returns the service account name for the Etcd.
2846
func GetServiceAccountName(etcdObjMeta metav1.ObjectMeta) string {
2947
return etcdObjMeta.Name
@@ -44,6 +62,11 @@ func GetOrdinalPodName(etcdObjMeta metav1.ObjectMeta, ordinal int) string {
4462
return fmt.Sprintf("%s-%d", etcdObjMeta.Name, ordinal)
4563
}
4664

65+
// GetMemberNameFromAddress returns the name of the etcd member based on the address.
66+
func GetMemberNameFromAddress(etcdObjMeta metav1.ObjectMeta, memberAddress string) string {
67+
return fmt.Sprintf("%s-%s", etcdObjMeta.Name, memberAddress)
68+
}
69+
4770
// GetAllPodNames returns the names of all pods for the Etcd.
4871
func GetAllPodNames(etcdObjMeta metav1.ObjectMeta, replicas int32) []string {
4972
podNames := make([]string, replicas)
@@ -54,12 +77,17 @@ func GetAllPodNames(etcdObjMeta metav1.ObjectMeta, replicas int32) []string {
5477
}
5578

5679
// GetMemberLeaseNames returns the name of member leases for the Etcd.
57-
func GetMemberLeaseNames(etcdObjMeta metav1.ObjectMeta, replicas int32) []string {
58-
leaseNames := make([]string, replicas)
59-
for i := range int(replicas) {
60-
leaseNames[i] = fmt.Sprintf("%s-%d", etcdObjMeta.Name, i)
80+
func GetMemberLeaseNames(etcd *Etcd) []string {
81+
if ArePodsManagedByEtcdDruid(etcd) {
82+
return GetAllPodNames(etcd.ObjectMeta, etcd.Spec.Replicas)
83+
} else {
84+
memberAddresses := etcd.Spec.ExternallyManagedMemberAddresses
85+
memberNames := make([]string, len(memberAddresses))
86+
for i, memberAddress := range memberAddresses {
87+
memberNames[i] = GetMemberNameFromAddress(etcd.ObjectMeta, memberAddress)
88+
}
89+
return memberNames
6190
}
62-
return leaseNames
6391
}
6492

6593
// GetPodDisruptionBudgetName returns the name of the pod disruption budget for the Etcd.
@@ -166,7 +194,7 @@ func RemoveOperationAnnotation(etcdObjMeta metav1.ObjectMeta) {
166194
delete(etcdObjMeta.Annotations, GardenerOperationAnnotation)
167195
}
168196

169-
// IsEtcdRuntimeComponentCreationEnabled checks if the creation of runtime components is enabled for an Etcd resource.
170-
func IsEtcdRuntimeComponentCreationEnabled(etcdObjMeta metav1.ObjectMeta) bool {
171-
return !metav1.HasAnnotation(etcdObjMeta, DisableEtcdRuntimeComponentCreationAnnotation)
197+
// ArePodsManagedByEtcdDruid checks if the management of pods is handled by etcd-druid for an Etcd resource.
198+
func ArePodsManagedByEtcdDruid(etcd *Etcd) bool {
199+
return len(etcd.Spec.ExternallyManagedMemberAddresses) == 0
172200
}

0 commit comments

Comments
 (0)