From b67155ee7ca69916d545056555513ecf08f2ed5d Mon Sep 17 00:00:00 2001 From: Alexander Yastrebov Date: Tue, 26 Mar 2024 14:47:45 +0100 Subject: [PATCH] crd: add hosts max length controller-gen does not support validating internal list items on list types, see https://github.com/kubernetes-sigs/controller-tools/issues/342 To add host pattern we used perl hack that is hard to extend to multiple validations. Also #16 added optional tls spec that contains hosts field for which perl hack worked by accident, see https://github.com/szuecs/routegroup-client/pull/16#discussion_r1443561139 This change replaces perl hack for a go hack and adds max length constraint. Signed-off-by: Alexander Yastrebov --- Makefile | 4 +- go.mod | 2 +- hack/crd/add_hosts_validation.go | 93 ++++++++++++++++++++++++++++++++ zalando.org_routegroups.yaml | 7 +-- 4 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 hack/crd/add_hosts_validation.go diff --git a/Makefile b/Makefile index 425f448..8d0d811 100644 --- a/Makefile +++ b/Makefile @@ -32,9 +32,9 @@ $(GENERATED): $(CRD_TYPE_SOURCE) $(GENERATED_CRD): go.mod $(GENERATED) go run sigs.k8s.io/controller-tools/cmd/controller-gen@v0.14.0 crd:crdVersions=v1 paths=./apis/... output:crd:dir=. - # workaround to add pattern to array items. Not supported by controller-gen + # workaround to add validation to array items. Not supported by controller-gen # ref: https://github.com/kubernetes-sigs/controller-tools/issues/342 - perl -i -p0e 's|(\s*)(hosts:.*?items:)|$$1$$2$$1 pattern: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*\$$"|sg' $(GENERATED_CRD) + go run hack/crd/add_hosts_validation.go $(GENERATED_CRD) build.local: $(LOCAL_BINARIES) $(GENERATED_CRD) build.linux: $(LINUX_BINARIES) $(GENERATED_CRD) diff --git a/go.mod b/go.mod index 2ea14bb..f8aac71 100644 --- a/go.mod +++ b/go.mod @@ -8,5 +8,5 @@ require ( k8s.io/apimachinery v0.25.16 k8s.io/client-go v0.25.16 k8s.io/code-generator v0.25.16 - sigs.k8s.io/yaml v1.3.0 // indirect + sigs.k8s.io/yaml v1.3.0 ) diff --git a/hack/crd/add_hosts_validation.go b/hack/crd/add_hosts_validation.go new file mode 100644 index 0000000..b4c6011 --- /dev/null +++ b/hack/crd/add_hosts_validation.go @@ -0,0 +1,93 @@ +// This program add host validation to CRD yaml. +// +// # Why +// +// controller-gen does not support validating internal list items on list types, +// see https://github.com/kubernetes-sigs/controller-tools/issues/342 +package main + +import ( + "log" + "os" + + "sigs.k8s.io/yaml" +) + +const ( + hostPattern = `^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$` + hostMaxLength = 255 // https://datatracker.ietf.org/doc/html/rfc1035#section-2.3.4 +) + +func must(err error) { + if err != nil { + log.Fatal(err) + } +} + +func mustGet(v []byte, err error) []byte { + must(err) + return v +} + +type setter struct { + o interface{} +} + +func (s *setter) field(name string) *setter { + return &setter{s.o.(map[string]interface{})[name]} +} + +func (s *setter) item(i int) *setter { + return &setter{s.o.([]interface{})[i]} +} + +func (s *setter) setField(name string, value interface{}) *setter { + s.o.(map[string]interface{})[name] = value + return s +} + +func main() { + if len(os.Args) != 2 { + log.Fatalf("CRD filename required") + } + crdFilename := os.Args[1] + + yamlBytes := mustGet(os.ReadFile(crdFilename)) + + o := make(map[string]interface{}) + must(yaml.Unmarshal(yamlBytes, &o)) + + s := &setter{o} + + s.field("spec"). + field("versions"). + item(0). + field("schema"). + field("openAPIV3Schema"). + field("properties"). + field("spec"). + field("properties"). + field("hosts"). + field("items"). + setField("pattern", hostPattern). + setField("maxLength", hostMaxLength) + + s.field("spec"). + field("versions"). + item(0). + field("schema"). + field("openAPIV3Schema"). + field("properties"). + field("spec"). + field("properties"). + field("tls"). + field("items"). + field("properties"). + field("hosts"). + field("items"). + setField("pattern", hostPattern). + setField("maxLength", hostMaxLength) + + outYaml := mustGet(yaml.Marshal(o)) + must(os.WriteFile(crdFilename, outYaml, 0664)) +} diff --git a/zalando.org_routegroups.yaml b/zalando.org_routegroups.yaml index 04236eb..f977a68 100644 --- a/zalando.org_routegroups.yaml +++ b/zalando.org_routegroups.yaml @@ -1,4 +1,3 @@ ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -132,7 +131,8 @@ spec: hosts: description: List of hostnames for the RouteGroup items: - pattern: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$" + maxLength: 255 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string minItems: 1 type: array @@ -216,7 +216,8 @@ spec: The values in this list must match the host name(s) used for the RouteGroup in order to terminate TLS for the host(s). items: - pattern: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$" + maxLength: 255 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string minItems: 1 type: array