...

Source file src/sigs.k8s.io/gateway-api/conformance/utils/kubernetes/helpers_test.go

Documentation: sigs.k8s.io/gateway-api/conformance/utils/kubernetes

     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  }
   322  

View as plain text