Skip to content

Commit 418fe49

Browse files
authored
Merge branch 'main' into chore/issue-263
2 parents e70d6e1 + 4438907 commit 418fe49

File tree

9 files changed

+236
-5
lines changed

9 files changed

+236
-5
lines changed

MAINTAINERS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
| ---------- | --------------- | ------- |
55
| Andrei Kvapil | [@kvaps](https://github.com/kvaps) | Ænix |
66
| George Gaál | [@gecube](https://github.com/gecube) | Ænix |
7+
| Kirill Klinchenkov | [@klinch0](https://github.com/klinch0) | Ænix |
8+
| Timofei Larkin | [@lllamnyp](https://github.com/lllamnyp) | Ænix |
79
| Kirill | [@sircthulhu](https://github.com/sircthulhu) | |
810
| Alex Gluck | [@AlexGluck](https://github.com/AlexGluck) | |
911
| Oleg Kunitsyn | [@hiddenmarten](https://github.com/hiddenmarten) | |

charts/etcd-operator/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
| etcdOperator.securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]}}` | ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ |
2727
| etcdOperator.service.port | int | `9443` | Service port |
2828
| etcdOperator.service.type | string | `"ClusterIP"` | Service type |
29+
| etcdOperator.vpa.enabled | bool | `true` | |
30+
| etcdOperator.vpa.maxAllowed.cpu | string | `"1000m"` | |
31+
| etcdOperator.vpa.maxAllowed.memory | string | `"1Gi"` | |
32+
| etcdOperator.vpa.minAllowed.cpu | string | `"100m"` | |
33+
| etcdOperator.vpa.minAllowed.memory | string | `"128Mi"` | |
2934
| fullnameOverride | string | `""` | Override a full name of helm release |
3035
| imagePullSecrets | list | `[]` | |
3136
| kubeRbacProxy.args[0] | string | `"--secure-listen-address=0.0.0.0:8443"` | |
@@ -41,6 +46,11 @@
4146
| kubeRbacProxy.securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]}}` | ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ |
4247
| kubeRbacProxy.service.port | int | `8443` | Service port |
4348
| kubeRbacProxy.service.type | string | `"ClusterIP"` | Service type |
49+
| kubeRbacProxy.vpa.enabled | bool | `true` | |
50+
| kubeRbacProxy.vpa.maxAllowed.cpu | string | `"500m"` | |
51+
| kubeRbacProxy.vpa.maxAllowed.memory | string | `"256Mi"` | |
52+
| kubeRbacProxy.vpa.minAllowed.cpu | string | `"50m"` | |
53+
| kubeRbacProxy.vpa.minAllowed.memory | string | `"64Mi"` | |
4454
| kubernetesClusterDomain | string | `"cluster.local"` | Kubernetes cluster domain prefix |
4555
| nameOverride | string | `""` | Override a name of helm release |
4656
| nodeSelector | object | `{}` | ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ |
@@ -51,4 +61,5 @@
5161
| serviceAccount.annotations | object | `{}` | Annotations to add to the service account |
5262
| serviceAccount.create | bool | `true` | Specifies whether a service account should be created |
5363
| tolerations | list | `[]` | ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ |
64+
| vpa.updatePolicy | string | `"Auto"` | |
5465

charts/etcd-operator/templates/workload/deployment.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ spec:
4545
readinessProbe:
4646
{{- toYaml . | nindent 12 }}
4747
{{- end }}
48+
{{- if not .Values.etcdOperator.vpa.enabled }}
4849
{{- with .Values.etcdOperator.resources }}
4950
resources:
5051
{{- toYaml . | nindent 12 }}
5152
{{- end }}
53+
{{- end }}
5254
{{- with .Values.etcdOperator.securityContext }}
5355
securityContext:
5456
{{- toYaml . | nindent 12 }}
@@ -87,10 +89,12 @@ spec:
8789
readinessProbe:
8890
{{- toYaml . | nindent 12 }}
8991
{{- end }}
92+
{{- if not .Values.kubeRbacProxy.vpa.enabled }}
9093
{{- with .Values.kubeRbacProxy.resources }}
9194
resources:
9295
{{- toYaml . | nindent 12 }}
9396
{{- end }}
97+
{{- end }}
9498
{{- with .Values.kubeRbacProxy.securityContext }}
9599
securityContext:
96100
{{- toYaml . | nindent 12 }}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{{- if or .Values.etcdOperator.vpa.enabled .Values.kubeRbacProxy.vpa.enabled }}
2+
apiVersion: autoscaling.k8s.io/v1
3+
kind: VerticalPodAutoscaler
4+
metadata:
5+
name: {{ include "etcd-operator.fullname" . }}-controller-manager
6+
labels:
7+
{{- include "etcd-operator.labels" . | nindent 4 }}
8+
spec:
9+
targetRef:
10+
apiVersion: "apps/v1"
11+
kind: Deployment
12+
name: {{ include "etcd-operator.fullname" . }}-controller-manager
13+
updatePolicy:
14+
updateMode: {{ .Values.vpa.updatePolicy | default "Auto" | quote }}
15+
resourcePolicy:
16+
containerPolicies:
17+
{{- if .Values.etcdOperator.vpa.enabled }}
18+
- containerName: etcd-operator
19+
{{- with .Values.etcdOperator.vpa.minAllowed }}
20+
minAllowed:
21+
{{- toYaml . | nindent 10 }}
22+
{{- end }}
23+
{{- with .Values.etcdOperator.vpa.maxAllowed }}
24+
maxAllowed:
25+
{{- toYaml . | nindent 10 }}
26+
{{- end }}
27+
controlledResources: ["cpu", "memory"]
28+
{{- end }}
29+
{{- if .Values.kubeRbacProxy.vpa.enabled }}
30+
- containerName: kube-rbac-proxy
31+
{{- with .Values.kubeRbacProxy.vpa.minAllowed }}
32+
minAllowed:
33+
{{- toYaml . | nindent 10 }}
34+
{{- end }}
35+
{{- with .Values.kubeRbacProxy.vpa.maxAllowed }}
36+
maxAllowed:
37+
{{- toYaml . | nindent 10 }}
38+
{{- end }}
39+
controlledResources: ["cpu", "memory"]
40+
{{- end }}
41+
{{- end }}

charts/etcd-operator/values.schema.json

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,36 @@
131131
}
132132
},
133133
"type": "object"
134+
},
135+
"vpa": {
136+
"properties": {
137+
"enabled": {
138+
"type": "boolean"
139+
},
140+
"maxAllowed": {
141+
"properties": {
142+
"cpu": {
143+
"type": "string"
144+
},
145+
"memory": {
146+
"type": "string"
147+
}
148+
},
149+
"type": "object"
150+
},
151+
"minAllowed": {
152+
"properties": {
153+
"cpu": {
154+
"type": "string"
155+
},
156+
"memory": {
157+
"type": "string"
158+
}
159+
},
160+
"type": "object"
161+
}
162+
},
163+
"type": "object"
134164
}
135165
},
136166
"type": "object"
@@ -227,6 +257,36 @@
227257
}
228258
},
229259
"type": "object"
260+
},
261+
"vpa": {
262+
"properties": {
263+
"enabled": {
264+
"type": "boolean"
265+
},
266+
"maxAllowed": {
267+
"properties": {
268+
"cpu": {
269+
"type": "string"
270+
},
271+
"memory": {
272+
"type": "string"
273+
}
274+
},
275+
"type": "object"
276+
},
277+
"minAllowed": {
278+
"properties": {
279+
"cpu": {
280+
"type": "string"
281+
},
282+
"memory": {
283+
"type": "string"
284+
}
285+
},
286+
"type": "object"
287+
}
288+
},
289+
"type": "object"
230290
}
231291
},
232292
"type": "object"
@@ -270,6 +330,14 @@
270330
},
271331
"tolerations": {
272332
"type": "array"
333+
},
334+
"vpa": {
335+
"properties": {
336+
"updatePolicy": {
337+
"type": "string"
338+
}
339+
},
340+
"type": "object"
273341
}
274342
},
275343
"type": "object"

charts/etcd-operator/values.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ etcdOperator:
8484
drop:
8585
- ALL
8686

87+
vpa:
88+
enabled: true
89+
minAllowed:
90+
cpu: 100m
91+
memory: 128Mi
92+
maxAllowed:
93+
cpu: 1000m
94+
memory: 1Gi
95+
8796
kubeRbacProxy:
8897

8998
image:
@@ -142,6 +151,15 @@ kubeRbacProxy:
142151
drop:
143152
- ALL
144153

154+
vpa:
155+
enabled: true
156+
minAllowed:
157+
cpu: 50m
158+
memory: 64Mi
159+
maxAllowed:
160+
cpu: 500m
161+
memory: 256Mi
162+
145163
# -- Kubernetes cluster domain prefix
146164
kubernetesClusterDomain: cluster.local
147165

@@ -182,3 +200,6 @@ tolerations: []
182200

183201
# -- ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity
184202
affinity: {}
203+
204+
vpa:
205+
updatePolicy: "Auto"

cmd/kubectl-etcd/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
corev1 "k8s.io/api/core/v1"
2121
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2222
"k8s.io/client-go/kubernetes"
23+
_ "k8s.io/client-go/plugin/pkg/client/auth" // Import all auth providers
2324
"k8s.io/client-go/tools/clientcmd"
2425
"k8s.io/client-go/tools/portforward"
2526
"k8s.io/client-go/transport/spdy"

internal/controller/etcdcluster_controller.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,18 @@ func (r *EtcdClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request)
111111
if err != nil {
112112
return ctrl.Result{}, err
113113
}
114+
defer func() {
115+
if clusterClient != nil {
116+
// nolint:errcheck
117+
clusterClient.Close()
118+
}
119+
for i := range singleClients {
120+
if singleClients[i] != nil {
121+
// nolint:errcheck
122+
singleClients[i].Close()
123+
}
124+
}
125+
}()
114126
state.endpointsFound = clusterClient != nil && singleClients != nil
115127

116128
if !state.endpointsFound {
@@ -286,6 +298,8 @@ func (r *EtcdClusterReconciler) ensureConditionalClusterObjects(
286298
}
287299

288300
// updateStatusOnErr wraps error and updates EtcdCluster status
301+
// TODO: refactor this so the linter doesn't complain
302+
// nolint:unparam
289303
func (r *EtcdClusterReconciler) updateStatusOnErr(ctx context.Context, cluster *etcdaenixiov1alpha1.EtcdCluster, err error) (ctrl.Result, error) {
290304
// The function 'updateStatusOnErr' will always return non-nil error. Hence, the ctrl.Result will always be ignored.
291305
// Therefore, the ctrl.Result returned by 'updateStatus' function can be discarded.
@@ -341,9 +355,8 @@ func (r *EtcdClusterReconciler) configureAuth(ctx context.Context, cluster *etcd
341355
return err
342356
}
343357

344-
defer func() {
345-
err = cli.Close()
346-
}()
358+
// nolint:errcheck
359+
defer cli.Close()
347360

348361
err = testMemberList(ctx, cli)
349362
if err != nil {

internal/controller/factory/etcd_client.go

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ package factory
22

33
import (
44
"context"
5+
"crypto/tls"
6+
"crypto/x509"
7+
"encoding/pem"
58
"fmt"
9+
"time"
610

711
"github.com/aenix-io/etcd-operator/api/v1alpha1"
812
clientv3 "go.etcd.io/etcd/client/v3"
@@ -29,6 +33,8 @@ func NewEtcdClientSet(ctx context.Context, cluster *v1alpha1.EtcdCluster, cli cl
2933
cfg.Endpoints = []string{ep}
3034
singleClients[i], err = clientv3.New(cfg)
3135
if err != nil {
36+
// nolint:errcheck
37+
clusterClient.Close()
3238
return nil, nil, fmt.Errorf("error building etcd single-endpoint client for endpoint %s: %w", ep, err)
3339
}
3440
}
@@ -56,8 +62,72 @@ func configFromCluster(ctx context.Context, cluster *v1alpha1.EtcdCluster, cli c
5662
}
5763
}
5864
for name := range names {
59-
urls = append(urls, fmt.Sprintf("%s:%s", name, "2379"))
65+
urls = append(urls, fmt.Sprintf("%s.%s.%s.svc:%s", name, ep.Name, ep.Namespace, "2379"))
6066
}
67+
etcdClient := clientv3.Config{Endpoints: urls, DialTimeout: 5 * time.Second}
6168

62-
return clientv3.Config{Endpoints: urls}, nil
69+
if s := cluster.Spec.Security; s != nil {
70+
if s.TLS.ClientSecret == "" {
71+
return clientv3.Config{}, fmt.Errorf("cannot configure client: security enabled, but client certificates not set")
72+
}
73+
clientSecret := &v1.Secret{}
74+
err := cli.Get(ctx, types.NamespacedName{Namespace: cluster.Namespace, Name: s.TLS.ClientSecret}, clientSecret)
75+
if err != nil {
76+
return clientv3.Config{}, fmt.Errorf("cannot configure client: failed to get client certificate: %w", err)
77+
}
78+
cert, err := parseTLSSecret(clientSecret)
79+
if err != nil {
80+
return clientv3.Config{}, fmt.Errorf("cannot configure client: failed to extract keypair from client secret: %w", err)
81+
}
82+
etcdClient.TLS = &tls.Config{}
83+
etcdClient.TLS.Certificates = []tls.Certificate{cert}
84+
etcdClient.TLS.GetClientCertificate = func(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) {
85+
return &etcdClient.TLS.Certificates[0], nil
86+
}
87+
caSecret := &v1.Secret{}
88+
err = cli.Get(ctx, types.NamespacedName{Namespace: cluster.Namespace, Name: s.TLS.ServerSecret}, caSecret)
89+
if err != nil {
90+
return clientv3.Config{}, fmt.Errorf("cannot configure client: failed to get server CA secret: %w", err)
91+
}
92+
ca, err := parseTLSSecretCA(caSecret)
93+
if err != nil {
94+
return clientv3.Config{}, fmt.Errorf("cannot configure client: failed to extract Root CA from server secret: %w", err)
95+
}
96+
pool := x509.NewCertPool()
97+
pool.AddCert(ca)
98+
etcdClient.TLS.RootCAs = pool
99+
}
100+
101+
return etcdClient, nil
102+
}
103+
104+
func parseTLSSecret(secret *v1.Secret) (cert tls.Certificate, err error) {
105+
certPem, ok := secret.Data["tls.crt"]
106+
if !ok {
107+
return tls.Certificate{}, fmt.Errorf("tls.crt not found in secret")
108+
}
109+
keyPem, ok := secret.Data["tls.key"]
110+
if !ok {
111+
return tls.Certificate{}, fmt.Errorf("tls.key not found in secret")
112+
}
113+
cert, err = tls.X509KeyPair(certPem, keyPem)
114+
if err != nil {
115+
err = fmt.Errorf("failed to load x509 key pair: %w", err)
116+
}
117+
return
118+
}
119+
120+
func parseTLSSecretCA(secret *v1.Secret) (ca *x509.Certificate, err error) {
121+
caPemBytes, ok := secret.Data["ca.crt"]
122+
if !ok {
123+
err = fmt.Errorf("secret does not contain ca.crt")
124+
return
125+
}
126+
block, _ := pem.Decode(caPemBytes)
127+
if block == nil {
128+
err = fmt.Errorf("failed to decode PEM bytes")
129+
return
130+
}
131+
ca, err = x509.ParseCertificate(block.Bytes)
132+
return
63133
}

0 commit comments

Comments
 (0)