diff --git a/apis/v1beta1/gateway_types.go b/apis/v1beta1/gateway_types.go index b7a79c0000..0afe4d4240 100644 --- a/apis/v1beta1/gateway_types.go +++ b/apis/v1beta1/gateway_types.go @@ -149,6 +149,7 @@ type GatewaySpec struct { // Support: Extended // // +optional + // // +kubebuilder:validation:MaxItems=16 // +kubebuilder:validation:XValidation:message="IPAddress values must be unique",rule="self.all(a1, a1.type == 'IPAddress' ? self.exists_one(a2, a2.type == a1.type && a2.value == a1.value) : true )" // +kubebuilder:validation:XValidation:message="Hostname values must be unique",rule="self.all(a1, a1.type == 'Hostname' ? self.exists_one(a2, a2.type == a1.type && a2.value == a1.value) : true )" @@ -486,6 +487,8 @@ type GatewayAddress struct { } // GatewayStatusAddress describes an address that is bound to a Gateway. +// +// +kubebuilder:validation:XValidation:message="Hostname value must only contain valid characters (matching ^(\\*\\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$)",rule="self.type == 'Hostname' ? self.value.matches('^(\\\\*\\\\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'): true" type GatewayStatusAddress struct { // Type of the address. // @@ -511,6 +514,7 @@ type GatewayStatus struct { // assigns an address from a reserved pool. // // +optional + // // +kubebuilder:validation:MaxItems=16 Addresses []GatewayStatusAddress `json:"addresses,omitempty"` diff --git a/config/crd/experimental/gateway.networking.k8s.io_gateways.yaml b/config/crd/experimental/gateway.networking.k8s.io_gateways.yaml index 08b0f02be3..d4cc3b7f48 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_gateways.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_gateways.yaml @@ -73,10 +73,24 @@ spec: manner, assigning an appropriate set of Addresses. \n The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. - \n Support: Extended" + \n Support: Extended \n " items: description: GatewayAddress describes an address that can be bound to a Gateway. + oneOf: + - properties: + type: + enum: + - IPAddress + value: + anyOf: + - format: ipv4 + - format: ipv6 + - properties: + type: + not: + enum: + - IPAddress properties: type: default: IPAddress @@ -500,13 +514,27 @@ spec: description: Status defines the current state of Gateway. properties: addresses: - description: Addresses lists the IP addresses that have actually been - bound to the Gateway. These addresses may differ from the addresses + description: "Addresses lists the IP addresses that have actually + been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address - from a reserved pool. + from a reserved pool. \n " items: description: GatewayStatusAddress describes an address that is bound to a Gateway. + oneOf: + - properties: + type: + enum: + - IPAddress + value: + anyOf: + - format: ipv4 + - format: ipv6 + - properties: + type: + not: + enum: + - IPAddress properties: type: default: IPAddress @@ -525,6 +553,11 @@ spec: required: - value type: object + x-kubernetes-validations: + - message: Hostname value must only contain valid characters (matching + ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) + rule: 'self.type == ''Hostname'' ? self.value.matches(''^(\\*\\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$''): + true' maxItems: 16 type: array conditions: @@ -813,10 +846,24 @@ spec: manner, assigning an appropriate set of Addresses. \n The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. - \n Support: Extended" + \n Support: Extended \n " items: description: GatewayAddress describes an address that can be bound to a Gateway. + oneOf: + - properties: + type: + enum: + - IPAddress + value: + anyOf: + - format: ipv4 + - format: ipv6 + - properties: + type: + not: + enum: + - IPAddress properties: type: default: IPAddress @@ -1240,13 +1287,27 @@ spec: description: Status defines the current state of Gateway. properties: addresses: - description: Addresses lists the IP addresses that have actually been - bound to the Gateway. These addresses may differ from the addresses + description: "Addresses lists the IP addresses that have actually + been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address - from a reserved pool. + from a reserved pool. \n " items: description: GatewayStatusAddress describes an address that is bound to a Gateway. + oneOf: + - properties: + type: + enum: + - IPAddress + value: + anyOf: + - format: ipv4 + - format: ipv6 + - properties: + type: + not: + enum: + - IPAddress properties: type: default: IPAddress @@ -1265,6 +1326,11 @@ spec: required: - value type: object + x-kubernetes-validations: + - message: Hostname value must only contain valid characters (matching + ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) + rule: 'self.type == ''Hostname'' ? self.value.matches(''^(\\*\\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$''): + true' maxItems: 16 type: array conditions: diff --git a/config/crd/experimental/gateway.networking.k8s.io_grpcroutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_grpcroutes.yaml index bc42220b70..ea5fc8aaad 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_grpcroutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_grpcroutes.yaml @@ -241,7 +241,7 @@ spec: Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n - Support: Extended \n " + Support: Extended \n " format: int32 maximum: 65535 minimum: 1 @@ -1495,7 +1495,7 @@ spec: the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. - \n Support: Extended \n " + \n Support: Extended \n " format: int32 maximum: 65535 minimum: 1 diff --git a/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml index 2d71ee1bee..e15913e7c0 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml @@ -228,7 +228,7 @@ spec: Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n - Support: Extended \n " + Support: Extended \n " format: int32 maximum: 65535 minimum: 1 @@ -2037,7 +2037,7 @@ spec: the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. - \n Support: Extended \n " + \n Support: Extended \n " format: int32 maximum: 65535 minimum: 1 @@ -2292,7 +2292,7 @@ spec: Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n - Support: Extended \n " + Support: Extended \n " format: int32 maximum: 65535 minimum: 1 @@ -4101,7 +4101,7 @@ spec: the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. - \n Support: Extended \n " + \n Support: Extended \n " format: int32 maximum: 65535 minimum: 1 diff --git a/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml index c8f925e61d..e1f4a6268a 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml @@ -163,7 +163,7 @@ spec: Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n - Support: Extended \n " + Support: Extended \n " format: int32 maximum: 65535 minimum: 1 @@ -515,7 +515,7 @@ spec: the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. - \n Support: Extended \n " + \n Support: Extended \n " format: int32 maximum: 65535 minimum: 1 diff --git a/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml index e35b549dc2..881c756be3 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml @@ -209,7 +209,7 @@ spec: Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n - Support: Extended \n " + Support: Extended \n " format: int32 maximum: 65535 minimum: 1 @@ -564,7 +564,7 @@ spec: the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. - \n Support: Extended \n " + \n Support: Extended \n " format: int32 maximum: 65535 minimum: 1 diff --git a/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml index 3af15f3892..a144d9e075 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml @@ -163,7 +163,7 @@ spec: Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. \n - Support: Extended \n " + Support: Extended \n " format: int32 maximum: 65535 minimum: 1 @@ -515,7 +515,7 @@ spec: the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. - \n Support: Extended \n " + \n Support: Extended \n " format: int32 maximum: 65535 minimum: 1 diff --git a/config/crd/standard/gateway.networking.k8s.io_gateways.yaml b/config/crd/standard/gateway.networking.k8s.io_gateways.yaml index 3b3951b079..e098af54e2 100644 --- a/config/crd/standard/gateway.networking.k8s.io_gateways.yaml +++ b/config/crd/standard/gateway.networking.k8s.io_gateways.yaml @@ -73,10 +73,24 @@ spec: manner, assigning an appropriate set of Addresses. \n The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. - \n Support: Extended" + \n Support: Extended \n " items: description: GatewayAddress describes an address that can be bound to a Gateway. + oneOf: + - properties: + type: + enum: + - IPAddress + value: + anyOf: + - format: ipv4 + - format: ipv6 + - properties: + type: + not: + enum: + - IPAddress properties: type: default: IPAddress @@ -500,13 +514,27 @@ spec: description: Status defines the current state of Gateway. properties: addresses: - description: Addresses lists the IP addresses that have actually been - bound to the Gateway. These addresses may differ from the addresses + description: "Addresses lists the IP addresses that have actually + been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address - from a reserved pool. + from a reserved pool. \n " items: description: GatewayStatusAddress describes an address that is bound to a Gateway. + oneOf: + - properties: + type: + enum: + - IPAddress + value: + anyOf: + - format: ipv4 + - format: ipv6 + - properties: + type: + not: + enum: + - IPAddress properties: type: default: IPAddress @@ -525,6 +553,11 @@ spec: required: - value type: object + x-kubernetes-validations: + - message: Hostname value must only contain valid characters (matching + ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) + rule: 'self.type == ''Hostname'' ? self.value.matches(''^(\\*\\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$''): + true' maxItems: 16 type: array conditions: @@ -813,10 +846,24 @@ spec: manner, assigning an appropriate set of Addresses. \n The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. - \n Support: Extended" + \n Support: Extended \n " items: description: GatewayAddress describes an address that can be bound to a Gateway. + oneOf: + - properties: + type: + enum: + - IPAddress + value: + anyOf: + - format: ipv4 + - format: ipv6 + - properties: + type: + not: + enum: + - IPAddress properties: type: default: IPAddress @@ -1240,13 +1287,27 @@ spec: description: Status defines the current state of Gateway. properties: addresses: - description: Addresses lists the IP addresses that have actually been - bound to the Gateway. These addresses may differ from the addresses + description: "Addresses lists the IP addresses that have actually + been bound to the Gateway. These addresses may differ from the addresses in the Spec, e.g. if the Gateway automatically assigns an address - from a reserved pool. + from a reserved pool. \n " items: description: GatewayStatusAddress describes an address that is bound to a Gateway. + oneOf: + - properties: + type: + enum: + - IPAddress + value: + anyOf: + - format: ipv4 + - format: ipv6 + - properties: + type: + not: + enum: + - IPAddress properties: type: default: IPAddress @@ -1265,6 +1326,11 @@ spec: required: - value type: object + x-kubernetes-validations: + - message: Hostname value must only contain valid characters (matching + ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) + rule: 'self.type == ''Hostname'' ? self.value.matches(''^(\\*\\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$''): + true' maxItems: 16 type: array conditions: diff --git a/examples/standard/gateway-addresses.yaml b/examples/standard/gateway-addresses.yaml new file mode 100644 index 0000000000..bb543a422c --- /dev/null +++ b/examples/standard/gateway-addresses.yaml @@ -0,0 +1,27 @@ +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: gateway-addresses +spec: + gatewayClassName: acme-lb + addresses: + - value: 1200:0000:AB00:1234:0000:2552:7777:1313 + - value: 21DA:D3:0:2F3B:2AA:FF:FE28:9C5A + - value: "2001:db8:3c4d:15:0:d234:3eee::" + - value: "1234::" + - value: "1.1.1.1" + - value: "1.2.3.4" + - value: "0.0.0.0" + - value: "9.255.255.255" + - value: "11.0.0.0" + - type: IPAddress + value: "255.255.255.255" + - type: "Hostname" + value: "example.com" + listeners: + - protocol: HTTP + port: 80 + name: prod-web-gw + allowedRoutes: + namespaces: + from: Same diff --git a/examples/standard/simple-gateway/gateway.yaml b/examples/standard/simple-gateway/gateway.yaml index 19924bdad0..9b19a20116 100644 --- a/examples/standard/simple-gateway/gateway.yaml +++ b/examples/standard/simple-gateway/gateway.yaml @@ -7,7 +7,7 @@ metadata: name: prod-web spec: gatewayClassName: acme-lb - listeners: + listeners: - protocol: HTTP port: 80 name: prod-web-gw diff --git a/hack/cel-validation/gateway_test.go b/hack/cel-validation/gateway_test.go index 51d1152bf1..5548f0f5f8 100644 --- a/hack/cel-validation/gateway_test.go +++ b/hack/cel-validation/gateway_test.go @@ -48,9 +48,10 @@ func TestValidateGateway(t *testing.T) { } testCases := []struct { - desc string - mutate func(gw *gatewayv1b1.Gateway) - wantErrors []string + desc string + mutate func(gw *gatewayv1b1.Gateway) + mutateStatus func(gw *gatewayv1b1.Gateway) + wantErrors []string }{ { desc: "tls config present with http protocol", @@ -173,7 +174,7 @@ func TestValidateGateway(t *testing.T) { wantErrors: []string{"hostname must be empty for protocols ['TCP', 'UDP']"}, }, { - desc: "certificatedRefs not set with https protocol and TLS terminate mode", + desc: "certificateRefs not set with https protocol and TLS terminate mode", mutate: func(gw *gatewayv1b1.Gateway) { tlsMode := gatewayv1b1.TLSModeType("Terminate") gw.Spec.Listeners = []gatewayv1b1.Listener{ @@ -190,7 +191,7 @@ func TestValidateGateway(t *testing.T) { wantErrors: []string{"certificateRefs must be set and not empty when TLSModeType is Terminate"}, }, { - desc: "certificatedRefs not set with tls protocol and TLS terminate mode", + desc: "certificateRefs not set with tls protocol and TLS terminate mode", mutate: func(gw *gatewayv1b1.Gateway) { tlsMode := gatewayv1b1.TLSModeType("Terminate") gw.Spec.Listeners = []gatewayv1b1.Listener{ @@ -207,7 +208,7 @@ func TestValidateGateway(t *testing.T) { wantErrors: []string{"certificateRefs must be set and not empty when TLSModeType is Terminate"}, }, { - desc: "certificatedRefs set with tls protocol and TLS terminate mode", + desc: "certificateRefs set with tls protocol and TLS terminate mode", mutate: func(gw *gatewayv1b1.Gateway) { tlsMode := gatewayv1b1.TLSModeType("Terminate") gw.Spec.Listeners = []gatewayv1b1.Listener{ @@ -399,10 +400,6 @@ func TestValidateGateway(t *testing.T) { mutate: func(gw *gatewayv1b1.Gateway) { gw.Spec.Addresses = []gatewayv1b1.GatewayAddress{ { - // TODO(gauravkghildiyal): Figure out a sensible way to check - // validity of IP addresses. Admission webhook uses golang IP - // parsing which may not be directly translateable to regex matching - // in CEL. At the moment, this value will not result in an error. Type: ptrTo(gatewayv1b1.IPAddressType), Value: "1.2.3.4:8080", }, @@ -416,7 +413,46 @@ func TestValidateGateway(t *testing.T) { }, } }, - wantErrors: []string{"Hostname value must only contain valid characters (matching ^(\\*\\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$)"}, + wantErrors: []string{"Invalid value: \"1.2.3.4:8080\": spec.addresses[0].value in body must be of type ipv4"}, + }, + { + desc: "ip address and hostname in status addresses are valid", + mutateStatus: func(gw *gatewayv1b1.Gateway) { + gw.Status.Addresses = []gatewayv1b1.GatewayStatusAddress{ + { + Type: ptrTo(gatewayv1b1.IPAddressType), + Value: "1.2.3.4", + }, + { + Type: ptrTo(gatewayv1b1.IPAddressType), + Value: "1111:2222:3333:4444::", + }, + { + Type: ptrTo(gatewayv1b1.HostnameAddressType), + Value: "foo.bar", + }, + } + }, + }, + { + desc: "ip address and hostname in status addresses are invalid", + mutateStatus: func(gw *gatewayv1b1.Gateway) { + gw.Status.Addresses = []gatewayv1b1.GatewayStatusAddress{ + { + Type: ptrTo(gatewayv1b1.IPAddressType), + Value: "1.2.3.4:8080", + }, + { + Type: ptrTo(gatewayv1b1.HostnameAddressType), + Value: "*foo/bar", + }, + { + Type: ptrTo(gatewayv1b1.HostnameAddressType), + Value: "12:34:56::", + }, + } + }, + wantErrors: []string{"Invalid value: \"1.2.3.4:8080\": spec.addresses[0].value in body must be of type ipv4"}, }, { desc: "duplicate ip address or hostname", @@ -446,14 +482,21 @@ func TestValidateGateway(t *testing.T) { for _, tc := range testCases { t.Run(tc.desc, func(t *testing.T) { - gwc := baseGateway.DeepCopy() - gwc.Name = fmt.Sprintf("foo-%v", time.Now().UnixNano()) + gw := baseGateway.DeepCopy() + gw.Name = fmt.Sprintf("foo-%v", time.Now().UnixNano()) + + if tc.mutate != nil { + tc.mutate(gw) + } + err := k8sClient.Create(ctx, gw) - tc.mutate(gwc) - err := k8sClient.Create(ctx, gwc) + if tc.mutateStatus != nil { + tc.mutateStatus(gw) + err = k8sClient.Status().Update(ctx, gw) + } if (len(tc.wantErrors) != 0) != (err != nil) { - t.Fatalf("Unexpected error while creating Gateway; got err=\n%v\n;want error=%v", err, tc.wantErrors != nil) + t.Fatalf("Unexpected response while creating Gateway; got err=\n%v\n;want error=%v", err, tc.wantErrors != nil) } var missingErrorStrings []string @@ -463,7 +506,7 @@ func TestValidateGateway(t *testing.T) { } } if len(missingErrorStrings) != 0 { - t.Errorf("Unexpected error while creating Gateway; got err=\n%v\n;missing strings within error=%q", err, missingErrorStrings) + t.Errorf("Unexpected response while creating Gateway; got err=\n%v\n;missing strings within error=%q", err, missingErrorStrings) } }) } diff --git a/hack/invalid-examples/v1beta1/gateway/invalid-addresses.yaml b/hack/invalid-examples/v1beta1/gateway/invalid-addresses.yaml new file mode 100644 index 0000000000..caa52c7acf --- /dev/null +++ b/hack/invalid-examples/v1beta1/gateway/invalid-addresses.yaml @@ -0,0 +1,28 @@ +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: invalid-addresses +spec: + gatewayClassName: acme-lb + addresses: + - value: 1200:0000:::AB00:1234:0000:2552:7777:1313 + - value: 21DA:D3:0:2F3B:2AY:FF:FE28:9C5A + - value: "2001:db8:3c4d:15:0:d234:3eee:" + - value: "2001:db8:3c4d:15:0:d234:3eee:::" + - value: ":::1234::" + - value: "1.1.1" + - value: "1.a.3.4" + - value: "foo.com" + - type: IPAddress + value: "256.255.255.255" + - type: "Hostname" + value: "foo.com:80" + - type: "example.com/custom" + value: "anything goes" + listeners: + - protocol: HTTP + port: 80 + name: prod-web-gw + allowedRoutes: + namespaces: + from: Same diff --git a/pkg/generator/main.go b/pkg/generator/main.go index 3b411dca99..7f07fb6f04 100644 --- a/pkg/generator/main.go +++ b/pkg/generator/main.go @@ -93,6 +93,7 @@ func main() { if channel == "standard" && !standardKinds[groupKind.Kind] { continue } + log.Printf("generating %s CRD for %v\n", channel, groupKind) parser.NeedCRDFor(groupKind, nil) @@ -111,7 +112,7 @@ func main() { channelCrd := crdRaw.DeepCopy() for _, version := range channelCrd.Spec.Versions { - version.Schema.OpenAPIV3Schema.Properties = channelTweaks(channel, version.Schema.OpenAPIV3Schema.Properties) + version.Schema.OpenAPIV3Schema.Properties = gatewayTweaks(channel, version.Schema.OpenAPIV3Schema.Properties) } conv, err := crd.AsVersion(*channelCrd, apiext.SchemeGroupVersion) @@ -133,9 +134,37 @@ func main() { } } -func channelTweaks(channel string, props map[string]apiext.JSONSchemaProps) map[string]apiext.JSONSchemaProps { +// Custom Gateway API Tweaks for tags prefixed with `") { + jsonProps.Items.Schema.OneOf = []apiext.JSONSchemaProps{{ + Properties: map[string]apiext.JSONSchemaProps{ + "type": { + Enum: []apiext.JSON{{Raw: []byte("\"IPAddress\"")}}, + }, + "value": { + AnyOf: []apiext.JSONSchemaProps{{ + Format: "ipv4", + }, { + Format: "ipv6", + }}, + }, + }, + }, { + Properties: map[string]apiext.JSONSchemaProps{ + "type": { + Not: &apiext.JSONSchemaProps{ + Enum: []apiext.JSON{{Raw: []byte("\"IPAddress\"")}}, + }, + }, + }, + }} + } + if channel == "standard" && strings.Contains(jsonProps.Description, "") { delete(props, name) continue @@ -158,13 +187,13 @@ func channelTweaks(channel string, props map[string]apiext.JSONSchemaProps) map[ } } - experimentalRe := regexp.MustCompile(``) - jsonProps.Description = experimentalRe.ReplaceAllLiteralString(jsonProps.Description, "") + gatewayRe := regexp.MustCompile(``) + jsonProps.Description = gatewayRe.ReplaceAllLiteralString(jsonProps.Description, "") if len(jsonProps.Properties) > 0 { - jsonProps.Properties = channelTweaks(channel, jsonProps.Properties) + jsonProps.Properties = gatewayTweaks(channel, jsonProps.Properties) } else if jsonProps.Items != nil && jsonProps.Items.Schema != nil { - jsonProps.Items.Schema.Properties = channelTweaks(channel, jsonProps.Items.Schema.Properties) + jsonProps.Items.Schema.Properties = gatewayTweaks(channel, jsonProps.Items.Schema.Properties) } props[name] = jsonProps }