Skip to content

Commit 36ad807

Browse files
committed
config: Init webhook. #486
kubebuilder create webhook --group mysql --version v1alpha1 --kind mysqlcluster --defaulting --programmatic-validation
1 parent 62535bf commit 36ad807

19 files changed

+420
-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.

cmd/manager/main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ func main() {
117117
os.Exit(1)
118118
}
119119
//+kubebuilder:scaffold:builder
120+
if os.Getenv("ENABLE_WEBHOOKS") != "false" {
121+
if err = (&mysqlv1alpha1.MysqlCluster{}).SetupWebhookWithManager(mgr); err != nil {
122+
setupLog.Error(err, "unable to create webhook", "webhook", "MysqlCluster")
123+
os.Exit(1)
124+
}
125+
}
120126

121127
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
122128
setupLog.Error(err, "unable to set up health check")

config/certmanager/certificate.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# The following manifests contain a self-signed issuer CR and a certificate CR.
2+
# More document can be found at https://docs.cert-manager.io
3+
# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes.
4+
apiVersion: cert-manager.io/v1
5+
kind: Issuer
6+
metadata:
7+
name: selfsigned-issuer
8+
namespace: system
9+
spec:
10+
selfSigned: {}
11+
---
12+
apiVersion: cert-manager.io/v1
13+
kind: Certificate
14+
metadata:
15+
name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml
16+
namespace: system
17+
spec:
18+
# $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize
19+
dnsNames:
20+
- $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc
21+
- $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local
22+
issuerRef:
23+
kind: Issuer
24+
name: selfsigned-issuer
25+
secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize

config/certmanager/kustomization.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
resources:
2+
- certificate.yaml
3+
4+
configurations:
5+
- kustomizeconfig.yaml
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# This configuration is for teaching kustomize how to update name ref and var substitution
2+
nameReference:
3+
- kind: Issuer
4+
group: cert-manager.io
5+
fieldSpecs:
6+
- kind: Certificate
7+
group: cert-manager.io
8+
path: spec/issuerRef/name
9+
10+
varReference:
11+
- kind: Certificate
12+
group: cert-manager.io
13+
path: spec/commonName
14+
- kind: Certificate
15+
group: cert-manager.io
16+
path: spec/dnsNames

config/crd/kustomization.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ resources:
1010
patchesStrategicMerge:
1111
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
1212
# patches here are for enabling the conversion webhook for each CRD
13-
#- patches/webhook_in_clusters.yaml
13+
- patches/webhook_in_mysqlclusters.yaml
1414
#+kubebuilder:scaffold:crdkustomizewebhookpatch
1515

1616
# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
1717
# patches here are for enabling the CA injection for each CRD
18-
#- patches/cainjection_in_clusters.yaml
18+
- patches/cainjection_in_mysqlclusters.yaml
1919
#+kubebuilder:scaffold:crdkustomizecainjectionpatch
2020

2121
# the following config is for teaching kustomize how to do kustomization for CRDs.

config/crd/patches/webhook_in_mysqlclusters.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ spec:
1111
service:
1212
namespace: system
1313
name: webhook-service
14-
path: /convert
14+
path: /validate-mysql-radondb-com-v1alpha1-mysqlcluster
1515
conversionReviewVersions:
1616
- v1

0 commit comments

Comments
 (0)