github.com/cilium/cilium@v1.16.2/operator/pkg/model/translation/ingress/dedicated_ingress_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package ingress
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/google/go-cmp/cmp"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  	"google.golang.org/protobuf/testing/protocmp"
    13  	corev1 "k8s.io/api/core/v1"
    14  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    15  
    16  	"github.com/cilium/cilium/operator/pkg/model"
    17  	"github.com/cilium/cilium/operator/pkg/model/translation"
    18  	ciliumv2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    19  	slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
    20  )
    21  
    22  func Test_getService(t *testing.T) {
    23  	resource := model.FullyQualifiedResource{
    24  		Name:      "dummy-ingress",
    25  		Namespace: "dummy-namespace",
    26  		Version:   "v1",
    27  		Kind:      "Ingress",
    28  		UID:       "d4bd3dc3-2ac5-4ab4-9dca-89c62c60177e",
    29  	}
    30  
    31  	t.Run("Default LB service", func(t *testing.T) {
    32  		it := &dedicatedIngressTranslator{}
    33  		res := it.getService(resource, nil, false)
    34  		require.Equal(t, &corev1.Service{
    35  			ObjectMeta: metav1.ObjectMeta{
    36  				Name:      "cilium-ingress-dummy-ingress",
    37  				Namespace: "dummy-namespace",
    38  				Labels:    map[string]string{"cilium.io/ingress": "true"},
    39  				OwnerReferences: []metav1.OwnerReference{
    40  					{
    41  						APIVersion: "networking.k8s.io/v1",
    42  						Kind:       "Ingress",
    43  						Name:       "dummy-ingress",
    44  						UID:        "d4bd3dc3-2ac5-4ab4-9dca-89c62c60177e",
    45  						Controller: model.AddressOf(true),
    46  					},
    47  				},
    48  			},
    49  			Spec: corev1.ServiceSpec{
    50  				Type: corev1.ServiceTypeLoadBalancer,
    51  				Ports: []corev1.ServicePort{
    52  					{
    53  						Name:     "http",
    54  						Protocol: "TCP",
    55  						Port:     80,
    56  					},
    57  					{
    58  						Name:     "https",
    59  						Protocol: "TCP",
    60  						Port:     443,
    61  					},
    62  				},
    63  			},
    64  		}, res)
    65  	})
    66  
    67  	t.Run("Default LB service with TLS only", func(t *testing.T) {
    68  		it := &dedicatedIngressTranslator{}
    69  		res := it.getService(resource, nil, true)
    70  		require.Equal(t, &corev1.Service{
    71  			ObjectMeta: metav1.ObjectMeta{
    72  				Name:      "cilium-ingress-dummy-ingress",
    73  				Namespace: "dummy-namespace",
    74  				Labels:    map[string]string{"cilium.io/ingress": "true"},
    75  				OwnerReferences: []metav1.OwnerReference{
    76  					{
    77  						APIVersion: "networking.k8s.io/v1",
    78  						Kind:       "Ingress",
    79  						Name:       "dummy-ingress",
    80  						UID:        "d4bd3dc3-2ac5-4ab4-9dca-89c62c60177e",
    81  						Controller: model.AddressOf(true),
    82  					},
    83  				},
    84  			},
    85  			Spec: corev1.ServiceSpec{
    86  				Type: corev1.ServiceTypeLoadBalancer,
    87  				Ports: []corev1.ServicePort{
    88  					{
    89  						Name:     "https",
    90  						Protocol: "TCP",
    91  						Port:     443,
    92  					},
    93  				},
    94  			},
    95  		}, res)
    96  	})
    97  
    98  	t.Run("Invalid LB service annotation, defaults to LoadBalancer", func(t *testing.T) {
    99  		it := &dedicatedIngressTranslator{}
   100  		res := it.getService(resource, &model.Service{
   101  			Type: "InvalidServiceType",
   102  		}, false)
   103  		require.Equal(t, &corev1.Service{
   104  			ObjectMeta: metav1.ObjectMeta{
   105  				Name:      "cilium-ingress-dummy-ingress",
   106  				Namespace: "dummy-namespace",
   107  				Labels:    map[string]string{"cilium.io/ingress": "true"},
   108  				OwnerReferences: []metav1.OwnerReference{
   109  					{
   110  						APIVersion: "networking.k8s.io/v1",
   111  						Kind:       "Ingress",
   112  						Name:       "dummy-ingress",
   113  						UID:        "d4bd3dc3-2ac5-4ab4-9dca-89c62c60177e",
   114  						Controller: model.AddressOf(true),
   115  					},
   116  				},
   117  			},
   118  			Spec: corev1.ServiceSpec{
   119  				Type: corev1.ServiceTypeLoadBalancer,
   120  				Ports: []corev1.ServicePort{
   121  					{
   122  						Name:     "http",
   123  						Protocol: "TCP",
   124  						Port:     80,
   125  					},
   126  					{
   127  						Name:     "https",
   128  						Protocol: "TCP",
   129  						Port:     443,
   130  					},
   131  				},
   132  			},
   133  		}, res)
   134  	})
   135  
   136  	t.Run("Node Port service", func(t *testing.T) {
   137  		var insecureNodePort uint32 = 3000
   138  		var secureNodePort uint32 = 3001
   139  		it := &dedicatedIngressTranslator{}
   140  		res := it.getService(resource, &model.Service{
   141  			Type:             "NodePort",
   142  			InsecureNodePort: &insecureNodePort,
   143  			SecureNodePort:   &secureNodePort,
   144  		}, false)
   145  		require.Equal(t, &corev1.Service{
   146  			ObjectMeta: metav1.ObjectMeta{
   147  				Name:      "cilium-ingress-dummy-ingress",
   148  				Namespace: "dummy-namespace",
   149  				Labels:    map[string]string{"cilium.io/ingress": "true"},
   150  				OwnerReferences: []metav1.OwnerReference{
   151  					{
   152  						APIVersion: "networking.k8s.io/v1",
   153  						Kind:       "Ingress",
   154  						Name:       "dummy-ingress",
   155  						UID:        "d4bd3dc3-2ac5-4ab4-9dca-89c62c60177e",
   156  						Controller: model.AddressOf(true),
   157  					},
   158  				},
   159  			},
   160  			Spec: corev1.ServiceSpec{
   161  				Type: corev1.ServiceTypeNodePort,
   162  				Ports: []corev1.ServicePort{
   163  					{
   164  						Name:     "http",
   165  						Protocol: "TCP",
   166  						Port:     80,
   167  						NodePort: 3000,
   168  					},
   169  					{
   170  						Name:     "https",
   171  						Protocol: "TCP",
   172  						Port:     443,
   173  						NodePort: 3001,
   174  					},
   175  				},
   176  			},
   177  		}, res)
   178  	})
   179  }
   180  
   181  func Test_getEndpointForIngress(t *testing.T) {
   182  	res := getEndpoints(model.FullyQualifiedResource{
   183  		Name:      "dummy-ingress",
   184  		Namespace: "dummy-namespace",
   185  		Version:   "v1",
   186  		Kind:      "Ingress",
   187  		UID:       "d4bd3dc3-2ac5-4ab4-9dca-89c62c60177e",
   188  	})
   189  
   190  	require.Equal(t, &corev1.Endpoints{
   191  		ObjectMeta: metav1.ObjectMeta{
   192  			Name:      "cilium-ingress-dummy-ingress",
   193  			Namespace: "dummy-namespace",
   194  			Labels:    map[string]string{"cilium.io/ingress": "true"},
   195  			OwnerReferences: []metav1.OwnerReference{
   196  				{
   197  					APIVersion: "networking.k8s.io/v1",
   198  					Kind:       "Ingress",
   199  					Name:       "dummy-ingress",
   200  					UID:        "d4bd3dc3-2ac5-4ab4-9dca-89c62c60177e",
   201  					Controller: model.AddressOf(true),
   202  				},
   203  			},
   204  		},
   205  		Subsets: []corev1.EndpointSubset{
   206  			{
   207  				Addresses: []corev1.EndpointAddress{{IP: "192.192.192.192"}},
   208  				Ports:     []corev1.EndpointPort{{Port: 9999}},
   209  			},
   210  		},
   211  	}, res)
   212  }
   213  
   214  func Test_translator_Translate(t *testing.T) {
   215  	type args struct {
   216  		m                            *model.Model
   217  		useProxyProtocol             bool
   218  		hostNetworkEnabled           bool
   219  		hostNetworkNodeLabelSelector *slim_metav1.LabelSelector
   220  		ipv4Enabled                  bool
   221  		ipv6Enabled                  bool
   222  	}
   223  	tests := []struct {
   224  		name          string
   225  		args          args
   226  		want          *ciliumv2.CiliumEnvoyConfig
   227  		wantLBSvcType corev1.ServiceType
   228  		wantErr       bool
   229  	}{
   230  		{
   231  			name: "Conformance/DefaultBackend",
   232  			args: args{
   233  				m: &model.Model{
   234  					HTTP: defaultBackendListeners,
   235  				},
   236  			},
   237  			want:          defaultBackendListenersCiliumEnvoyConfig,
   238  			wantLBSvcType: corev1.ServiceTypeLoadBalancer,
   239  		},
   240  		{
   241  			name: "Conformance/HostRules",
   242  			args: args{
   243  				m: &model.Model{
   244  					HTTP: hostRulesListenersEnforceHTTPS,
   245  				},
   246  			},
   247  			want:          hostRulesListenersEnforceHTTPSCiliumEnvoyConfig,
   248  			wantLBSvcType: corev1.ServiceTypeLoadBalancer,
   249  		},
   250  		{
   251  			name: "Conformance/HostRules,no Force HTTPS",
   252  			args: args{
   253  				m: &model.Model{
   254  					HTTP: hostRulesListeners,
   255  				},
   256  			},
   257  			want:          hostRulesListenersCiliumEnvoyConfig,
   258  			wantLBSvcType: corev1.ServiceTypeLoadBalancer,
   259  		},
   260  		{
   261  			name: "Conformance/PathRules",
   262  			args: args{
   263  				m: &model.Model{
   264  					HTTP: pathRulesListeners,
   265  				},
   266  			},
   267  			want:          pathRulesListenersCiliumEnvoyConfig,
   268  			wantLBSvcType: corev1.ServiceTypeLoadBalancer,
   269  		},
   270  		{
   271  			name: "Conformance/ProxyProtocol",
   272  			args: args{
   273  				m: &model.Model{
   274  					HTTP: proxyProtocolListeners,
   275  				},
   276  				useProxyProtocol: true,
   277  			},
   278  			want:          proxyProtoListenersCiliumEnvoyConfig,
   279  			wantLBSvcType: corev1.ServiceTypeLoadBalancer,
   280  		},
   281  		{
   282  			name: "Conformance/HostNetwork",
   283  			args: args{
   284  				m: &model.Model{
   285  					HTTP: hostNetworkListeners(55555),
   286  				},
   287  				hostNetworkEnabled:           true,
   288  				hostNetworkNodeLabelSelector: &slim_metav1.LabelSelector{MatchLabels: map[string]slim_metav1.MatchLabelsValue{"a": "b"}},
   289  				ipv4Enabled:                  true,
   290  			},
   291  			want:          hostNetworkListenersCiliumEnvoyConfig("0.0.0.0", 55555, &slim_metav1.LabelSelector{MatchLabels: map[string]slim_metav1.MatchLabelsValue{"a": "b"}}),
   292  			wantLBSvcType: corev1.ServiceTypeClusterIP,
   293  		},
   294  		{
   295  			name: "ComplexNodePortIngress",
   296  			args: args{
   297  				m: &model.Model{
   298  					HTTP: complexNodePortIngressListeners,
   299  				},
   300  				hostNetworkEnabled:           true,
   301  				hostNetworkNodeLabelSelector: &slim_metav1.LabelSelector{MatchLabels: map[string]slim_metav1.MatchLabelsValue{"a": "b"}},
   302  				ipv4Enabled:                  true,
   303  			},
   304  			want:          complexNodePortIngressCiliumEnvoyConfig,
   305  			wantLBSvcType: corev1.ServiceTypeNodePort,
   306  		},
   307  	}
   308  
   309  	for _, tt := range tests {
   310  		t.Run(tt.name, func(t *testing.T) {
   311  			trans := &dedicatedIngressTranslator{
   312  				cecTranslator:      translation.NewCECTranslator("cilium-secrets", tt.args.useProxyProtocol, false, false, 60, tt.args.hostNetworkEnabled, tt.args.hostNetworkNodeLabelSelector, tt.args.ipv4Enabled, tt.args.ipv6Enabled, 0),
   313  				hostNetworkEnabled: tt.args.hostNetworkEnabled,
   314  			}
   315  
   316  			cec, svc, ep, err := trans.Translate(tt.args.m)
   317  			require.Equal(t, tt.wantErr, err != nil, "Error mismatch")
   318  
   319  			diffOutput := cmp.Diff(tt.want, cec, protocmp.Transform())
   320  			if len(diffOutput) != 0 {
   321  				t.Errorf("CiliumEnvoyConfigs did not match:\n%s\n", diffOutput)
   322  			}
   323  
   324  			require.NotNil(t, svc)
   325  			assert.Equal(t, tt.wantLBSvcType, svc.Spec.Type)
   326  
   327  			require.NotNil(t, ep)
   328  		})
   329  	}
   330  }