sigs.k8s.io/gateway-api@v1.0.0/conformance/tests/gateway-static-addresses.go (about) 1 /* 2 Copyright 2023 The Kubernetes Authors. 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 tests 18 19 import ( 20 "context" 21 "testing" 22 "time" 23 24 "github.com/stretchr/testify/require" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/types" 27 "sigs.k8s.io/controller-runtime/pkg/client" 28 29 v1 "sigs.k8s.io/gateway-api/apis/v1" 30 "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" 31 "sigs.k8s.io/gateway-api/conformance/utils/suite" 32 ) 33 34 func init() { 35 ConformanceTests = append(ConformanceTests, GatewayStaticAddresses) 36 } 37 38 // GatewayStaticAddresses tests the implementation's support of deploying 39 // Gateway resources with static addresses, or in other words addresses 40 // provided via the specification rather than relying on the underlying 41 // implementation/network to dynamically assign the Gateway an address. 42 // 43 // Running this test against your own implementation is currently a little bit 44 // messy, as at the time of writing we didn't have great ways to provide the 45 // test suite with things like known good, or known bad addresses to run the 46 // test with (as we obviously can't determine that for the implementation). 47 // 48 // As such, if you're trying to enable this test for yourself and you're getting 49 // confused about how to provide addresses, you'll actually do that in the 50 // conformance test suite BEFORE you even set up and run your tests. Make sure 51 // you populate the following test suite fields: 52 // 53 // - suite.UsableNetworkAddresses 54 // - suite.UnusableNetworkAddresses 55 // 56 // With appropriate network addresses for your network environment. 57 var GatewayStaticAddresses = suite.ConformanceTest{ 58 ShortName: "GatewayStaticAddresses", 59 Description: "A Gateway in the gateway-conformance-infra namespace should be able to use previously determined addresses.", 60 Features: []suite.SupportedFeature{ 61 suite.SupportGateway, 62 suite.SupportGatewayStaticAddresses, 63 }, 64 Manifests: []string{ 65 "tests/gateway-static-addresses.yaml", 66 }, 67 Test: func(t *testing.T, s *suite.ConformanceTestSuite) { 68 gwNN := types.NamespacedName{ 69 Name: "gateway-static-addresses", 70 Namespace: "gateway-conformance-infra", 71 } 72 ctx, cancel := context.WithTimeout(context.Background(), time.Minute) 73 defer cancel() 74 75 t.Logf("waiting for namespace %s and Gateway %s to be ready for testing", gwNN.Namespace, gwNN.Name) 76 kubernetes.GatewayMustHaveLatestConditions(t, s.Client, s.TimeoutConfig, gwNN) 77 78 t.Logf("retrieving Gateway %s/%s and noting the provided addresses", gwNN.Namespace, gwNN.Name) 79 currentGW := &v1.Gateway{} 80 err := s.Client.Get(ctx, gwNN, currentGW) 81 require.NoError(t, err, "error getting Gateway: %v", err) 82 require.Len(t, currentGW.Spec.Addresses, 3, "expected 3 addresses on the Gateway, one invalid, one usable and one unusable. somehow got %d", len(currentGW.Spec.Addresses)) 83 invalidAddress := currentGW.Spec.Addresses[0] 84 unusableAddress := currentGW.Spec.Addresses[1] 85 usableAddress := currentGW.Spec.Addresses[2] 86 87 t.Logf("verifying that the Gateway %s/%s is NOT accepted due to an address type that the implementation doesn't support", gwNN.Namespace, gwNN.Name) 88 kubernetes.GatewayMustHaveCondition(t, s.Client, s.TimeoutConfig, gwNN, metav1.Condition{ 89 Type: string(v1.GatewayConditionAccepted), 90 Status: metav1.ConditionFalse, 91 Reason: string(v1.GatewayReasonUnsupportedAddress), 92 }) 93 94 t.Logf("patching Gateway %s/%s to remove the invalid address %s", gwNN.Namespace, gwNN.Name, invalidAddress.Value) 95 updatedGW := currentGW.DeepCopy() 96 updatedGW.Spec.Addresses = filterAddr(currentGW.Spec.Addresses, invalidAddress) 97 err = s.Client.Patch(ctx, updatedGW, client.MergeFrom(currentGW)) 98 require.NoError(t, err, "failed to patch Gateway: %v", err) 99 kubernetes.GatewayMustHaveLatestConditions(t, s.Client, s.TimeoutConfig, gwNN) 100 101 t.Logf("verifying that the Gateway %s/%s is now accepted, but is not programmed due to an address that can't be used", gwNN.Namespace, gwNN.Name) 102 err = s.Client.Get(ctx, gwNN, currentGW) 103 require.NoError(t, err, "error getting Gateway: %v", err) 104 kubernetes.GatewayMustHaveCondition(t, s.Client, s.TimeoutConfig, gwNN, metav1.Condition{ 105 Type: string(v1.GatewayConditionAccepted), 106 Status: metav1.ConditionTrue, 107 Reason: string(v1.GatewayReasonAccepted), 108 }) 109 kubernetes.GatewayMustHaveCondition(t, s.Client, s.TimeoutConfig, gwNN, metav1.Condition{ 110 Type: string(v1.GatewayConditionProgrammed), 111 Status: metav1.ConditionFalse, 112 Reason: string(v1.GatewayReasonAddressNotUsable), 113 }) 114 115 t.Logf("patching Gateway %s/%s to remove the unusable address %s", gwNN.Namespace, gwNN.Name, unusableAddress.Value) 116 updatedGW = currentGW.DeepCopy() 117 updatedGW.Spec.Addresses = filterAddr(currentGW.Spec.Addresses, unusableAddress) 118 err = s.Client.Patch(ctx, updatedGW, client.MergeFrom(currentGW)) 119 require.NoError(t, err, "failed to patch Gateway: %v", err) 120 kubernetes.GatewayMustHaveLatestConditions(t, s.Client, s.TimeoutConfig, gwNN) 121 122 t.Logf("verifying that the Gateway %s/%s is accepted and programmed with the usable static address %s assigned", gwNN.Namespace, gwNN.Name, usableAddress.Value) 123 err = s.Client.Get(ctx, gwNN, currentGW) 124 require.NoError(t, err, "error getting Gateway: %v", err) 125 kubernetes.GatewayMustHaveCondition(t, s.Client, s.TimeoutConfig, gwNN, metav1.Condition{ 126 Type: string(v1.GatewayConditionAccepted), 127 Status: metav1.ConditionTrue, 128 Reason: string(v1.GatewayReasonAccepted), 129 }) 130 kubernetes.GatewayMustHaveCondition(t, s.Client, s.TimeoutConfig, gwNN, metav1.Condition{ 131 Type: string(v1.GatewayConditionProgrammed), 132 Status: metav1.ConditionTrue, 133 Reason: string(v1.GatewayReasonProgrammed), 134 }) 135 kubernetes.GatewayStatusMustHaveListeners(t, s.Client, s.TimeoutConfig, gwNN, finalExpectedListenerState) 136 require.Len(t, currentGW.Spec.Addresses, 1, "expected only 1 address left specified on Gateway") 137 require.Len(t, currentGW.Status.Addresses, 1, "one usable address was provided, so it should be the one reflected in status") 138 require.Equal(t, usableAddress.Type, currentGW.Status.Addresses[0].Type, "expected address type to match the usable address") 139 require.Equal(t, usableAddress.Value, currentGW.Status.Addresses[0].Value, "expected usable address to be assigned") 140 }, 141 } 142 143 // ----------------------------------------------------------------------------- 144 // Private Helper Functions 145 // ----------------------------------------------------------------------------- 146 147 func filterAddr(addrs []v1.GatewayAddress, filter v1.GatewayAddress) (newAddrs []v1.GatewayAddress) { 148 for _, addr := range addrs { 149 if addr.Value != filter.Value { 150 newAddrs = append(newAddrs, addr) 151 } 152 } 153 return 154 } 155 156 var finalExpectedListenerState = []v1.ListenerStatus{ 157 { 158 Name: v1.SectionName("http"), 159 SupportedKinds: []v1.RouteGroupKind{{ 160 Group: (*v1.Group)(&v1.GroupVersion.Group), 161 Kind: v1.Kind("HTTPRoute"), 162 }}, 163 Conditions: []metav1.Condition{ 164 { 165 Type: string(v1.ListenerConditionAccepted), 166 Status: metav1.ConditionTrue, 167 Reason: "", // any reason 168 }, 169 { 170 Type: string(v1.ListenerConditionResolvedRefs), 171 Status: metav1.ConditionTrue, 172 Reason: "", // any reason 173 }, 174 }, 175 AttachedRoutes: 0, 176 }, 177 }