sigs.k8s.io/gateway-api@v1.0.0/conformance/utils/kubernetes/helpers_test.go (about) 1 /* 2 Copyright 2022 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 kubernetes 18 19 import ( 20 "fmt" 21 "testing" 22 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 corev1 "k8s.io/api/core/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/types" 28 "k8s.io/utils/ptr" 29 30 "sigs.k8s.io/gateway-api/apis/v1alpha2" 31 "sigs.k8s.io/gateway-api/apis/v1beta1" 32 ) 33 34 // ----------------------------------------------------------------------------- 35 // Test - Public Functions 36 // ----------------------------------------------------------------------------- 37 38 func TestNewGatewayRef(t *testing.T) { 39 tests := []struct { 40 name string 41 nsn types.NamespacedName 42 listenerNames []string 43 }{ 44 { 45 name: "verifying the contents of a GatewayRef with no provided listeners", 46 nsn: types.NamespacedName{Namespace: corev1.NamespaceDefault, Name: "fake-gateway"}, 47 }, 48 { 49 name: "verifying the contents of a GatewayRef listeners with one listener provided", 50 nsn: types.NamespacedName{Namespace: corev1.NamespaceDefault, Name: "fake-gateway"}, 51 listenerNames: []string{"fake-listener-1"}, 52 }, 53 { 54 name: "verifying the contents of a GatewayRef listeners with multiple listeners provided", 55 nsn: types.NamespacedName{Namespace: corev1.NamespaceDefault, Name: "fake-gateway"}, 56 listenerNames: []string{ 57 "fake-listener-1", 58 "fake-listener-2", 59 "fake-listener-3", 60 }, 61 }, 62 } 63 64 for i := 0; i < len(tests); i++ { 65 test := tests[i] 66 t.Run(test.name, func(t *testing.T) { 67 ref := NewGatewayRef(test.nsn, test.listenerNames...) 68 require.IsType(t, GatewayRef{}, ref) 69 if test.listenerNames == nil { 70 require.Len(t, ref.listenerNames, 1) 71 assert.Equal(t, "", string(*ref.listenerNames[0])) 72 } else { 73 require.Len(t, ref.listenerNames, len(test.listenerNames)) 74 for i := 0; i < len(ref.listenerNames); i++ { 75 assert.Equal(t, test.listenerNames[i], string(*ref.listenerNames[i])) 76 } 77 } 78 assert.Equal(t, test.nsn, ref.NamespacedName) 79 }) 80 } 81 } 82 83 func TestVerifyConditionsMatchGeneration(t *testing.T) { 84 tests := []struct { 85 name string 86 obj metav1.Object 87 conditions []metav1.Condition 88 expected error 89 }{ 90 {}, 91 { 92 name: "if no conditions are provided this technically passes verification", 93 }, 94 { 95 name: "conditions where all match the generation pass verification", 96 obj: &v1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Name: "fake-gateway", Generation: 20}}, 97 conditions: []metav1.Condition{ 98 {Type: "FakeCondition1", ObservedGeneration: 20}, 99 {Type: "FakeCondition2", ObservedGeneration: 20}, 100 {Type: "FakeCondition3", ObservedGeneration: 20}, 101 }, 102 }, 103 { 104 name: "conditions where one does not match the generation fail verification", 105 obj: &v1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Name: "fake-gateway", Generation: 20}}, 106 conditions: []metav1.Condition{ 107 {Type: "FakeCondition1", ObservedGeneration: 20}, 108 {Type: "FakeCondition2", ObservedGeneration: 19}, 109 {Type: "FakeCondition3", ObservedGeneration: 20}, 110 }, 111 expected: fmt.Errorf("expected observedGeneration to be updated to 20 for all conditions, only 2/3 were updated. stale conditions are: FakeCondition2 (generation 19)"), 112 }, 113 { 114 name: "conditions where most do not match the generation fail verification", 115 obj: &v1beta1.Gateway{ObjectMeta: metav1.ObjectMeta{Name: "fake-gateway", Generation: 20}}, 116 conditions: []metav1.Condition{ 117 {Type: "FakeCondition1", ObservedGeneration: 18}, 118 {Type: "FakeCondition2", ObservedGeneration: 18}, 119 {Type: "FakeCondition3", ObservedGeneration: 14}, 120 {Type: "FakeCondition4", ObservedGeneration: 20}, 121 {Type: "FakeCondition5", ObservedGeneration: 16}, 122 {Type: "FakeCondition6", ObservedGeneration: 15}, 123 }, 124 expected: fmt.Errorf("expected observedGeneration to be updated to 20 for all conditions, only 1/6 were updated. stale conditions are: FakeCondition1 (generation 18), FakeCondition2 (generation 18), FakeCondition3 (generation 14), FakeCondition5 (generation 16), FakeCondition6 (generation 15)"), 125 }, 126 } 127 128 for i := 0; i < len(tests); i++ { 129 test := tests[i] 130 t.Run(test.name, func(t *testing.T) { 131 err := ConditionsHaveLatestObservedGeneration(test.obj, test.conditions) 132 assert.Equal(t, test.expected, err) 133 }) 134 } 135 } 136 137 // ----------------------------------------------------------------------------- 138 // Test - Private Functions 139 // ----------------------------------------------------------------------------- 140 141 func Test_listenersMatch(t *testing.T) { 142 tests := []struct { 143 name string 144 expected []v1beta1.ListenerStatus 145 actual []v1beta1.ListenerStatus 146 want bool 147 }{ 148 { 149 name: "listeners do not match if a different number of actual and expected listeners are provided", 150 expected: []v1beta1.ListenerStatus{ 151 { 152 SupportedKinds: []v1beta1.RouteGroupKind{ 153 { 154 Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group), 155 Kind: v1beta1.Kind("HTTPRoute"), 156 }, 157 }, 158 }, 159 { 160 SupportedKinds: []v1beta1.RouteGroupKind{ 161 { 162 Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group), 163 Kind: v1beta1.Kind("GRPCRoute"), 164 }, 165 }, 166 }, 167 }, 168 actual: []v1beta1.ListenerStatus{ 169 { 170 SupportedKinds: []v1beta1.RouteGroupKind{ 171 { 172 Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group), 173 Kind: v1beta1.Kind("HTTPRoute"), 174 }, 175 }, 176 }, 177 }, 178 want: false, 179 }, 180 { 181 name: "SupportedKinds: expected empty and actual is non empty", 182 expected: []v1beta1.ListenerStatus{ 183 { 184 SupportedKinds: []v1beta1.RouteGroupKind{}, 185 }, 186 }, 187 actual: []v1beta1.ListenerStatus{ 188 { 189 SupportedKinds: []v1beta1.RouteGroupKind{ 190 { 191 Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group), 192 Kind: v1beta1.Kind("HTTPRoute"), 193 }, 194 }, 195 }, 196 }, 197 want: false, 198 }, 199 { 200 name: "SupportedKinds: expected and actual are equal", 201 expected: []v1beta1.ListenerStatus{ 202 { 203 SupportedKinds: []v1beta1.RouteGroupKind{ 204 { 205 Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group), 206 Kind: v1beta1.Kind("HTTPRoute"), 207 }, 208 }, 209 }, 210 }, 211 actual: []v1beta1.ListenerStatus{ 212 { 213 SupportedKinds: []v1beta1.RouteGroupKind{ 214 { 215 Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group), 216 Kind: v1beta1.Kind("HTTPRoute"), 217 }, 218 }, 219 }, 220 }, 221 want: true, 222 }, 223 { 224 name: "SupportedKinds: expected and actual are equal values, Group pointers are different", 225 expected: []v1beta1.ListenerStatus{ 226 { 227 SupportedKinds: []v1beta1.RouteGroupKind{ 228 { 229 Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group), 230 Kind: v1beta1.Kind("HTTPRoute"), 231 }, 232 }, 233 }, 234 }, 235 actual: []v1beta1.ListenerStatus{ 236 { 237 SupportedKinds: []v1beta1.RouteGroupKind{ 238 { 239 Group: ptr.To[v1beta1.Group]("gateway.networking.k8s.io"), 240 Kind: v1beta1.Kind("HTTPRoute"), 241 }, 242 }, 243 }, 244 }, 245 want: true, 246 }, 247 { 248 name: "SupportedKinds: expected kind not found in actual", 249 expected: []v1beta1.ListenerStatus{ 250 { 251 SupportedKinds: []v1beta1.RouteGroupKind{ 252 { 253 Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group), 254 Kind: v1beta1.Kind("HTTPRoute"), 255 }, 256 }, 257 }, 258 }, 259 actual: []v1beta1.ListenerStatus{ 260 { 261 SupportedKinds: []v1beta1.RouteGroupKind{ 262 { 263 Group: (*v1beta1.Group)(&v1alpha2.GroupVersion.Group), 264 Kind: v1beta1.Kind("GRPCRoute"), 265 }, 266 }, 267 }, 268 }, 269 want: false, 270 }, 271 { 272 name: "SupportedKinds: expected is a subset of actual", 273 expected: []v1beta1.ListenerStatus{ 274 { 275 SupportedKinds: []v1beta1.RouteGroupKind{ 276 { 277 Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group), 278 Kind: v1beta1.Kind("HTTPRoute"), 279 }, 280 }, 281 }, 282 }, 283 actual: []v1beta1.ListenerStatus{ 284 { 285 SupportedKinds: []v1beta1.RouteGroupKind{ 286 { 287 Group: (*v1beta1.Group)(&v1alpha2.GroupVersion.Group), 288 Kind: v1beta1.Kind("GRPCRoute"), 289 }, 290 { 291 Group: (*v1beta1.Group)(&v1beta1.GroupVersion.Group), 292 Kind: v1beta1.Kind("HTTPRoute"), 293 }, 294 }, 295 }, 296 }, 297 want: true, 298 }, 299 { 300 name: "expected and actual can be in different orders", 301 expected: []v1beta1.ListenerStatus{ 302 {Name: "listener-2"}, 303 {Name: "listener-3"}, 304 {Name: "listener-1"}, 305 }, 306 actual: []v1beta1.ListenerStatus{ 307 {Name: "listener-1"}, 308 {Name: "listener-2"}, 309 {Name: "listener-3"}, 310 }, 311 want: true, 312 }, 313 } 314 315 for _, test := range tests { 316 test := test 317 t.Run(test.name, func(t *testing.T) { 318 assert.Equal(t, test.want, listenersMatch(t, test.expected, test.actual)) 319 }) 320 } 321 }