From 7ae834e0b5ea57ec9ea5ee41c6a413b56077d0f1 Mon Sep 17 00:00:00 2001 From: Eyal Paz Date: Sun, 16 Feb 2025 10:45:35 +0200 Subject: [PATCH 1/4] conformance: add test for optional address value --- conformance/conformance.go | 1 + .../tests/gateway-optional-address-value.go | 72 +++++++++++++++++++ .../tests/gateway-optional-address-value.yaml | 17 +++++ conformance/utils/flags/flags.go | 1 + conformance/utils/kubernetes/apply.go | 10 +++ 5 files changed, 101 insertions(+) create mode 100644 conformance/tests/gateway-optional-address-value.go create mode 100644 conformance/tests/gateway-optional-address-value.yaml diff --git a/conformance/conformance.go b/conformance/conformance.go index ce264a9441..ebca201162 100644 --- a/conformance/conformance.go +++ b/conformance/conformance.go @@ -88,6 +88,7 @@ func DefaultOptions(t *testing.T) suite.ConformanceOptions { ExemptFeatures: exemptFeatures, ManifestFS: []fs.FS{&Manifests}, GatewayClassName: *flags.GatewayClassName, + AddressType: *flags.AddressType, Implementation: implementation, Mode: *flags.Mode, NamespaceAnnotations: namespaceAnnotations, diff --git a/conformance/tests/gateway-optional-address-value.go b/conformance/tests/gateway-optional-address-value.go new file mode 100644 index 0000000000..ea9d668ec0 --- /dev/null +++ b/conformance/tests/gateway-optional-address-value.go @@ -0,0 +1,72 @@ +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tests + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + + v1 "sigs.k8s.io/gateway-api/apis/v1" + "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" + "sigs.k8s.io/gateway-api/conformance/utils/suite" + "sigs.k8s.io/gateway-api/pkg/features" +) + +func init() { + ConformanceTests = append(ConformanceTests, GatewayOptionalAddressValue) +} + +var GatewayOptionalAddressValue = suite.ConformanceTest{ + ShortName: "GatewayOptionalAddressValue", + Description: "Check Gateway Support for GatewayAddressEmpty feature", + Features: []features.FeatureName{ + features.SupportGateway, + features.SupportGatewayAddressEmpty, + }, + Manifests: []string{ + "tests/gateway-optional-address-value.yaml", + }, + Test: func(t *testing.T, s *suite.ConformanceTestSuite) { + ns := "gateway-conformance-infra" + + kubernetes.NamespacesMustBeReady(t, s.Client, s.TimeoutConfig, []string{ns}) + + gwNN := types.NamespacedName{ + Name: "gateway-without-address-value", + Namespace: "gateway-conformance-infra", + } + ctx, cancel := context.WithTimeout(context.Background(), s.TimeoutConfig.DefaultTestTimeout) + defer cancel() + + t.Logf("waiting for namespace %s and Gateway %s to be ready for testing", gwNN.Namespace, gwNN.Name) + kubernetes.GatewayMustHaveLatestConditions(t, s.Client, s.TimeoutConfig, gwNN) + + t.Logf("retrieving Gateway %s/%s", gwNN.Namespace, gwNN.Name) + currentGW := &v1.Gateway{} + err := s.Client.Get(ctx, gwNN, currentGW) + require.NoError(t, err, "error getting Gateway: %v", err) + t.Logf("verifying that the Gateway %s/%s is accepted without address value field", gwNN.Namespace, gwNN.Name) + kubernetes.GatewayMustHaveCondition(t, s.Client, s.TimeoutConfig, gwNN, metav1.Condition{ + Type: string(v1.GatewayConditionAccepted), + Status: metav1.ConditionTrue, + }) + }, +} diff --git a/conformance/tests/gateway-optional-address-value.yaml b/conformance/tests/gateway-optional-address-value.yaml new file mode 100644 index 0000000000..90d901ab1d --- /dev/null +++ b/conformance/tests/gateway-optional-address-value.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: gateway-without-address-value + namespace: gateway-conformance-infra +spec: + gatewayClassName: "{GATEWAY_CLASS_NAME}" + addresses: + # This indicates an address type that will be substituted within the test suite + # It's currently inputted via the `address-type` flag + - type: "PLACEHOLDER_ADDRESS_TYPE" + + listeners: + - name: http + port: 8080 + protocol: HTTP diff --git a/conformance/utils/flags/flags.go b/conformance/utils/flags/flags.go index 95da9eaad1..9fdb2442a1 100644 --- a/conformance/utils/flags/flags.go +++ b/conformance/utils/flags/flags.go @@ -49,4 +49,5 @@ var ( ConformanceProfiles = flag.String("conformance-profiles", "", "Comma-separated list of the conformance profiles to run") ReportOutput = flag.String("report-output", "", "The file where to write the conformance report") SkipProvisionalTests = flag.Bool("skip-provisional-tests", false, "Whether to skip provisional tests") + AddressType = flag.String("address-type", "IPAddress", "Type of address in the gateway spec") ) diff --git a/conformance/utils/kubernetes/apply.go b/conformance/utils/kubernetes/apply.go index 1b3b01d5aa..5caaf7d2e6 100644 --- a/conformance/utils/kubernetes/apply.go +++ b/conformance/utils/kubernetes/apply.go @@ -139,6 +139,16 @@ func (a Applier) prepareGateway(t *testing.T, uObj *unstructured.Unstructured) { err = unstructured.SetNestedSlice(uObj.Object, primOverlayAddrs, "spec", "addresses") require.NoError(t, err, "could not overlay static addresses on Gateway %s/%s", ns, name) } + + // This is being done in order to support the injection of implementation-specific address types + // into the test suite + if len(gwspec.Addresses) > 0 && *gwspec.Addresses[0].Type == "PLACEHOLDER_ADDRESS_TYPE" { + addrs := map[string]interface{}{ + "type": a.AddressType, + } + err = unstructured.SetNestedSlice(uObj.Object, []interface{}{addrs}, "spec", "addresses") + require.NoError(t, err, "could not overlay address type on Gateway %s/%s", ns, name) + } } // prepareGatewayClass adjust the spec.controllerName on the resource From c1a20ac5a01412121554c52acaef112d2eb8fd18 Mon Sep 17 00:00:00 2001 From: Eyal Paz Date: Sun, 30 Mar 2025 10:59:46 +0300 Subject: [PATCH 2/4] fix: small resolutions after CR --- conformance/tests/gateway-optional-address-value.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conformance/tests/gateway-optional-address-value.go b/conformance/tests/gateway-optional-address-value.go index ea9d668ec0..7770e8319a 100644 --- a/conformance/tests/gateway-optional-address-value.go +++ b/conformance/tests/gateway-optional-address-value.go @@ -41,6 +41,7 @@ var GatewayOptionalAddressValue = suite.ConformanceTest{ features.SupportGateway, features.SupportGatewayAddressEmpty, }, + Provisional: true, Manifests: []string{ "tests/gateway-optional-address-value.yaml", }, @@ -63,7 +64,7 @@ var GatewayOptionalAddressValue = suite.ConformanceTest{ currentGW := &v1.Gateway{} err := s.Client.Get(ctx, gwNN, currentGW) require.NoError(t, err, "error getting Gateway: %v", err) - t.Logf("verifying that the Gateway %s/%s is accepted without address value field", gwNN.Namespace, gwNN.Name) + t.Logf("verifying that the Gateway %s/%s is accepted", gwNN.Namespace, gwNN.Name) kubernetes.GatewayMustHaveCondition(t, s.Client, s.TimeoutConfig, gwNN, metav1.Condition{ Type: string(v1.GatewayConditionAccepted), Status: metav1.ConditionTrue, From ecc2dd8d6d6c905bf9ddd1441a33a4e40262be0f Mon Sep 17 00:00:00 2001 From: Eyal Paz Date: Tue, 1 Apr 2025 09:55:03 +0300 Subject: [PATCH 3/4] fix: CR Resolution 2 --- .../tests/gateway-optional-address-value.go | 7 ++----- conformance/utils/kubernetes/apply.go | 17 ++++++++++++----- conformance/utils/kubernetes/helpers.go | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/conformance/tests/gateway-optional-address-value.go b/conformance/tests/gateway-optional-address-value.go index 7770e8319a..71d6e2721f 100644 --- a/conformance/tests/gateway-optional-address-value.go +++ b/conformance/tests/gateway-optional-address-value.go @@ -21,7 +21,6 @@ import ( "testing" "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" v1 "sigs.k8s.io/gateway-api/apis/v1" @@ -65,9 +64,7 @@ var GatewayOptionalAddressValue = suite.ConformanceTest{ err := s.Client.Get(ctx, gwNN, currentGW) require.NoError(t, err, "error getting Gateway: %v", err) t.Logf("verifying that the Gateway %s/%s is accepted", gwNN.Namespace, gwNN.Name) - kubernetes.GatewayMustHaveCondition(t, s.Client, s.TimeoutConfig, gwNN, metav1.Condition{ - Type: string(v1.GatewayConditionAccepted), - Status: metav1.ConditionTrue, - }) + _, err = kubernetes.WaitForGatewayAddress(t, s.Client, s.TimeoutConfig, kubernetes.NewGatewayRef(gwNN, "http")) + require.NoError(t, err, "timed out waiting for Gateway address to be assigned") }, } diff --git a/conformance/utils/kubernetes/apply.go b/conformance/utils/kubernetes/apply.go index 5caaf7d2e6..73d8386e66 100644 --- a/conformance/utils/kubernetes/apply.go +++ b/conformance/utils/kubernetes/apply.go @@ -142,13 +142,20 @@ func (a Applier) prepareGateway(t *testing.T, uObj *unstructured.Unstructured) { // This is being done in order to support the injection of implementation-specific address types // into the test suite - if len(gwspec.Addresses) > 0 && *gwspec.Addresses[0].Type == "PLACEHOLDER_ADDRESS_TYPE" { - addrs := map[string]interface{}{ - "type": a.AddressType, + var addresses []interface{} + for _, add := range gwspec.Addresses { + if *add.Type == "PLACEHOLDER_ADDRESS_TYPE" { + addresses = append(addresses, map[string]interface{}{ + "type": a.AddressType, + }, + ) + } else { + addresses = append(addresses, add) } - err = unstructured.SetNestedSlice(uObj.Object, []interface{}{addrs}, "spec", "addresses") - require.NoError(t, err, "could not overlay address type on Gateway %s/%s", ns, name) } + err = unstructured.SetNestedSlice(uObj.Object, addresses, "spec", "addresses") + fmt.Println(uObj.Object) + require.NoError(t, err, "could not overlay address type on Gateway %s/%s", ns, name) } // prepareGatewayClass adjust the spec.controllerName on the resource diff --git a/conformance/utils/kubernetes/helpers.go b/conformance/utils/kubernetes/helpers.go index 2b43828c09..dd5b45dc17 100644 --- a/conformance/utils/kubernetes/helpers.go +++ b/conformance/utils/kubernetes/helpers.go @@ -444,7 +444,7 @@ func WaitForGatewayAddress(t *testing.T, client client.Client, timeoutConfig con port = strconv.FormatInt(int64(listener.Port), 10) for _, address := range gw.Status.Addresses { - if address.Type != nil && (*address.Type == gatewayv1.IPAddressType || *address.Type == v1alpha2.HostnameAddressType) { + if address.Type != nil { ipAddr = address.Value return true, nil } From da53f221f68c05584b8e5d5d0fbef6a8ebc448ce Mon Sep 17 00:00:00 2001 From: Eyal Paz Date: Tue, 1 Apr 2025 10:27:01 +0300 Subject: [PATCH 4/4] fix: only overlay addresses if needed --- conformance/utils/kubernetes/apply.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/conformance/utils/kubernetes/apply.go b/conformance/utils/kubernetes/apply.go index 73d8386e66..f2f8e994f2 100644 --- a/conformance/utils/kubernetes/apply.go +++ b/conformance/utils/kubernetes/apply.go @@ -153,9 +153,11 @@ func (a Applier) prepareGateway(t *testing.T, uObj *unstructured.Unstructured) { addresses = append(addresses, add) } } - err = unstructured.SetNestedSlice(uObj.Object, addresses, "spec", "addresses") - fmt.Println(uObj.Object) - require.NoError(t, err, "could not overlay address type on Gateway %s/%s", ns, name) + + if len(addresses) > 0 { + err = unstructured.SetNestedSlice(uObj.Object, addresses, "spec", "addresses") + require.NoError(t, err, "could not overlay address type on Gateway %s/%s", ns, name) + } } // prepareGatewayClass adjust the spec.controllerName on the resource