Skip to content

Commit 40e4c61

Browse files
authored
Merge pull request #487 from runkecheng/feature_webhook
*: Init webhook.
2 parents 1110137 + 4729341 commit 40e4c61

25 files changed

+597
-39
lines changed

PROJECT

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ resources:
1313
kind: MysqlCluster
1414
path: github.com/radondb/radondb-mysql-kubernetes/api/v1alpha1
1515
version: v1alpha1
16+
webhooks:
17+
validation: true
18+
webhookVersion: v1
1619
- controller: true
1720
domain: radondb.com
1821
group: mysql

api/v1alpha1/mysqlcluster_webhook.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
Copyright 2021 RadonDB.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1alpha1
18+
19+
import (
20+
"fmt"
21+
22+
apierrors "k8s.io/apimachinery/pkg/api/errors"
23+
"k8s.io/apimachinery/pkg/runtime"
24+
"k8s.io/apimachinery/pkg/runtime/schema"
25+
ctrl "sigs.k8s.io/controller-runtime"
26+
logf "sigs.k8s.io/controller-runtime/pkg/log"
27+
"sigs.k8s.io/controller-runtime/pkg/webhook"
28+
)
29+
30+
// log is for logging in this package.
31+
var mysqlclusterlog = logf.Log.WithName("mysqlcluster-resource")
32+
33+
func (r *MysqlCluster) SetupWebhookWithManager(mgr ctrl.Manager) error {
34+
return ctrl.NewWebhookManagedBy(mgr).
35+
For(r).
36+
Complete()
37+
}
38+
39+
// TODO(user): EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
40+
41+
// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation.
42+
//+kubebuilder:webhook:path=/convert,mutating=false,failurePolicy=fail,sideEffects=None,groups=mysql.radondb.com,resources=mysqlclusters,verbs=create;update,versions=v1alpha1,name=vmysqlcluster.kb.io,admissionReviewVersions=v1
43+
44+
var _ webhook.Validator = &MysqlCluster{}
45+
46+
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
47+
func (r *MysqlCluster) ValidateCreate() error {
48+
mysqlclusterlog.Info("validate create", "name", r.Name)
49+
50+
// TODO(user): fill in your validation logic upon object creation.
51+
return nil
52+
}
53+
54+
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
55+
func (r *MysqlCluster) ValidateUpdate(old runtime.Object) error {
56+
mysqlclusterlog.Info("validate update", "name", r.Name)
57+
58+
oldCluster, ok := old.(*MysqlCluster)
59+
if !ok {
60+
return apierrors.NewBadRequest(fmt.Sprintf("expected an MysqlCluster but got a %T", old))
61+
}
62+
if err := r.validateVolumeSize(*oldCluster); err != nil {
63+
return err
64+
}
65+
66+
return nil
67+
}
68+
69+
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
70+
func (r *MysqlCluster) ValidateDelete() error {
71+
mysqlclusterlog.Info("validate delete", "name", r.Name)
72+
73+
// TODO(user): fill in your validation logic upon object deletion.
74+
return nil
75+
}
76+
77+
func (r *MysqlCluster) validateVolumeSize(old MysqlCluster) error {
78+
if r.Spec.Persistence.Size < old.Spec.Persistence.Size {
79+
return apierrors.NewForbidden(schema.GroupResource{}, "", fmt.Errorf("volesize can not be decreased"))
80+
}
81+
return nil
82+
}

api/v1alpha1/webhook_suite_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
Copyright 2021 RadonDB.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1alpha1
18+
19+
import (
20+
"context"
21+
"crypto/tls"
22+
"fmt"
23+
"net"
24+
"path/filepath"
25+
"testing"
26+
"time"
27+
28+
. "github.com/onsi/ginkgo/v2"
29+
. "github.com/onsi/gomega"
30+
31+
admissionv1beta1 "k8s.io/api/admission/v1beta1"
32+
//+kubebuilder:scaffold:imports
33+
"k8s.io/apimachinery/pkg/runtime"
34+
ctrl "sigs.k8s.io/controller-runtime"
35+
"sigs.k8s.io/controller-runtime/pkg/client"
36+
"sigs.k8s.io/controller-runtime/pkg/envtest"
37+
logf "sigs.k8s.io/controller-runtime/pkg/log"
38+
"sigs.k8s.io/controller-runtime/pkg/log/zap"
39+
)
40+
41+
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
42+
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
43+
44+
var k8sClient client.Client
45+
var testEnv *envtest.Environment
46+
var ctx context.Context
47+
var cancel context.CancelFunc
48+
49+
func TestAPIs(t *testing.T) {
50+
RegisterFailHandler(Fail)
51+
52+
RunSpecs(t, "Webhook Suite")
53+
}
54+
55+
var _ = BeforeSuite(func() {
56+
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
57+
58+
ctx, cancel = context.WithCancel(context.TODO())
59+
60+
By("bootstrapping test environment")
61+
testEnv = &envtest.Environment{
62+
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
63+
ErrorIfCRDPathMissing: false,
64+
WebhookInstallOptions: envtest.WebhookInstallOptions{
65+
Paths: []string{filepath.Join("..", "..", "config", "webhook")},
66+
},
67+
}
68+
69+
cfg, err := testEnv.Start()
70+
Expect(err).NotTo(HaveOccurred())
71+
Expect(cfg).NotTo(BeNil())
72+
73+
scheme := runtime.NewScheme()
74+
err = AddToScheme(scheme)
75+
Expect(err).NotTo(HaveOccurred())
76+
77+
err = admissionv1beta1.AddToScheme(scheme)
78+
Expect(err).NotTo(HaveOccurred())
79+
80+
//+kubebuilder:scaffold:scheme
81+
82+
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme})
83+
Expect(err).NotTo(HaveOccurred())
84+
Expect(k8sClient).NotTo(BeNil())
85+
86+
// start webhook server using Manager
87+
webhookInstallOptions := &testEnv.WebhookInstallOptions
88+
mgr, err := ctrl.NewManager(cfg, ctrl.Options{
89+
Scheme: scheme,
90+
Host: webhookInstallOptions.LocalServingHost,
91+
Port: webhookInstallOptions.LocalServingPort,
92+
CertDir: webhookInstallOptions.LocalServingCertDir,
93+
LeaderElection: false,
94+
MetricsBindAddress: "0",
95+
})
96+
Expect(err).NotTo(HaveOccurred())
97+
98+
err = (&MysqlCluster{}).SetupWebhookWithManager(mgr)
99+
Expect(err).NotTo(HaveOccurred())
100+
101+
//+kubebuilder:scaffold:webhook
102+
103+
go func() {
104+
defer GinkgoRecover()
105+
err = mgr.Start(ctx)
106+
Expect(err).NotTo(HaveOccurred())
107+
}()
108+
109+
// wait for the webhook server to get ready
110+
dialer := &net.Dialer{Timeout: time.Second}
111+
addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort)
112+
Eventually(func() error {
113+
conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true})
114+
if err != nil {
115+
return err
116+
}
117+
conn.Close()
118+
return nil
119+
}).Should(Succeed())
120+
121+
})
122+
123+
var _ = AfterSuite(func() {
124+
cancel()
125+
By("tearing down the test environment")
126+
err := testEnv.Stop()
127+
Expect(err).NotTo(HaveOccurred())
128+
})

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

charts/mysql-operator/templates/_helpers.tpl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,22 @@ If release name contains chart name it will be used as a full name.
2323
{{- end }}
2424
{{- end }}
2525

26+
{{- define "validating-webhook-configuration.name" -}}
27+
{{ default "radondb-mysql-validation" }}
28+
{{- end }}
29+
30+
{{- define "certificate.name" -}}
31+
{{ default "radondb-mysql-certificate" }}
32+
{{- end }}
33+
34+
{{- define "issuer.name" -}}
35+
{{ default "radondb-mysql-issuer" }}
36+
{{- end }}
37+
38+
{{- define "webhook.name" -}}
39+
{{ default "radondb-mysql-webhook" }}
40+
{{- end }}
41+
2642
{{/*
2743
Create chart name and version as used by the chart label.
2844
*/}}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{{- define "webhook.caBundleCertPEM" -}}
2+
{{- if .Values.webhook.caBundlePEM -}}
3+
{{- trim .Values.webhook.caBundlePEM -}}
4+
{{- else -}}
5+
{{- /* Generate ca with CN "radondb-ca" and 5 years validity duration if not exists in the current scope.*/ -}}
6+
{{- $caKeypair := .selfSignedCAKeypair | default (genCA "radondb-ca" 1825) -}}
7+
{{- $_ := set . "selfSignedCAKeypair" $caKeypair -}}
8+
{{- $caKeypair.Cert -}}
9+
{{- end -}}
10+
{{- end -}}
11+
12+
{{- define "webhook.certPEM" -}}
13+
{{- if .Values.webhook.crtPEM -}}
14+
{{- trim .Values.webhook.crtPEM -}}
15+
{{- else -}}
16+
{{- $webhookDomain := printf "%s.%s.svc" (include "webhook.name" .) .Release.Namespace -}}
17+
{{- $webhookDomainLocal := printf "%s.%s.svc.cluster.local" (include "webhook.name" .) .Release.Namespace -}}
18+
{{- $webhookCA := required "self-signed CA keypair is requried" .selfSignedCAKeypair -}}
19+
{{- /* genSignedCert <CN> <IP> <DNS> <Validity duration> <CA> */ -}}
20+
{{- $webhookServerTLSKeypair := .webhookTLSKeypair | default (genSignedCert "radondb-mysql" nil (list $webhookDomain $webhookDomainLocal) 1825 $webhookCA) -}}
21+
{{- $_ := set . "webhookTLSKeypair" $webhookServerTLSKeypair -}}
22+
{{- $webhookServerTLSKeypair.Cert -}}
23+
{{- end -}}
24+
{{- end -}}
25+
26+
{{- define "webhook.keyPEM" -}}
27+
{{- if .Values.webhook.keyPEM -}}
28+
{{ trim .Values.webhook.keyPEM }}
29+
{{- else -}}
30+
{{- $webhookDomain := printf "%s.%s.svc" (include "webhook.name" .) .Release.Namespace -}}
31+
{{- $webhookDomainLocal := printf "%s.%s.svc.cluster.local" (include "webhook.name" .) .Release.Namespace -}}
32+
{{- $webhookCA := required "self-signed CA keypair is requried" .selfSignedCAKeypair -}}
33+
{{- /* genSignedCert <CN> <IP> <DNS> <Validity duration> <CA> */ -}}
34+
{{- $webhookServerTLSKeypair := .webhookTLSKeypair | default (genSignedCert "radondb-mysql" nil (list $webhookDomain $webhookDomainLocal) 1825 $webhookCA) -}}
35+
{{- $_ := set . "webhookTLSKeypair" $webhookServerTLSKeypair -}}
36+
{{- $webhookServerTLSKeypair.Key -}}
37+
{{- end -}}
38+
{{- end -}}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{{- if .Values.webhook.certManager.enabled }}
2+
apiVersion: cert-manager.io/v1
3+
kind: Issuer
4+
metadata:
5+
name: {{ template "issuer.name" . }}
6+
namespace: {{ .Release.Namespace }}
7+
spec:
8+
selfSigned: {}
9+
---
10+
apiVersion: cert-manager.io/v1
11+
kind: Certificate
12+
metadata:
13+
name: {{ template "certificate.name" . }}
14+
namespace: {{ .Release.Namespace }}
15+
spec:
16+
dnsNames:
17+
- {{ printf "%s.%s.svc" (include "webhook.name" .) .Release.Namespace }}
18+
- {{ printf "%s.%s.svc.cluster.local" (include "webhook.name" .) .Release.Namespace }}
19+
issuerRef:
20+
kind: Issuer
21+
name: {{ template "issuer.name" . }}
22+
secretName: "{{ template "webhook.name" . }}-certs"
23+
{{- end }}

charts/mysql-operator/templates/deployment.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ spec:
2121
spec:
2222
securityContext:
2323
runAsNonRoot: true
24+
volumes:
25+
- name: cert
26+
secret:
27+
defaultMode: 420
28+
secretName: "{{ template "webhook.name" . }}-certs"
2429
containers:
2530
{{- if .Values.rbacProxy.create }}
2631
- name: kube-rbac-proxy
@@ -39,6 +44,14 @@ spec:
3944
name: https
4045
{{- end }}
4146
- name: manager
47+
ports:
48+
- containerPort: 9443
49+
name: webhook-server
50+
protocol: TCP
51+
volumeMounts:
52+
- name: cert
53+
mountPath: /tmp/k8s-webhook-server/serving-certs/
54+
readOnly: true
4255
command:
4356
- /manager
4457
args:
@@ -54,6 +67,8 @@ spec:
5467
env:
5568
- name: IMAGE_PREFIX
5669
value: {{ .Values.imagePrefix }}
70+
- name: ENABLED_WEBHOOKS
71+
value: {{ .Values.manager.enabledWebhooks | quote }}
5772
securityContext:
5873
allowPrivilegeEscalation: false
5974
livenessProbe:

0 commit comments

Comments
 (0)