github.com/cilium/cilium@v1.16.2/operator/pkg/gateway-api/gateway_reconcile_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package gateway_api
     5  
     6  import (
     7  	"context"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  	corev1 "k8s.io/api/core/v1"
    13  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	ctrl "sigs.k8s.io/controller-runtime"
    15  	"sigs.k8s.io/controller-runtime/pkg/client"
    16  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    17  	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
    18  	gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
    19  
    20  	"github.com/cilium/cilium/operator/pkg/model"
    21  	"github.com/cilium/cilium/operator/pkg/model/translation"
    22  	gatewayApiTranslation "github.com/cilium/cilium/operator/pkg/model/translation/gateway-api"
    23  )
    24  
    25  var gwFixture = []client.Object{
    26  	// Valid Gateway class
    27  	&gatewayv1.GatewayClass{
    28  		ObjectMeta: metav1.ObjectMeta{
    29  			Name: "cilium",
    30  		},
    31  		Spec: gatewayv1.GatewayClassSpec{
    32  			ControllerName: "io.cilium/gateway-controller",
    33  		},
    34  	},
    35  
    36  	// Service for valid HTTPRoute
    37  	&corev1.Service{
    38  		ObjectMeta: metav1.ObjectMeta{
    39  			Name:      "dummy-backend",
    40  			Namespace: "default",
    41  		},
    42  	},
    43  	&corev1.Service{
    44  		ObjectMeta: metav1.ObjectMeta{
    45  			Name:      "cilium-gateway-valid-gateway",
    46  			Namespace: "another-namespace",
    47  			Annotations: map[string]string{
    48  				"pre-existing-annotation": "true",
    49  			},
    50  		},
    51  		Status: corev1.ServiceStatus{
    52  			LoadBalancer: corev1.LoadBalancerStatus{
    53  				Ingress: []corev1.LoadBalancerIngress{
    54  					{
    55  						IP: "10.10.10.11",
    56  						Ports: []corev1.PortStatus{
    57  							{
    58  								Port:     80,
    59  								Protocol: "TCP",
    60  							},
    61  						},
    62  					},
    63  				},
    64  			},
    65  		},
    66  	},
    67  	&corev1.Service{
    68  		ObjectMeta: metav1.ObjectMeta{
    69  			Name:      "cilium-gateway-valid-gateway",
    70  			Namespace: "default",
    71  			Annotations: map[string]string{
    72  				"pre-existing-annotation": "true",
    73  			},
    74  		},
    75  		Status: corev1.ServiceStatus{
    76  			LoadBalancer: corev1.LoadBalancerStatus{
    77  				Ingress: []corev1.LoadBalancerIngress{
    78  					{
    79  						IP: "10.10.10.10",
    80  						Ports: []corev1.PortStatus{
    81  							{
    82  								Port:     80,
    83  								Protocol: "TCP",
    84  							},
    85  						},
    86  					},
    87  				},
    88  			},
    89  		},
    90  	},
    91  	&corev1.Service{
    92  		ObjectMeta: metav1.ObjectMeta{
    93  			Name:      "cilium-gateway-test-long-long-long-long-long-long-lo-8tfth549c6",
    94  			Namespace: "long-name-test",
    95  			Annotations: map[string]string{
    96  				"pre-existing-annotation": "true",
    97  			},
    98  		},
    99  	},
   100  
   101  	// Service in another namespace
   102  	&corev1.Service{
   103  		ObjectMeta: metav1.ObjectMeta{
   104  			Name:      "dummy-backend",
   105  			Namespace: "another-namespace",
   106  		},
   107  	},
   108  
   109  	// Valid HTTPRoute
   110  	&gatewayv1.HTTPRoute{
   111  		ObjectMeta: metav1.ObjectMeta{
   112  			Name:      "http-route",
   113  			Namespace: "default",
   114  		},
   115  		Spec: gatewayv1.HTTPRouteSpec{
   116  			CommonRouteSpec: gatewayv1.CommonRouteSpec{
   117  				ParentRefs: []gatewayv1.ParentReference{
   118  					{
   119  						Name: "valid-gateway",
   120  					},
   121  				},
   122  			},
   123  			Rules: []gatewayv1.HTTPRouteRule{
   124  				{
   125  					BackendRefs: []gatewayv1.HTTPBackendRef{
   126  						{
   127  							BackendRef: gatewayv1.BackendRef{
   128  								BackendObjectReference: gatewayv1.BackendObjectReference{
   129  									Name: "dummy-backend",
   130  									Port: model.AddressOf[gatewayv1.PortNumber](80),
   131  								},
   132  							},
   133  						},
   134  					},
   135  				},
   136  			},
   137  		},
   138  		Status: gatewayv1.HTTPRouteStatus{
   139  			RouteStatus: gatewayv1.RouteStatus{
   140  				Parents: []gatewayv1.RouteParentStatus{
   141  					{
   142  						ParentRef: gatewayv1.ParentReference{
   143  							Name: "valid-gateway",
   144  						},
   145  						ControllerName: "io.cilium/gateway-controller",
   146  						Conditions: []metav1.Condition{
   147  							{
   148  								Type:   "Accepted",
   149  								Status: "True",
   150  							},
   151  						},
   152  					},
   153  				},
   154  			},
   155  		},
   156  	},
   157  
   158  	// Valid TLSRoute
   159  	&gatewayv1alpha2.TLSRoute{
   160  		ObjectMeta: metav1.ObjectMeta{
   161  			Name:      "tls-route",
   162  			Namespace: "default",
   163  		},
   164  		Spec: gatewayv1alpha2.TLSRouteSpec{
   165  			CommonRouteSpec: gatewayv1.CommonRouteSpec{
   166  				ParentRefs: []gatewayv1.ParentReference{
   167  					{
   168  						Name: "valid-tlsroute-gateway",
   169  					},
   170  				},
   171  			},
   172  			Hostnames: []gatewayv1alpha2.Hostname{
   173  				"sni.cilium.rocks",
   174  			},
   175  			Rules: []gatewayv1alpha2.TLSRouteRule{
   176  				{
   177  					BackendRefs: []gatewayv1.BackendRef{
   178  						{
   179  							BackendObjectReference: gatewayv1.BackendObjectReference{
   180  								Name: "dummy-backend",
   181  								Port: model.AddressOf[gatewayv1.PortNumber](443),
   182  							},
   183  						},
   184  					},
   185  				},
   186  			},
   187  		},
   188  		Status: gatewayv1alpha2.TLSRouteStatus{
   189  			RouteStatus: gatewayv1.RouteStatus{
   190  				Parents: []gatewayv1.RouteParentStatus{
   191  					{
   192  						ParentRef: gatewayv1.ParentReference{
   193  							Name: "valid-tlsroute-gateway",
   194  						},
   195  						ControllerName: "io.cilium/gateway-controller",
   196  						Conditions: []metav1.Condition{
   197  							{
   198  								Type:   "Accepted",
   199  								Status: "True",
   200  							},
   201  						},
   202  					},
   203  				},
   204  			},
   205  		},
   206  	},
   207  
   208  	// Valid gateway
   209  	&gatewayv1.Gateway{
   210  		TypeMeta: metav1.TypeMeta{
   211  			Kind:       "Gateway",
   212  			APIVersion: gatewayv1.GroupName,
   213  		},
   214  		ObjectMeta: metav1.ObjectMeta{
   215  			Name:      "valid-gateway",
   216  			Namespace: "default",
   217  		},
   218  		Spec: gatewayv1.GatewaySpec{
   219  			GatewayClassName: "cilium",
   220  			Listeners: []gatewayv1.Listener{
   221  				{
   222  					Name:     "http",
   223  					Port:     80,
   224  					Hostname: model.AddressOf[gatewayv1.Hostname]("*.cilium.io"),
   225  					Protocol: "HTTP",
   226  				},
   227  			},
   228  		},
   229  	},
   230  	// Valid gateway
   231  	&gatewayv1.Gateway{
   232  		TypeMeta: metav1.TypeMeta{
   233  			Kind:       "Gateway",
   234  			APIVersion: gatewayv1.GroupName,
   235  		},
   236  		ObjectMeta: metav1.ObjectMeta{
   237  			Name:      "test-long-long-long-long-long-long-long-long-long-long-long-long-name",
   238  			Namespace: "long-name-test",
   239  		},
   240  		Spec: gatewayv1.GatewaySpec{
   241  			GatewayClassName: "cilium",
   242  			Listeners: []gatewayv1.Listener{
   243  				{
   244  					Name:     "http",
   245  					Port:     80,
   246  					Hostname: model.AddressOf[gatewayv1.Hostname]("*.cilium.io"),
   247  					Protocol: "HTTP",
   248  				},
   249  			},
   250  		},
   251  	},
   252  	// gateway with non-existent gateway class
   253  	&gatewayv1.Gateway{
   254  		TypeMeta: metav1.TypeMeta{
   255  			Kind:       "Gateway",
   256  			APIVersion: gatewayv1.GroupName,
   257  		},
   258  		ObjectMeta: metav1.ObjectMeta{
   259  			Name:      "gateway-with-non-existent-gateway-class",
   260  			Namespace: "default",
   261  		},
   262  		Spec: gatewayv1.GatewaySpec{
   263  			GatewayClassName: "non-existent-gateway-class",
   264  			Listeners: []gatewayv1.Listener{
   265  				{
   266  					Name:     "http",
   267  					Port:     80,
   268  					Hostname: model.AddressOf[gatewayv1.Hostname]("*.cilium.io"),
   269  					Protocol: "HTTP",
   270  				},
   271  			},
   272  		},
   273  	},
   274  
   275  	/// Valid TLSRoute gateway
   276  	&gatewayv1.Gateway{
   277  		TypeMeta: metav1.TypeMeta{
   278  			Kind:       "Gateway",
   279  			APIVersion: gatewayv1.GroupName,
   280  		},
   281  		ObjectMeta: metav1.ObjectMeta{
   282  			Name:      "valid-tlsroute-gateway",
   283  			Namespace: "default",
   284  		},
   285  		Spec: gatewayv1.GatewaySpec{
   286  			GatewayClassName: "cilium",
   287  			Listeners: []gatewayv1.Listener{
   288  				{
   289  					Name:     "tls",
   290  					Port:     443,
   291  					Hostname: model.AddressOf[gatewayv1.Hostname]("*.cilium.rocks"),
   292  					Protocol: "TLS",
   293  				},
   294  			},
   295  		},
   296  	},
   297  }
   298  
   299  func Test_gatewayReconciler_Reconcile(t *testing.T) {
   300  	c := fake.NewClientBuilder().
   301  		WithScheme(testScheme()).
   302  		WithObjects(gwFixture...).
   303  		WithStatusSubresource(&gatewayv1.Gateway{}).
   304  		Build()
   305  
   306  	cecTranslator := translation.NewCECTranslator("", false, false, true, 60, false, nil, false, false, 0)
   307  	gatewayAPITranslator := gatewayApiTranslation.NewTranslator(cecTranslator, false, string(corev1.ServiceExternalTrafficPolicyCluster))
   308  
   309  	r := &gatewayReconciler{
   310  		Client:     c,
   311  		translator: gatewayAPITranslator,
   312  	}
   313  
   314  	t.Run("non-existent gateway", func(t *testing.T) {
   315  		result, err := r.Reconcile(context.Background(), ctrl.Request{
   316  			NamespacedName: client.ObjectKey{
   317  				Namespace: "default",
   318  				Name:      "non-existent-gateway",
   319  			},
   320  		})
   321  
   322  		require.NoError(t, err)
   323  		require.Equal(t, ctrl.Result{}, result)
   324  	})
   325  
   326  	t.Run("non-existent gateway class", func(t *testing.T) {
   327  		key := client.ObjectKey{
   328  			Namespace: "default",
   329  			Name:      "gateway-with-non-existent-gateway-class",
   330  		}
   331  		result, err := r.Reconcile(context.Background(), ctrl.Request{
   332  			NamespacedName: key,
   333  		})
   334  
   335  		require.NoError(t, err)
   336  		require.Equal(t, ctrl.Result{}, result)
   337  	})
   338  
   339  	t.Run("valid http gateway", func(t *testing.T) {
   340  		key := client.ObjectKey{
   341  			Namespace: "default",
   342  			Name:      "valid-gateway",
   343  		}
   344  		result, err := r.Reconcile(context.Background(), ctrl.Request{NamespacedName: key})
   345  
   346  		// First reconcile should wait for LB status before writing addresses into Ingress status
   347  		require.NoError(t, err)
   348  		require.Equal(t, ctrl.Result{}, result)
   349  
   350  		gw := &gatewayv1.Gateway{}
   351  		err = c.Get(context.Background(), key, gw)
   352  		require.NoError(t, err)
   353  
   354  		// Check that the gateway status has been updated
   355  		err = c.Get(context.Background(), key, gw)
   356  		require.NoError(t, err)
   357  
   358  		require.Len(t, gw.Status.Conditions, 2)
   359  		require.Equal(t, "Accepted", gw.Status.Conditions[0].Type)
   360  		require.Equal(t, "True", string(gw.Status.Conditions[0].Status))
   361  		require.Equal(t, "Gateway successfully scheduled", gw.Status.Conditions[0].Message)
   362  		require.Equal(t, "Programmed", gw.Status.Conditions[1].Type)
   363  		require.Equal(t, "True", string(gw.Status.Conditions[1].Status))
   364  		require.Equal(t, "Gateway successfully reconciled", gw.Status.Conditions[1].Message)
   365  
   366  		require.Len(t, gw.Status.Addresses, 1)
   367  		require.Equal(t, "IPAddress", string(*gw.Status.Addresses[0].Type))
   368  		require.Equal(t, "10.10.10.10", gw.Status.Addresses[0].Value)
   369  
   370  		require.Len(t, gw.Status.Listeners, 1)
   371  		require.Equal(t, "http", string(gw.Status.Listeners[0].Name))
   372  		require.Len(t, gw.Status.Listeners[0].Conditions, 3)
   373  		require.Equal(t, "Programmed", gw.Status.Listeners[0].Conditions[0].Type)
   374  		require.Equal(t, "True", string(gw.Status.Listeners[0].Conditions[0].Status))
   375  		require.Equal(t, "Programmed", gw.Status.Listeners[0].Conditions[0].Reason)
   376  		require.Equal(t, "Listener Programmed", gw.Status.Listeners[0].Conditions[0].Message)
   377  		require.Equal(t, "Accepted", gw.Status.Listeners[0].Conditions[1].Type)
   378  		require.Equal(t, "True", string(gw.Status.Listeners[0].Conditions[1].Status))
   379  		require.Equal(t, "ResolvedRefs", gw.Status.Listeners[0].Conditions[2].Type)
   380  		require.Equal(t, "True", string(gw.Status.Listeners[0].Conditions[2].Status))
   381  	})
   382  
   383  	t.Run("valid http gateway - long name", func(t *testing.T) {
   384  		key := client.ObjectKey{
   385  			Namespace: "long-name-test",
   386  			Name:      "test-long-long-long-long-long-long-long-long-long-long-long-long-name",
   387  		}
   388  		result, err := r.Reconcile(context.Background(), ctrl.Request{NamespacedName: key})
   389  
   390  		// First reconcile should wait for LB status before writing addresses into Ingress status
   391  		require.NoError(t, err)
   392  		require.Equal(t, ctrl.Result{}, result)
   393  
   394  		gw := &gatewayv1.Gateway{}
   395  		err = c.Get(context.Background(), key, gw)
   396  		require.NoError(t, err)
   397  		require.Empty(t, gw.Status.Addresses)
   398  
   399  		// Simulate LB service update
   400  		lb := &corev1.Service{}
   401  		err = c.Get(context.Background(), client.ObjectKey{Namespace: "long-name-test", Name: "cilium-gateway-test-long-long-long-long-long-long-lo-8tfth549c6"}, lb)
   402  		require.NoError(t, err)
   403  		require.Equal(t, corev1.ServiceTypeLoadBalancer, lb.Spec.Type)
   404  		require.Equal(t, "test-long-long-long-long-long-long-long-long-long-lo-4bftbgh5ht", lb.Labels["io.cilium.gateway/owning-gateway"])
   405  		require.Equal(t, "true", lb.Annotations["pre-existing-annotation"])
   406  
   407  		// Update LB status
   408  		lb.Status.LoadBalancer.Ingress = []corev1.LoadBalancerIngress{
   409  			{
   410  				IP: "10.10.10.20",
   411  				Ports: []corev1.PortStatus{
   412  					{
   413  						Port:     80,
   414  						Protocol: "TCP",
   415  					},
   416  				},
   417  			},
   418  		}
   419  		err = c.Status().Update(context.Background(), lb)
   420  		require.NoError(t, err)
   421  
   422  		// Perform second reconciliation
   423  		result, err = r.Reconcile(context.Background(), ctrl.Request{NamespacedName: key})
   424  		require.NoError(t, err)
   425  		require.Equal(t, ctrl.Result{}, result)
   426  
   427  		// Check that the gateway status has been updated
   428  		err = c.Get(context.Background(), key, gw)
   429  		require.NoError(t, err)
   430  
   431  		require.Len(t, gw.Status.Conditions, 2)
   432  		require.Equal(t, "Accepted", gw.Status.Conditions[0].Type)
   433  		require.Equal(t, "True", string(gw.Status.Conditions[0].Status))
   434  		require.Equal(t, "Gateway successfully scheduled", gw.Status.Conditions[0].Message)
   435  		require.Equal(t, "Programmed", gw.Status.Conditions[1].Type)
   436  		require.Equal(t, "True", string(gw.Status.Conditions[1].Status))
   437  		require.Equal(t, "Gateway successfully reconciled", gw.Status.Conditions[1].Message)
   438  
   439  		require.Len(t, gw.Status.Addresses, 1)
   440  		require.Equal(t, "IPAddress", string(*gw.Status.Addresses[0].Type))
   441  		require.Equal(t, "10.10.10.20", gw.Status.Addresses[0].Value)
   442  
   443  		require.Len(t, gw.Status.Listeners, 1)
   444  		require.Equal(t, "http", string(gw.Status.Listeners[0].Name))
   445  		require.Len(t, gw.Status.Listeners[0].Conditions, 3)
   446  		require.Equal(t, "Programmed", gw.Status.Listeners[0].Conditions[0].Type)
   447  		require.Equal(t, "True", string(gw.Status.Listeners[0].Conditions[0].Status))
   448  		require.Equal(t, "Programmed", gw.Status.Listeners[0].Conditions[0].Reason)
   449  		require.Equal(t, "Listener Programmed", gw.Status.Listeners[0].Conditions[0].Message)
   450  		require.Equal(t, "Accepted", gw.Status.Listeners[0].Conditions[1].Type)
   451  		require.Equal(t, "True", string(gw.Status.Listeners[0].Conditions[1].Status))
   452  		require.Equal(t, "ResolvedRefs", gw.Status.Listeners[0].Conditions[2].Type)
   453  		require.Equal(t, "True", string(gw.Status.Listeners[0].Conditions[2].Status))
   454  	})
   455  
   456  	t.Run("valid tls gateway", func(t *testing.T) {
   457  		key := client.ObjectKey{
   458  			Namespace: "default",
   459  			Name:      "valid-tlsroute-gateway",
   460  		}
   461  		result, err := r.Reconcile(context.Background(), ctrl.Request{NamespacedName: key})
   462  
   463  		// First reconcile should wait for LB status before writing addresses into Ingress status
   464  		require.NoError(t, err)
   465  		require.Equal(t, ctrl.Result{}, result)
   466  
   467  		gw := &gatewayv1.Gateway{}
   468  		err = c.Get(context.Background(), key, gw)
   469  		require.NoError(t, err)
   470  		require.Empty(t, gw.Status.Addresses)
   471  
   472  		// Simulate LB service update
   473  		lb := &corev1.Service{}
   474  		err = c.Get(context.Background(), client.ObjectKey{Namespace: "default", Name: "cilium-gateway-valid-tlsroute-gateway"}, lb)
   475  		require.NoError(t, err)
   476  		require.Equal(t, corev1.ServiceTypeLoadBalancer, lb.Spec.Type)
   477  		require.Equal(t, "valid-tlsroute-gateway", lb.Labels["io.cilium.gateway/owning-gateway"])
   478  
   479  		// Update LB status
   480  		lb.Status.LoadBalancer.Ingress = []corev1.LoadBalancerIngress{
   481  			{
   482  				IP: "10.10.10.11",
   483  				Ports: []corev1.PortStatus{
   484  					{
   485  						Port:     443,
   486  						Protocol: "TCP",
   487  					},
   488  				},
   489  			},
   490  		}
   491  		err = c.Status().Update(context.Background(), lb)
   492  		require.NoError(t, err)
   493  
   494  		// Perform second reconciliation
   495  		result, err = r.Reconcile(context.Background(), ctrl.Request{NamespacedName: key})
   496  		require.NoError(t, err)
   497  		require.Equal(t, ctrl.Result{}, result)
   498  
   499  		// Check that the gateway status has been updated
   500  		err = c.Get(context.Background(), key, gw)
   501  		require.NoError(t, err)
   502  
   503  		require.Len(t, gw.Status.Conditions, 2)
   504  		require.Equal(t, "Accepted", gw.Status.Conditions[0].Type)
   505  		require.Equal(t, "True", string(gw.Status.Conditions[0].Status))
   506  		require.Equal(t, "Gateway successfully scheduled", gw.Status.Conditions[0].Message)
   507  		require.Equal(t, "Programmed", gw.Status.Conditions[1].Type)
   508  		require.Equal(t, "True", string(gw.Status.Conditions[1].Status))
   509  		require.Equal(t, "Gateway successfully reconciled", gw.Status.Conditions[1].Message)
   510  
   511  		require.Len(t, gw.Status.Addresses, 1)
   512  		require.Equal(t, "IPAddress", string(*gw.Status.Addresses[0].Type))
   513  		require.Equal(t, "10.10.10.11", gw.Status.Addresses[0].Value)
   514  
   515  		require.Len(t, gw.Status.Listeners, 1)
   516  		require.Equal(t, "tls", string(gw.Status.Listeners[0].Name))
   517  		require.Len(t, gw.Status.Listeners[0].Conditions, 3)
   518  		require.Equal(t, "Programmed", gw.Status.Listeners[0].Conditions[0].Type)
   519  		require.Equal(t, "True", string(gw.Status.Listeners[0].Conditions[0].Status))
   520  		require.Equal(t, "Programmed", gw.Status.Listeners[0].Conditions[0].Reason)
   521  		require.Equal(t, "Listener Programmed", gw.Status.Listeners[0].Conditions[0].Message)
   522  		require.Equal(t, "Accepted", gw.Status.Listeners[0].Conditions[1].Type)
   523  		require.Equal(t, "True", string(gw.Status.Listeners[0].Conditions[1].Status))
   524  		require.Equal(t, "ResolvedRefs", gw.Status.Listeners[0].Conditions[2].Type)
   525  		require.Equal(t, "True", string(gw.Status.Listeners[0].Conditions[2].Status))
   526  	})
   527  }
   528  
   529  func Test_isValidPemFormat(t *testing.T) {
   530  	cert := []byte(`-----BEGIN CERTIFICATE-----
   531  MIIENDCCApygAwIBAgIRAKD/BLFBfwKIZ0WGrHtTH6gwDQYJKoZIhvcNAQELBQAw
   532  dzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSYwJAYDVQQLDB10YW1t
   533  YWNoQGZlZG9yYS5sYW4gKFRhbSBNYWNoKTEtMCsGA1UEAwwkbWtjZXJ0IHRhbW1h
   534  Y2hAZmVkb3JhLmxhbiAoVGFtIE1hY2gpMB4XDTIzMDIyMTExMDg0M1oXDTI1MDUy
   535  MTEyMDg0M1owUTEnMCUGA1UEChMebWtjZXJ0IGRldmVsb3BtZW50IGNlcnRpZmlj
   536  YXRlMSYwJAYDVQQLDB10YW1tYWNoQGZlZG9yYS5sYW4gKFRhbSBNYWNoKTCCASIw
   537  DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMIZy+0JRVjqpWgeq2dP+1oliO4A
   538  CcZnMg4tSqPalhDQL6Mf68HYLfizyJIpRzMJ905rYd0AcmXmu/g0Eo8ykHxFDz5T
   539  sePs2XQng8MN4azsRmm1l4f74ovawQzQcb822QP1CS6ILZ3VtwNjRh2nAwthYBMo
   540  CkngDGeQ8Gl0tjHLFnBdTdSwQRmE2jtDBcAgyEGpq+6ReYt+/47nNn7dCftsVqhE
   541  BYr9XH3itefHmsbfj7zWFbptdko7q9lMHwnBd+0hd40MmJIXMZrOGGFZjawJDBqS
   542  sBq2Q3l6XQz8X7P/GA8Dn8h4w3rppmiaN7LOmGXeki3xX2wqnM+0s6aZYZsCAwEA
   543  AaNhMF8wDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB8GA1Ud
   544  IwQYMBaAFGQ2DB06CdQFQBsYPye0NBwErUNEMBcGA1UdEQQQMA6CDHVuaXR0ZXN0
   545  LmNvbTANBgkqhkiG9w0BAQsFAAOCAYEArtHdKWXR6aELpfal17biabCPvIF9j6nw
   546  uDzcdMYQLrXm8M+NHe8x3dpI7u3lltO+dzLng+nVKQOR3alQACSmRD9c7ie8eT5d
   547  7zKOTk6keY195I1wVV4jbNLbNWa9y4RJQRTvBLAvAP9NVtUw2Q/w/ErUTqSyz+ob
   548  dwnt4gYCw6dGnluLxlfF34DB9KflvVNSnkyMB/gsB4A3r1GPOIo0Gyf74ig3FWrS
   549  wHYKnBbtZfYO0JV0LCoPyHe8g0XajZe8DCbP/E6SmlTNAmJESVjigTTcIBAkFI+n
   550  toBAdxfhjKUGaClOHS29cpaiynjSayGm4RkHkx7mcAua9lWPf7pSa3mCcFb+wFr3
   551  ABkHDPJH2acfaUK1vgKTgOwcG/6KA820/PraoSihLaPK/A7eg77r1EeYpt0Neppb
   552  XjvUp3YmVlIMZXPzrjOsastoDSrsygj5jdVtm4Pslv9nPhzDrBjlZpEJScW4Jlb+
   553  6wtd7p03UDBSKfTbVROVAe5mvJvA0hoS
   554  -----END CERTIFICATE-----
   555  `)
   556  	key := []byte(`-----BEGIN PRIVATE KEY-----
   557  MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDCGcvtCUVY6qVo
   558  HqtnT/taJYjuAAnGZzIOLUqj2pYQ0C+jH+vB2C34s8iSKUczCfdOa2HdAHJl5rv4
   559  NBKPMpB8RQ8+U7Hj7Nl0J4PDDeGs7EZptZeH++KL2sEM0HG/NtkD9QkuiC2d1bcD
   560  Y0YdpwMLYWATKApJ4AxnkPBpdLYxyxZwXU3UsEEZhNo7QwXAIMhBqavukXmLfv+O
   561  5zZ+3Qn7bFaoRAWK/Vx94rXnx5rG34+81hW6bXZKO6vZTB8JwXftIXeNDJiSFzGa
   562  zhhhWY2sCQwakrAatkN5el0M/F+z/xgPA5/IeMN66aZomjeyzphl3pIt8V9sKpzP
   563  tLOmmWGbAgMBAAECggEAEjASoMJ2og9Ssn/1NbgT6G2N+Cc+wz2WPifWT6ZC2452
   564  eEWcdMyJ+jz2dWOyzUCI0OtU/z10esH1KRvQBWUKjup1tDRpfd8KvUyalyNs2yRE
   565  sNEYQuDCaLJ11nqNvgooqatDUf3msFx/Sqz5u/uTWHSmaQUeea+p2eaF8IvEKsQf
   566  6QNklkeHsv+GVPv+iibfbXXne6I5aV35Rc4Q08zRCgYX/BN1AYXV6ho4RC9dZVGP
   567  JUkSLzRadegok/EONKkrqLZOFJVb2wtFq85gJ01lODM/gj7GqM59M/wk55CaQIRD
   568  9x5H4X4rpM2rhmiNLkIN0tGLKO8X31up7hTx9bvJcQKBgQD51MLWYYUPz/umvSrN
   569  QOT9UhEHI/bxtCbWQthW3L1qrVT7DB8Jko/6/xYlXhl7nwVwJz24jJf9vuxWbBpL
   570  HZRf0QsDO2/O4rqhKDov/GMUCx2shzc+J7k+T93KNVANYa05guqMeB8n30HProkF
   571  LgihVFF20k9Z6SibUvgTMpF1EwKBgQDG5MBgc8oFXmlr/7pHKizC4F3eDAXUxVHM
   572  WCIbSwMyzOXKqDcdXNDz8cQrjhKa2rD1fKhE0oRR+QvHz8IPC+0MsT7Q6QsIHYj5
   573  CXubHr0s5k8PJAp+Lk2EdHePZQM/I/vj/gSwxnJ9Qs64FWZ25K9zYnNNsiojQel7
   574  WVmI9IVaWQKBgD3BYggsQwANoV8uE455JCGaT6s8MKa+qXr9Owz9s7TS89a6wFFV
   575  cVHSDF9gS1xLisSWbqNX3ZpTv4f9YOKAhVTKD7bU0maJlSiREREbikJCHSuwoO80
   576  Uo4cn+6EDy2/n1pACkp+xvTMMzBrLGOjZW67sQd2JTdMc0Ux1TCpp1sRAoGAaEVI
   577  rchGYyYp8pqw19o+eTQTQfPforqHta+GwfRDiwBsgCBMNLKSQTHAfG0RR+na1/gw
   578  Z1ROVoNQL8K1pBnGft71ZaSnSeviAV19Vcd5ue5MCE4GyjwQG57Lh3uXhiShS9fC
   579  McL4Br9djJh7jV06ti0o8dSzzqQhea9QB0LaHpECgYApc8oBoiK69s0wXyI4+Phx
   580  ScBJ0XqDBYFkxyXr8Y5pEarEaqCtl1OPPMOiQRDWoxRR+FwA/0laSfh5xw0U3b+q
   581  iZ2XpkrbQp034rC0UR6p+Km1Sv9AVCACAjrcQ3NZaf8bDOWqvpla7Auq0oG8i6UX
   582  hEKCKf/N3gE1oMrTxVzUDQ==
   583  -----END PRIVATE KEY-----
   584  `)
   585  	keyAndCert := append(key, cert...)
   586  	type args struct {
   587  		b []byte
   588  	}
   589  	tests := []struct {
   590  		name string
   591  		args args
   592  		want bool
   593  	}{
   594  		{
   595  			name: "valid cert pem",
   596  			args: args{
   597  				b: cert,
   598  			},
   599  			want: true,
   600  		},
   601  		{
   602  			name: "value key pem",
   603  			args: args{
   604  				b: key,
   605  			},
   606  			want: true,
   607  		},
   608  		{
   609  			name: "multiple valid pem blocks",
   610  			args: args{
   611  				b: keyAndCert,
   612  			},
   613  			want: true,
   614  		},
   615  		{
   616  			name: "invalid first block",
   617  			args: args{
   618  				b: append([]byte("invalid block"), key...),
   619  			},
   620  			want: false,
   621  		},
   622  		{
   623  			name: "invalid subsequent block",
   624  			args: args{
   625  				b: append(keyAndCert, []byte("invalid block")...),
   626  			},
   627  			want: false,
   628  		},
   629  		{
   630  			name: "invalid pem",
   631  			args: args{
   632  				b: []byte("invalid pem"),
   633  			},
   634  			want: false,
   635  		},
   636  	}
   637  	for _, tt := range tests {
   638  		t.Run(tt.name, func(t *testing.T) {
   639  			assert.Equalf(t, tt.want, isValidPemFormat(tt.args.b), "isValidPemFormat(%v)", tt.args.b)
   640  		})
   641  	}
   642  }
   643  
   644  func Test_sectionNameMatched(t *testing.T) {
   645  	httpListener := &gatewayv1.Listener{
   646  		Name:     "http",
   647  		Port:     80,
   648  		Hostname: model.AddressOf[gatewayv1.Hostname]("*.cilium.io"),
   649  		Protocol: "HTTP",
   650  	}
   651  	httpNoMatchListener := &gatewayv1.Listener{
   652  		Name:     "http-no-match",
   653  		Port:     8080,
   654  		Hostname: model.AddressOf[gatewayv1.Hostname]("*.cilium.io"),
   655  		Protocol: "HTTP",
   656  	}
   657  	gw := &gatewayv1.Gateway{
   658  		TypeMeta: metav1.TypeMeta{
   659  			Kind:       "Gateway",
   660  			APIVersion: gatewayv1.GroupName,
   661  		},
   662  		ObjectMeta: metav1.ObjectMeta{
   663  			Name:      "valid-gateway",
   664  			Namespace: "default",
   665  		},
   666  		Spec: gatewayv1.GatewaySpec{
   667  			GatewayClassName: "cilium",
   668  			Listeners: []gatewayv1.Listener{
   669  				*httpListener,
   670  				*httpNoMatchListener,
   671  			},
   672  		},
   673  	}
   674  	type args struct {
   675  		routeNamespace string
   676  		listener       *gatewayv1.Listener
   677  		refs           []gatewayv1.ParentReference
   678  	}
   679  	tests := []struct {
   680  		name string
   681  		args args
   682  		want bool
   683  	}{
   684  		{
   685  			name: "Matching Section name",
   686  			args: args{
   687  				listener: httpListener,
   688  				refs: []gatewayv1.ParentReference{
   689  					{
   690  						Kind:        (*gatewayv1.Kind)(model.AddressOf("Gateway")),
   691  						Name:        "valid-gateway",
   692  						SectionName: (*gatewayv1.SectionName)(model.AddressOf("http")),
   693  					},
   694  				},
   695  			},
   696  			want: true,
   697  		},
   698  		{
   699  			name: "Not matching Section name",
   700  			args: args{
   701  				listener: httpNoMatchListener,
   702  				refs: []gatewayv1.ParentReference{
   703  					{
   704  						Kind:        (*gatewayv1.Kind)(model.AddressOf("Gateway")),
   705  						Name:        "valid-gateway",
   706  						SectionName: (*gatewayv1.SectionName)(model.AddressOf("http")),
   707  					},
   708  				},
   709  			},
   710  			want: false,
   711  		},
   712  		{
   713  			name: "Matching Port number",
   714  			args: args{
   715  				listener: httpListener,
   716  				refs: []gatewayv1.ParentReference{
   717  					{
   718  						Kind: (*gatewayv1.Kind)(model.AddressOf("Gateway")),
   719  						Name: "valid-gateway",
   720  						Port: (*gatewayv1.PortNumber)(model.AddressOf[int32](80)),
   721  					},
   722  				},
   723  			},
   724  			want: true,
   725  		},
   726  		{
   727  			name: "No matching Port number",
   728  			args: args{
   729  				listener: httpNoMatchListener,
   730  				refs: []gatewayv1.ParentReference{
   731  					{
   732  						Kind: (*gatewayv1.Kind)(model.AddressOf("Gateway")),
   733  						Name: "valid-gateway",
   734  						Port: (*gatewayv1.PortNumber)(model.AddressOf[int32](80)),
   735  					},
   736  				},
   737  			},
   738  			want: false,
   739  		},
   740  		{
   741  			name: "Matching both Section name and Port number",
   742  			args: args{
   743  				listener: httpListener,
   744  				refs: []gatewayv1.ParentReference{
   745  					{
   746  						Kind:        (*gatewayv1.Kind)(model.AddressOf("Gateway")),
   747  						Name:        "valid-gateway",
   748  						SectionName: (*gatewayv1.SectionName)(model.AddressOf("http")),
   749  						Port:        (*gatewayv1.PortNumber)(model.AddressOf[int32](80)),
   750  					},
   751  				},
   752  			},
   753  			want: true,
   754  		},
   755  		{
   756  			name: "Matching any listener (httpListener)",
   757  			args: args{
   758  				listener: httpListener,
   759  				refs: []gatewayv1.ParentReference{
   760  					{
   761  						Kind: (*gatewayv1.Kind)(model.AddressOf("Gateway")),
   762  						Name: "valid-gateway",
   763  					},
   764  				},
   765  			},
   766  			want: true,
   767  		},
   768  		{
   769  			name: "Matching any listener (httpNoMatchListener)",
   770  			args: args{
   771  				listener: httpNoMatchListener,
   772  				refs: []gatewayv1.ParentReference{
   773  					{
   774  						Kind: (*gatewayv1.Kind)(model.AddressOf("Gateway")),
   775  						Name: "valid-gateway",
   776  					},
   777  				},
   778  			},
   779  			want: true,
   780  		},
   781  	}
   782  	for _, tt := range tests {
   783  		t.Run(tt.name, func(t *testing.T) {
   784  			assert.Equalf(t, tt.want, parentRefMatched(gw, tt.args.listener, "default", tt.args.refs), "parentRefMatched(%v, %v, %v, %v)", gw, tt.args.listener, tt.args.routeNamespace, tt.args.refs)
   785  		})
   786  	}
   787  }