sigs.k8s.io/gateway-api@v1.0.0/conformance/tests/gateway-modify-listeners.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, GatewayModifyListeners) 36 } 37 38 var GatewayModifyListeners = suite.ConformanceTest{ 39 ShortName: "GatewayModifyListeners", 40 Description: "A Gateway in the gateway-conformance-infra namespace should handle adding and removing listeners.", 41 Features: []suite.SupportedFeature{ 42 suite.SupportGateway, 43 }, 44 Manifests: []string{"tests/gateway-modify-listeners.yaml"}, 45 Test: func(t *testing.T, s *suite.ConformanceTestSuite) { 46 t.Run("should be able to add a listener that then becomes available for routing traffic", func(t *testing.T) { 47 gwNN := types.NamespacedName{Name: "gateway-add-listener", Namespace: "gateway-conformance-infra"} 48 ctx, cancel := context.WithTimeout(context.Background(), time.Minute) 49 defer cancel() 50 51 namespaces := []string{"gateway-conformance-infra"} 52 kubernetes.NamespacesMustBeReady(t, s.Client, s.TimeoutConfig, namespaces) 53 54 // verify that the implementation is tracking the most recent resource changes 55 kubernetes.GatewayMustHaveLatestConditions(t, s.Client, s.TimeoutConfig, gwNN) 56 57 original := &v1.Gateway{} 58 err := s.Client.Get(ctx, gwNN, original) 59 require.NoErrorf(t, err, "error getting Gateway: %v", err) 60 61 all := v1.NamespacesFromAll 62 63 mutate := original.DeepCopy() 64 65 // add a new listener to the Gateway spec 66 hostname := v1.Hostname("data.test.com") 67 mutate.Spec.Listeners = append(mutate.Spec.Listeners, v1.Listener{ 68 Name: "http", 69 Port: 80, 70 Protocol: v1.HTTPProtocolType, 71 Hostname: &hostname, 72 AllowedRoutes: &v1.AllowedRoutes{ 73 Namespaces: &v1.RouteNamespaces{From: &all}, 74 }, 75 }) 76 77 err = s.Client.Patch(ctx, mutate, client.MergeFrom(original)) 78 require.NoErrorf(t, err, "error patching the Gateway: %v", err) 79 80 // Ensure the generation and observedGeneration sync up 81 kubernetes.NamespacesMustBeReady(t, s.Client, s.TimeoutConfig, namespaces) 82 83 listeners := []v1.ListenerStatus{ 84 { 85 Name: v1.SectionName("https"), 86 SupportedKinds: []v1.RouteGroupKind{{ 87 Group: (*v1.Group)(&v1.GroupVersion.Group), 88 Kind: v1.Kind("HTTPRoute"), 89 }}, 90 Conditions: []metav1.Condition{ 91 { 92 Type: string(v1.ListenerConditionAccepted), 93 Status: metav1.ConditionTrue, 94 Reason: "", // any reason 95 }, 96 { 97 Type: string(v1.ListenerConditionResolvedRefs), 98 Status: metav1.ConditionTrue, 99 Reason: "", // any reason 100 }, 101 }, 102 AttachedRoutes: 1, 103 }, 104 { 105 Name: v1.SectionName("http"), 106 SupportedKinds: []v1.RouteGroupKind{{ 107 Group: (*v1.Group)(&v1.GroupVersion.Group), 108 Kind: v1.Kind("HTTPRoute"), 109 }}, 110 Conditions: []metav1.Condition{ 111 { 112 Type: string(v1.ListenerConditionAccepted), 113 Status: metav1.ConditionTrue, 114 Reason: "", // any reason 115 }, 116 { 117 Type: string(v1.ListenerConditionResolvedRefs), 118 Status: metav1.ConditionTrue, 119 Reason: "", // any reason 120 }, 121 }, 122 AttachedRoutes: 1, 123 }, 124 } 125 126 kubernetes.GatewayStatusMustHaveListeners(t, s.Client, s.TimeoutConfig, gwNN, listeners) 127 128 // verify that the implementation continues to keep up to date with the resource changes we've been making 129 kubernetes.GatewayMustHaveLatestConditions(t, s.Client, s.TimeoutConfig, gwNN) 130 131 updated := &v1.Gateway{} 132 err = s.Client.Get(ctx, gwNN, updated) 133 require.NoErrorf(t, err, "error getting Gateway: %v", err) 134 135 require.NotEqual(t, original.Generation, updated.Generation, "generation should change after an update") 136 }) 137 138 t.Run("should be able to remove listeners, which would then stop routing the relevant traffic", func(t *testing.T) { 139 gwNN := types.NamespacedName{Name: "gateway-remove-listener", Namespace: "gateway-conformance-infra"} 140 ctx, cancel := context.WithTimeout(context.Background(), time.Minute) 141 defer cancel() 142 143 namespaces := []string{"gateway-conformance-infra"} 144 kubernetes.NamespacesMustBeReady(t, s.Client, s.TimeoutConfig, namespaces) 145 146 // verify that the implementation is tracking the most recent resource changes 147 kubernetes.GatewayMustHaveLatestConditions(t, s.Client, s.TimeoutConfig, gwNN) 148 149 original := &v1.Gateway{} 150 err := s.Client.Get(ctx, gwNN, original) 151 require.NoErrorf(t, err, "error getting Gateway: %v", err) 152 153 mutate := original.DeepCopy() 154 require.Equalf(t, 2, len(mutate.Spec.Listeners), "the gateway must have 2 listeners") 155 156 // remove the "https" Gateway listener, leaving only the "http" listener 157 var newListeners []v1.Listener 158 for _, listener := range mutate.Spec.Listeners { 159 if listener.Name == "http" { 160 newListeners = append(newListeners, listener) 161 } 162 } 163 mutate.Spec.Listeners = newListeners 164 165 err = s.Client.Patch(ctx, mutate, client.MergeFrom(original)) 166 require.NoErrorf(t, err, "error patching the Gateway: %v", err) 167 168 // Ensure the generation and observedGeneration sync up 169 kubernetes.NamespacesMustBeReady(t, s.Client, s.TimeoutConfig, namespaces) 170 171 listeners := []v1.ListenerStatus{ 172 { 173 Name: v1.SectionName("http"), 174 SupportedKinds: []v1.RouteGroupKind{{ 175 Group: (*v1.Group)(&v1.GroupVersion.Group), 176 Kind: v1.Kind("HTTPRoute"), 177 }}, 178 Conditions: []metav1.Condition{ 179 { 180 Type: string(v1.ListenerConditionAccepted), 181 Status: metav1.ConditionTrue, 182 Reason: "", // any reason 183 }, 184 { 185 Type: string(v1.ListenerConditionResolvedRefs), 186 Status: metav1.ConditionTrue, 187 Reason: "", // any reason 188 }, 189 }, 190 AttachedRoutes: 1, 191 }, 192 } 193 194 kubernetes.GatewayStatusMustHaveListeners(t, s.Client, s.TimeoutConfig, gwNN, listeners) 195 196 // verify that the implementation continues to keep up to date with the resource changes we've been making 197 kubernetes.GatewayMustHaveLatestConditions(t, s.Client, s.TimeoutConfig, gwNN) 198 199 updated := &v1.Gateway{} 200 err = s.Client.Get(ctx, gwNN, updated) 201 require.NoErrorf(t, err, "error getting Gateway: %v", err) 202 203 require.NotEqual(t, original.Generation, updated.Generation, "generation should change after an update") 204 }) 205 }, 206 }