Skip to content

Commit 9ad300a

Browse files
committed
Conversion webhook review
This patch makes several corrections to the conversion webhook: - fixes several occurrences of the old validation webhook codebase - simplify the webhook command and drops the direct cobra dependency - align the README with what the code really does - removes the unneeded structure in the unit tests - removes unneeded code and the unneeded dependencies - drop support for converting VolumeSnapshots and VolumeSnapshotClasses as they are structurally identical Signed-off-by: Leonardo Cecchi <[email protected]>
1 parent d1f9f39 commit 9ad300a

32 files changed

+122
-1338
lines changed

client/config/crd/groupsnapshot.storage.k8s.io_volumegroupsnapshotclasses.yaml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,6 @@ spec:
1717
- vgsclasses
1818
singular: volumegroupsnapshotclass
1919
scope: Cluster
20-
conversion:
21-
strategy: Webhook
22-
webhook:
23-
conversionReviewVersions: ["v1","v1beta1"]
24-
clientConfig:
25-
service:
26-
namespace: default
27-
name: snapshot-conversion-webhook-service
28-
path: /convert
2920
versions:
3021
- additionalPrinterColumns:
3122
- jsonPath: .driver

client/config/crd/groupsnapshot.storage.k8s.io_volumegroupsnapshots.yaml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,6 @@ spec:
1616
- vgs
1717
singular: volumegroupsnapshot
1818
scope: Namespaced
19-
conversion:
20-
strategy: Webhook
21-
webhook:
22-
conversionReviewVersions: ["v1","v1beta1"]
23-
clientConfig:
24-
service:
25-
namespace: default
26-
name: snapshot-conversion-webhook-service
27-
path: /convert
2819
versions:
2920
- additionalPrinterColumns:
3021
- description: Indicates if all the individual snapshots in the group are ready

cmd/snapshot-conversion-webhook/main.go

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,65 @@ limitations under the License.
1717
package main
1818

1919
import (
20+
"context"
21+
"crypto/tls"
2022
"flag"
2123

22-
webhook "github.com/kubernetes-csi/external-snapshotter/v8/pkg/webhook"
24+
"github.com/kubernetes-csi/csi-lib-utils/standardflags"
25+
"github.com/kubernetes-csi/external-snapshotter/v8/pkg/webhook"
26+
"k8s.io/component-base/logs"
27+
logsapi "k8s.io/component-base/logs/api/v1"
2328
"k8s.io/klog/v2"
2429
)
2530

31+
var (
32+
certFile = flag.String(
33+
"tls-cert-file",
34+
"",
35+
"File containing the x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert). Required.",
36+
)
37+
keyFile = flag.String(
38+
"tls-private-key-file",
39+
"",
40+
"File containing the x509 private key matching --tls-cert-file. Required.",
41+
)
42+
port = flag.Int(
43+
"port",
44+
443,
45+
"Secure port that the webhook listens on",
46+
)
47+
)
48+
2649
func main() {
27-
rootCmd := webhook.CmdWebhook
50+
c := logsapi.NewLoggingConfiguration()
51+
logsapi.AddGoFlags(c, flag.CommandLine)
52+
logs.InitLogs()
53+
standardflags.AddAutomaxprocs(klog.Infof)
54+
flag.Parse()
55+
56+
klog.Info("Starting conversion webhook server")
57+
58+
if certFile == nil || *certFile == "" {
59+
klog.Fatal("--tls-cert-file must be specified")
60+
}
61+
if keyFile == nil || *keyFile == "" {
62+
klog.Fatal("--tls-private-key-file must be specified")
63+
}
64+
65+
// Create new cert watcher
66+
ctx, cancel := context.WithCancel(context.Background())
67+
defer cancel() // stops certwatcher
68+
69+
cw, err := webhook.NewCertWatcher(*certFile, *keyFile)
70+
if err != nil {
71+
klog.Fatalf("failed to initialize new cert watcher: %v", err)
72+
}
73+
tlsConfig := &tls.Config{
74+
GetCertificate: cw.GetCertificate,
75+
}
2876

29-
loggingFlags := &flag.FlagSet{}
30-
klog.InitFlags(loggingFlags)
31-
rootCmd.PersistentFlags().AddGoFlagSet(loggingFlags)
32-
rootCmd.Execute()
77+
// Start the webhook server
78+
if err := webhook.StartServer(ctx, tlsConfig, cw, *port); err != nil {
79+
klog.Fatalf("server stopped: %v", err)
80+
}
3381
}

deploy/kubernetes/webhook-example/README.md

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
# Validating Webhook
1+
# Conversion Webhook
22

3-
The snapshot validating webhook is an HTTP callback which responds to [admission requests](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/). It is part of a larger [plan](https://github.com/kubernetes/enhancements/tree/master/keps/sig-storage/1900-volume-snapshot-validation-webhook#proposal) to tighten validation for volume snapshot objects. This webhook introduces the [ratcheting validation](https://github.com/kubernetes/enhancements/tree/master/keps/sig-storage/1900-volume-snapshot-validation-webhook#backwards-compatibility) mechanism targeting the tighter validation. The cluster admin or Kubernetes distribution admin should install the webhook alongside the snapshot controllers and CRDs.
3+
The snapshot conversion webhook is an HTTP callback which responds to
4+
[conversion requests](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#webhook-conversion),
5+
allowing the API server to convert between the VolumeGroupSnapshotContent v1beta1 API to and from the v1beta2 API.
6+
7+
The cluster admin or Kubernetes distribution admin should install the webhook
8+
alongside the snapshot controllers and CRDs.
49

510
## How to build the webhook
611

@@ -18,11 +23,25 @@ docker build -t snapshot-conversion-webhook:latest -f ./cmd/snapshot-conversion-
1823

1924
## How to deploy the webhook
2025

21-
The webhook server is provided as an image which can be built from this repository. It can be deployed anywhere, as long as the api server is able to reach it over HTTPS. It is recommended to deploy the webhook server in the cluster as snapshotting is latency sensitive. A `ValidatingWebhookConfiguration` object is needed to configure the api server to contact the webhook server. Please see the [documentation](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/) for more details. The webhook server code is adapted from the [webhook server](https://github.com/kubernetes/kubernetes/tree/v1.18.6/test/images/agnhost/webhook) used in the kubernetes/kubernetes end to end testing code.
26+
The webhook server is provided as an image which can be built from this repository. It can be deployed anywhere,
27+
as long as the api server is able to reach it over HTTPS. It is recommended to deploy the webhook server in the
28+
cluster as snapshotting is latency sensitive.
29+
30+
The CRD may need to be patched to allow safe TLS communication to the webhook server.
31+
Please see the [documentation](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#webhook-conversion)
32+
for more details.
33+
34+
The webhook server code is adapted from the [webhook server](https://github.com/kubernetes/kubernetes/blob/v1.25.3/test/images/agnhost/crd-conversion-webhook/main.go)
35+
used in the kubernetes/kubernetes e2e testing code.
2236

2337
### Example in-cluster deployment using Kubernetes Secrets
2438

25-
Please note this is not considered to be a production ready method to deploy the certificates and is only provided for demo purposes. This is only one of many ways to deploy the certificates, it is your responsibility to ensure the security of your cluster. TLS certificates and private keys should be handled with care and you may not want to keep them in plain Kubernetes secrets.
39+
Please note this is not considered to be a production ready method to deploy the certificates and is only provided
40+
for demo purposes. This is only one of many ways to deploy the certificates, it is your responsibility to
41+
ensure the security of your cluster.
42+
43+
TLS certificates and private keys should be handled with care and you may not want to keep them in plain
44+
Kubernetes secrets.
2645

2746
This method was heavily adapted from [banzai cloud](https://banzaicloud.com/blog/k8s-admission-webhooks/).
2847

@@ -38,7 +57,7 @@ These commands should be run from the top level directory.
3857
./deploy/kubernetes/webhook-example/create-cert.sh --service snapshot-conversion-webhook-service --secret snapshot-conversion-webhook-secret --namespace default # Make sure to use a different namespace
3958
```
4059

41-
2. Patch the VolumeGroupSnapshot, VolumeGroupSnapshotContent and VolumeGroupSnapshotClass CRDs filling in the CA bundle field.
60+
2. Patch the VolumeGroupSnapshotContent CRD filling in the CA bundle field.
4261

4362
```bash
4463
./deploy/kubernetes/webhook-example/patch-ca-bundle.sh
@@ -56,20 +75,18 @@ Once all the pods from the deployment are up and running, you should be ready to
5675

5776
#### Verify the webhook works
5877

59-
Try to query the API server for a VolumeGroupSnapshot object in the version `v1beta1`.
78+
Try to query the API server for a VolumeGroupSnapshotContent object in the version `v1beta1`.
6079

6180
```bash
62-
kubectl get volumegroupsnapshotclass.v1beta1.groupsnapshot.storage.k8s.io
63-
6481
kubectl get volumegroupsnapshotcontent.v1beta1.groupsnapshot.storage.k8s.io
65-
66-
kubectl get volumegroupsnapshot.v1beta1.groupsnapshot.storage.k8s.io
6782
```
6883

6984
### Other methods to deploy the webhook server
7085

71-
Look into [cert-manager](https://cert-manager.io/) to handle the certificates, and this kube-builder [tutorial](https://book.kubebuilder.io/cronjob-tutorial/cert-manager.html) on how to deploy a webhook.
86+
Look into [cert-manager](https://cert-manager.io/) to handle the certificates,
87+
and this kube-builder [tutorial](https://book.kubebuilder.io/cronjob-tutorial/cert-manager.html) on how to deploy a webhook.
7288

7389
#### Important
7490

75-
Please see the deployment [yaml](./webhook.yaml) for the arguments expected by the webhook server. The snapshot validation webhook is served at the path `/volumesnapshot`.
91+
Please see the deployment [yaml](./webhook.yaml) for the arguments expected by the
92+
webhook server. The conversion webhook is served at the path `/convert`.

deploy/kubernetes/webhook-example/webhook.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ spec:
1818
spec:
1919
containers:
2020
- name: snapshot-conversion-webhook
21-
image: registry.k8s.io/sig-storage/snapshot-conversion-webhook:v8.0.1 # change the image if you wish to use your own custom validation server image
21+
image: registry.k8s.io/sig-storage/snapshot-conversion-webhook:v8.0.1 # change the image if you wish to use your own custom conversion server image
2222
imagePullPolicy: IfNotPresent
2323
args:
2424
- '--tls-cert-file=/etc/snapshot-conversion-webhook/certs/tls.crt'

go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,13 @@ require (
99
github.com/evanphx/json-patch v5.9.0+incompatible
1010
github.com/fsnotify/fsnotify v1.7.0
1111
github.com/golang/mock v1.6.0
12-
github.com/google/gofuzz v1.2.0
1312
github.com/kubernetes-csi/csi-lib-utils v0.22.0
1413
github.com/kubernetes-csi/csi-test/v5 v5.3.1
1514
github.com/kubernetes-csi/external-snapshotter/client/v8 v8.2.0
1615
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
1716
github.com/prometheus/client_golang v1.22.0
1817
github.com/prometheus/client_model v0.6.1
1918
github.com/prometheus/common v0.62.0
20-
github.com/spf13/cobra v1.8.1
2119
google.golang.org/grpc v1.69.0
2220
google.golang.org/protobuf v1.36.5
2321
k8s.io/api v0.33.2
@@ -57,6 +55,7 @@ require (
5755
github.com/modern-go/reflect2 v1.0.2 // indirect
5856
github.com/pkg/errors v0.9.1 // indirect
5957
github.com/prometheus/procfs v0.15.1 // indirect
58+
github.com/spf13/cobra v1.8.1 // indirect
6059
github.com/spf13/pflag v1.0.5 // indirect
6160
github.com/x448/float16 v0.8.4 // indirect
6261
go.opentelemetry.io/auto/sdk v1.1.0 // indirect

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
4747
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
4848
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
4949
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
50-
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
51-
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
5250
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
5351
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
5452
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=

pkg/webhook/convert.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,8 @@ func convertGroupSnapshotCRD(obj *unstructured.Unstructured, toVersion string) (
4848
switch toVersion {
4949
case "groupsnapshot.storage.k8s.io/v1beta2":
5050
switch kind {
51-
case "VolumeGroupSnapshot":
52-
case "VolumeGroupSnapshotClass":
5351
case "VolumeGroupSnapshotContent":
54-
if err := convertVolumeGroupSnapshotFromV1beta1ToV1beta2(convertedObject); err != nil {
52+
if err := convertVolumeGroupSnapshotContentFromV1beta1ToV1beta2(convertedObject); err != nil {
5553
return nil, statusErrorWithMessage("%s", err.Error())
5654
}
5755
default:
@@ -64,10 +62,8 @@ func convertGroupSnapshotCRD(obj *unstructured.Unstructured, toVersion string) (
6462
switch toVersion {
6563
case "groupsnapshot.storage.k8s.io/v1beta1":
6664
switch kind {
67-
case "VolumeGroupSnapshot":
68-
case "VolumeGroupSnapshotClass":
6965
case "VolumeGroupSnapshotContent":
70-
if err := convertVolumeGroupSnapshotFromV1beta2ToV1beta1(convertedObject); err != nil {
66+
if err := convertVolumeGroupSnapshotContentFromV1beta2ToV1beta1(convertedObject); err != nil {
7167
return nil, statusErrorWithMessage("%s", err.Error())
7268
}
7369
default:
@@ -83,7 +79,7 @@ func convertGroupSnapshotCRD(obj *unstructured.Unstructured, toVersion string) (
8379
return convertedObject, statusSucceed()
8480
}
8581

86-
func convertVolumeGroupSnapshotFromV1beta1ToV1beta2(obj *unstructured.Unstructured) error {
82+
func convertVolumeGroupSnapshotContentFromV1beta1ToV1beta2(obj *unstructured.Unstructured) error {
8783
annotations := obj.GetAnnotations()
8884
if value, ok := annotations[volumeSnapshotInfoAnnotationName]; ok {
8985
// We use the annotation to fill the missing fields into the status
@@ -129,7 +125,7 @@ func convertVolumeGroupSnapshotFromV1beta1ToV1beta2(obj *unstructured.Unstructur
129125
return nil
130126
}
131127

132-
func convertVolumeGroupSnapshotFromV1beta2ToV1beta1(obj *unstructured.Unstructured) error {
128+
func convertVolumeGroupSnapshotContentFromV1beta2ToV1beta1(obj *unstructured.Unstructured) error {
133129
volumeSnapshotInfoList, found, err := unstructured.NestedSlice(obj.Object, "status", "volumeSnapshotInfoList")
134130
if err != nil {
135131
return fmt.Errorf("unable to traverse for .status.volumeSnapshotInfoList: %w", err)

pkg/webhook/convert_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func TestFromBeta1ToBeta2(t *testing.T) {
3939
from := fromFile(t, beta1FileName)
4040
to := fromFile(t, beta2FileName)
4141

42-
err := convertVolumeGroupSnapshotFromV1beta1ToV1beta2(from)
42+
err := convertVolumeGroupSnapshotContentFromV1beta1ToV1beta2(from)
4343
if err != nil {
4444
t.Fatalf("conversion failed: %v", err.Error())
4545
}
@@ -65,7 +65,7 @@ func TestFromBeta2ToBeta1(t *testing.T) {
6565
from := fromFile(t, beta2FileName)
6666
to := fromFile(t, beta1FileName)
6767

68-
err := convertVolumeGroupSnapshotFromV1beta2ToV1beta1(from)
68+
err := convertVolumeGroupSnapshotContentFromV1beta2ToV1beta1(from)
6969
if err != nil {
7070
t.Fatalf("conversion failed: %v", err.Error())
7171
}

pkg/webhook/fuzzer.go

Lines changed: 0 additions & 43 deletions
This file was deleted.

0 commit comments

Comments
 (0)