github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/configs/ingress_test.go (about)

     1  package configs
     2  
     3  import (
     4  	"errors"
     5  	"reflect"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/google/go-cmp/cmp"
    10  	"github.com/nginxinc/kubernetes-ingress/internal/k8s/secrets"
    11  	v1 "k8s.io/api/core/v1"
    12  	networking "k8s.io/api/networking/v1beta1"
    13  	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    15  	"k8s.io/apimachinery/pkg/util/intstr"
    16  
    17  	"github.com/nginxinc/kubernetes-ingress/internal/configs/version1"
    18  )
    19  
    20  func TestGenerateNginxCfg(t *testing.T) {
    21  	cafeIngressEx := createCafeIngressEx()
    22  	configParams := NewDefaultConfigParams()
    23  
    24  	isPlus := false
    25  	expected := createExpectedConfigForCafeIngressEx(isPlus)
    26  
    27  	apRes := AppProtectResources{}
    28  	result, warnings := generateNginxCfg(&cafeIngressEx, apRes, false, configParams, false, false, &StaticConfigParams{}, false)
    29  
    30  	if diff := cmp.Diff(expected, result); diff != "" {
    31  		t.Errorf("generateNginxCfg() returned unexpected result (-want +got):\n%s", diff)
    32  	}
    33  	if len(warnings) != 0 {
    34  		t.Errorf("generateNginxCfg() returned warnings: %v", warnings)
    35  	}
    36  }
    37  
    38  func TestGenerateNginxCfgForJWT(t *testing.T) {
    39  	cafeIngressEx := createCafeIngressEx()
    40  	cafeIngressEx.Ingress.Annotations["nginx.com/jwt-key"] = "cafe-jwk"
    41  	cafeIngressEx.Ingress.Annotations["nginx.com/jwt-realm"] = "Cafe App"
    42  	cafeIngressEx.Ingress.Annotations["nginx.com/jwt-token"] = "$cookie_auth_token"
    43  	cafeIngressEx.Ingress.Annotations["nginx.com/jwt-login-url"] = "https://login.example.com"
    44  	cafeIngressEx.SecretRefs["cafe-jwk"] = &secrets.SecretReference{
    45  		Secret: &v1.Secret{
    46  			Type: secrets.SecretTypeJWK,
    47  		},
    48  		Path: "/etc/nginx/secrets/default-cafe-jwk",
    49  	}
    50  
    51  	configParams := NewDefaultConfigParams()
    52  
    53  	isPlus := true
    54  
    55  	expected := createExpectedConfigForCafeIngressEx(isPlus)
    56  	expected.Servers[0].JWTAuth = &version1.JWTAuth{
    57  		Key:                  "/etc/nginx/secrets/default-cafe-jwk",
    58  		Realm:                "Cafe App",
    59  		Token:                "$cookie_auth_token",
    60  		RedirectLocationName: "@login_url_default-cafe-ingress",
    61  	}
    62  	expected.Servers[0].JWTRedirectLocations = []version1.JWTRedirectLocation{
    63  		{
    64  			Name:     "@login_url_default-cafe-ingress",
    65  			LoginURL: "https://login.example.com",
    66  		},
    67  	}
    68  
    69  	apRes := AppProtectResources{}
    70  	result, warnings := generateNginxCfg(&cafeIngressEx, apRes, false, configParams, true, false, &StaticConfigParams{}, false)
    71  
    72  	if !reflect.DeepEqual(result.Servers[0].JWTAuth, expected.Servers[0].JWTAuth) {
    73  		t.Errorf("generateNginxCfg returned \n%v,  but expected \n%v", result.Servers[0].JWTAuth, expected.Servers[0].JWTAuth)
    74  	}
    75  	if !reflect.DeepEqual(result.Servers[0].JWTRedirectLocations, expected.Servers[0].JWTRedirectLocations) {
    76  		t.Errorf("generateNginxCfg returned \n%v,  but expected \n%v", result.Servers[0].JWTRedirectLocations, expected.Servers[0].JWTRedirectLocations)
    77  	}
    78  	if len(warnings) != 0 {
    79  		t.Errorf("generateNginxCfg returned warnings: %v", warnings)
    80  	}
    81  }
    82  
    83  func TestGenerateNginxCfgWithMissingTLSSecret(t *testing.T) {
    84  	cafeIngressEx := createCafeIngressEx()
    85  	cafeIngressEx.SecretRefs["cafe-secret"].Error = errors.New("secret doesn't exist")
    86  	configParams := NewDefaultConfigParams()
    87  
    88  	apRes := AppProtectResources{}
    89  	result, resultWarnings := generateNginxCfg(&cafeIngressEx, apRes, false, configParams, false, false, &StaticConfigParams{}, false)
    90  
    91  	expectedSSLRejectHandshake := true
    92  	expectedWarnings := Warnings{
    93  		cafeIngressEx.Ingress: {
    94  			"TLS secret cafe-secret is invalid: secret doesn't exist",
    95  		},
    96  	}
    97  
    98  	resultSSLRejectHandshake := result.Servers[0].SSLRejectHandshake
    99  	if !reflect.DeepEqual(resultSSLRejectHandshake, expectedSSLRejectHandshake) {
   100  		t.Errorf("generateNginxCfg returned SSLRejectHandshake %v,  but expected %v", resultSSLRejectHandshake, expectedSSLRejectHandshake)
   101  	}
   102  	if diff := cmp.Diff(expectedWarnings, resultWarnings); diff != "" {
   103  		t.Errorf("generateNginxCfg returned unexpected result (-want +got):\n%s", diff)
   104  	}
   105  }
   106  
   107  func TestGenerateNginxCfgWithWildcardTLSSecret(t *testing.T) {
   108  	cafeIngressEx := createCafeIngressEx()
   109  	cafeIngressEx.Ingress.Spec.TLS[0].SecretName = ""
   110  	configParams := NewDefaultConfigParams()
   111  
   112  	apRes := AppProtectResources{}
   113  	result, warnings := generateNginxCfg(&cafeIngressEx, apRes, false, configParams, false, false, &StaticConfigParams{}, true)
   114  
   115  	resultServer := result.Servers[0]
   116  	if !reflect.DeepEqual(resultServer.SSLCertificate, pemFileNameForWildcardTLSSecret) {
   117  		t.Errorf("generateNginxCfg returned SSLCertificate %v,  but expected %v", resultServer.SSLCertificate, pemFileNameForWildcardTLSSecret)
   118  	}
   119  	if !reflect.DeepEqual(resultServer.SSLCertificateKey, pemFileNameForWildcardTLSSecret) {
   120  		t.Errorf("generateNginxCfg returned SSLCertificateKey %v,  but expected %v", resultServer.SSLCertificateKey, pemFileNameForWildcardTLSSecret)
   121  	}
   122  	if len(warnings) != 0 {
   123  		t.Errorf("generateNginxCfg returned warnings: %v", warnings)
   124  	}
   125  }
   126  
   127  func TestPathOrDefaultReturnDefault(t *testing.T) {
   128  	path := ""
   129  	expected := "/"
   130  	if pathOrDefault(path) != expected {
   131  		t.Errorf("pathOrDefault(%q) should return %q", path, expected)
   132  	}
   133  }
   134  
   135  func TestPathOrDefaultReturnActual(t *testing.T) {
   136  	path := "/path/to/resource"
   137  	if pathOrDefault(path) != path {
   138  		t.Errorf("pathOrDefault(%q) should return %q", path, path)
   139  	}
   140  }
   141  
   142  func TestGenerateIngressPath(t *testing.T) {
   143  	exact := networking.PathTypeExact
   144  	prefix := networking.PathTypePrefix
   145  	impSpec := networking.PathTypeImplementationSpecific
   146  	tests := []struct {
   147  		pathType *networking.PathType
   148  		path     string
   149  		expected string
   150  	}{
   151  		{
   152  			pathType: &exact,
   153  			path:     "/path/to/resource",
   154  			expected: "= /path/to/resource",
   155  		},
   156  		{
   157  			pathType: &prefix,
   158  			path:     "/path/to/resource",
   159  			expected: "/path/to/resource",
   160  		},
   161  		{
   162  			pathType: &impSpec,
   163  			path:     "/path/to/resource",
   164  			expected: "/path/to/resource",
   165  		},
   166  		{
   167  			pathType: nil,
   168  			path:     "/path/to/resource",
   169  			expected: "/path/to/resource",
   170  		},
   171  	}
   172  	for _, test := range tests {
   173  		result := generateIngressPath(test.path, test.pathType)
   174  		if result != test.expected {
   175  			t.Errorf("generateIngressPath(%v, %v) returned %v, but expected %v", test.path, test.pathType, result, test.expected)
   176  		}
   177  	}
   178  }
   179  
   180  func createExpectedConfigForCafeIngressEx(isPlus bool) version1.IngressNginxConfig {
   181  	coffeeUpstream := version1.Upstream{
   182  		Name:             "default-cafe-ingress-cafe.example.com-coffee-svc-80",
   183  		LBMethod:         "random two least_conn",
   184  		UpstreamZoneSize: "256k",
   185  		UpstreamServers: []version1.UpstreamServer{
   186  			{
   187  				Address:     "10.0.0.1",
   188  				Port:        "80",
   189  				MaxFails:    1,
   190  				MaxConns:    0,
   191  				FailTimeout: "10s",
   192  			},
   193  		},
   194  	}
   195  	if isPlus {
   196  		coffeeUpstream.UpstreamLabels = version1.UpstreamLabels{
   197  			Service:           "coffee-svc",
   198  			ResourceType:      "ingress",
   199  			ResourceName:      "cafe-ingress",
   200  			ResourceNamespace: "default",
   201  		}
   202  	}
   203  
   204  	teaUpstream := version1.Upstream{
   205  		Name:             "default-cafe-ingress-cafe.example.com-tea-svc-80",
   206  		LBMethod:         "random two least_conn",
   207  		UpstreamZoneSize: "256k",
   208  		UpstreamServers: []version1.UpstreamServer{
   209  			{
   210  				Address:     "10.0.0.2",
   211  				Port:        "80",
   212  				MaxFails:    1,
   213  				MaxConns:    0,
   214  				FailTimeout: "10s",
   215  			},
   216  		},
   217  	}
   218  	if isPlus {
   219  		teaUpstream.UpstreamLabels = version1.UpstreamLabels{
   220  			Service:           "tea-svc",
   221  			ResourceType:      "ingress",
   222  			ResourceName:      "cafe-ingress",
   223  			ResourceNamespace: "default",
   224  		}
   225  	}
   226  
   227  	expected := version1.IngressNginxConfig{
   228  		Upstreams: []version1.Upstream{
   229  			coffeeUpstream,
   230  			teaUpstream,
   231  		},
   232  		Servers: []version1.Server{
   233  			{
   234  				Name:         "cafe.example.com",
   235  				ServerTokens: "on",
   236  				Locations: []version1.Location{
   237  					{
   238  						Path:                "/coffee",
   239  						ServiceName:         "coffee-svc",
   240  						Upstream:            coffeeUpstream,
   241  						ProxyConnectTimeout: "60s",
   242  						ProxyReadTimeout:    "60s",
   243  						ProxySendTimeout:    "60s",
   244  						ClientMaxBodySize:   "1m",
   245  						ProxyBuffering:      true,
   246  						ProxySSLName:        "coffee-svc.default.svc",
   247  					},
   248  					{
   249  						Path:                "/tea",
   250  						ServiceName:         "tea-svc",
   251  						Upstream:            teaUpstream,
   252  						ProxyConnectTimeout: "60s",
   253  						ProxyReadTimeout:    "60s",
   254  						ProxySendTimeout:    "60s",
   255  						ClientMaxBodySize:   "1m",
   256  						ProxyBuffering:      true,
   257  						ProxySSLName:        "tea-svc.default.svc",
   258  					},
   259  				},
   260  				SSL:               true,
   261  				SSLCertificate:    "/etc/nginx/secrets/default-cafe-secret",
   262  				SSLCertificateKey: "/etc/nginx/secrets/default-cafe-secret",
   263  				StatusZone:        "cafe.example.com",
   264  				HSTSMaxAge:        2592000,
   265  				Ports:             []int{80},
   266  				SSLPorts:          []int{443},
   267  				SSLRedirect:       true,
   268  				HealthChecks:      make(map[string]version1.HealthCheck),
   269  			},
   270  		},
   271  		Ingress: version1.Ingress{
   272  			Name:      "cafe-ingress",
   273  			Namespace: "default",
   274  			Annotations: map[string]string{
   275  				"kubernetes.io/ingress.class": "nginx",
   276  			},
   277  		},
   278  	}
   279  	return expected
   280  }
   281  
   282  func createCafeIngressEx() IngressEx {
   283  	cafeIngress := networking.Ingress{
   284  		ObjectMeta: meta_v1.ObjectMeta{
   285  			Name:      "cafe-ingress",
   286  			Namespace: "default",
   287  			Annotations: map[string]string{
   288  				"kubernetes.io/ingress.class": "nginx",
   289  			},
   290  		},
   291  		Spec: networking.IngressSpec{
   292  			TLS: []networking.IngressTLS{
   293  				{
   294  					Hosts:      []string{"cafe.example.com"},
   295  					SecretName: "cafe-secret",
   296  				},
   297  			},
   298  			Rules: []networking.IngressRule{
   299  				{
   300  					Host: "cafe.example.com",
   301  					IngressRuleValue: networking.IngressRuleValue{
   302  						HTTP: &networking.HTTPIngressRuleValue{
   303  							Paths: []networking.HTTPIngressPath{
   304  								{
   305  									Path: "/coffee",
   306  									Backend: networking.IngressBackend{
   307  										ServiceName: "coffee-svc",
   308  										ServicePort: intstr.FromString("80"),
   309  									},
   310  								},
   311  								{
   312  									Path: "/tea",
   313  									Backend: networking.IngressBackend{
   314  										ServiceName: "tea-svc",
   315  										ServicePort: intstr.FromString("80"),
   316  									},
   317  								},
   318  							},
   319  						},
   320  					},
   321  				},
   322  			},
   323  		},
   324  	}
   325  	cafeIngressEx := IngressEx{
   326  		Ingress: &cafeIngress,
   327  		Endpoints: map[string][]string{
   328  			"coffee-svc80": {"10.0.0.1:80"},
   329  			"tea-svc80":    {"10.0.0.2:80"},
   330  		},
   331  		ExternalNameSvcs: map[string]bool{},
   332  		ValidHosts: map[string]bool{
   333  			"cafe.example.com": true,
   334  		},
   335  		SecretRefs: map[string]*secrets.SecretReference{
   336  			"cafe-secret": {
   337  				Secret: &v1.Secret{
   338  					Type: v1.SecretTypeTLS,
   339  				},
   340  				Path: "/etc/nginx/secrets/default-cafe-secret",
   341  			},
   342  		},
   343  	}
   344  	return cafeIngressEx
   345  }
   346  
   347  func TestGenerateNginxCfgForMergeableIngresses(t *testing.T) {
   348  	mergeableIngresses := createMergeableCafeIngress()
   349  
   350  	isPlus := false
   351  	expected := createExpectedConfigForMergeableCafeIngress(isPlus)
   352  
   353  	configParams := NewDefaultConfigParams()
   354  
   355  	masterApRes := AppProtectResources{}
   356  	result, warnings := generateNginxCfgForMergeableIngresses(mergeableIngresses, masterApRes, configParams, false, false, &StaticConfigParams{}, false)
   357  
   358  	if diff := cmp.Diff(expected, result); diff != "" {
   359  		t.Errorf("generateNginxCfgForMergeableIngresses() returned unexpected result (-want +got):\n%s", diff)
   360  	}
   361  	if len(warnings) != 0 {
   362  		t.Errorf("generateNginxCfgForMergeableIngresses() returned warnings: %v", warnings)
   363  	}
   364  }
   365  
   366  func TestGenerateNginxConfigForCrossNamespaceMergeableIngresses(t *testing.T) {
   367  	mergeableIngresses := createMergeableCafeIngress()
   368  	// change the namespaces of the minions to be coffee and tea
   369  	for i, m := range mergeableIngresses.Minions {
   370  		if strings.Contains(m.Ingress.Name, "coffee") {
   371  			mergeableIngresses.Minions[i].Ingress.Namespace = "coffee"
   372  		} else {
   373  			mergeableIngresses.Minions[i].Ingress.Namespace = "tea"
   374  		}
   375  	}
   376  
   377  	expected := createExpectedConfigForCrossNamespaceMergeableCafeIngress()
   378  	configParams := NewDefaultConfigParams()
   379  
   380  	emptyApResources := AppProtectResources{}
   381  	result, warnings := generateNginxCfgForMergeableIngresses(mergeableIngresses, emptyApResources, configParams, false, false, &StaticConfigParams{}, false)
   382  
   383  	if diff := cmp.Diff(expected, result); diff != "" {
   384  		t.Errorf("generateNginxCfgForMergeableIngresses() returned unexpected result (-want +got):\n%s", diff)
   385  	}
   386  	if len(warnings) != 0 {
   387  		t.Errorf("generateNginxCfgForMergeableIngresses() returned warnings: %v", warnings)
   388  	}
   389  }
   390  
   391  func TestGenerateNginxCfgForMergeableIngressesForJWT(t *testing.T) {
   392  	mergeableIngresses := createMergeableCafeIngress()
   393  	mergeableIngresses.Master.Ingress.Annotations["nginx.com/jwt-key"] = "cafe-jwk"
   394  	mergeableIngresses.Master.Ingress.Annotations["nginx.com/jwt-realm"] = "Cafe"
   395  	mergeableIngresses.Master.Ingress.Annotations["nginx.com/jwt-token"] = "$cookie_auth_token"
   396  	mergeableIngresses.Master.Ingress.Annotations["nginx.com/jwt-login-url"] = "https://login.example.com"
   397  	mergeableIngresses.Master.SecretRefs["cafe-jwk"] = &secrets.SecretReference{
   398  		Secret: &v1.Secret{
   399  			Type: secrets.SecretTypeJWK,
   400  		},
   401  		Path: "/etc/nginx/secrets/default-cafe-jwk",
   402  	}
   403  
   404  	mergeableIngresses.Minions[0].Ingress.Annotations["nginx.com/jwt-key"] = "coffee-jwk"
   405  	mergeableIngresses.Minions[0].Ingress.Annotations["nginx.com/jwt-realm"] = "Coffee"
   406  	mergeableIngresses.Minions[0].Ingress.Annotations["nginx.com/jwt-token"] = "$cookie_auth_token_coffee"
   407  	mergeableIngresses.Minions[0].Ingress.Annotations["nginx.com/jwt-login-url"] = "https://login.cofee.example.com"
   408  	mergeableIngresses.Minions[0].SecretRefs["coffee-jwk"] = &secrets.SecretReference{
   409  		Secret: &v1.Secret{
   410  			Type: secrets.SecretTypeJWK,
   411  		},
   412  		Path: "/etc/nginx/secrets/default-coffee-jwk",
   413  	}
   414  
   415  	isPlus := true
   416  
   417  	expected := createExpectedConfigForMergeableCafeIngress(isPlus)
   418  	expected.Servers[0].JWTAuth = &version1.JWTAuth{
   419  		Key:                  "/etc/nginx/secrets/default-cafe-jwk",
   420  		Realm:                "Cafe",
   421  		Token:                "$cookie_auth_token",
   422  		RedirectLocationName: "@login_url_default-cafe-ingress-master",
   423  	}
   424  	expected.Servers[0].Locations[0].JWTAuth = &version1.JWTAuth{
   425  		Key:                  "/etc/nginx/secrets/default-coffee-jwk",
   426  		Realm:                "Coffee",
   427  		Token:                "$cookie_auth_token_coffee",
   428  		RedirectLocationName: "@login_url_default-cafe-ingress-coffee-minion",
   429  	}
   430  	expected.Servers[0].JWTRedirectLocations = []version1.JWTRedirectLocation{
   431  		{
   432  			Name:     "@login_url_default-cafe-ingress-master",
   433  			LoginURL: "https://login.example.com",
   434  		},
   435  		{
   436  			Name:     "@login_url_default-cafe-ingress-coffee-minion",
   437  			LoginURL: "https://login.cofee.example.com",
   438  		},
   439  	}
   440  
   441  	minionJwtKeyFileNames := make(map[string]string)
   442  	minionJwtKeyFileNames[objectMetaToFileName(&mergeableIngresses.Minions[0].Ingress.ObjectMeta)] = "/etc/nginx/secrets/default-coffee-jwk"
   443  	configParams := NewDefaultConfigParams()
   444  
   445  	masterApRes := AppProtectResources{}
   446  	result, warnings := generateNginxCfgForMergeableIngresses(mergeableIngresses, masterApRes, configParams, isPlus, false, &StaticConfigParams{}, false)
   447  
   448  	if !reflect.DeepEqual(result.Servers[0].JWTAuth, expected.Servers[0].JWTAuth) {
   449  		t.Errorf("generateNginxCfgForMergeableIngresses returned \n%v,  but expected \n%v", result.Servers[0].JWTAuth, expected.Servers[0].JWTAuth)
   450  	}
   451  	if !reflect.DeepEqual(result.Servers[0].Locations[0].JWTAuth, expected.Servers[0].Locations[0].JWTAuth) {
   452  		t.Errorf("generateNginxCfgForMergeableIngresses returned \n%v,  but expected \n%v", result.Servers[0].Locations[0].JWTAuth, expected.Servers[0].Locations[0].JWTAuth)
   453  	}
   454  	if !reflect.DeepEqual(result.Servers[0].JWTRedirectLocations, expected.Servers[0].JWTRedirectLocations) {
   455  		t.Errorf("generateNginxCfgForMergeableIngresses returned \n%v,  but expected \n%v", result.Servers[0].JWTRedirectLocations, expected.Servers[0].JWTRedirectLocations)
   456  	}
   457  	if len(warnings) != 0 {
   458  		t.Errorf("generateNginxCfgForMergeableIngresses returned warnings: %v", warnings)
   459  	}
   460  }
   461  
   462  func createMergeableCafeIngress() *MergeableIngresses {
   463  	master := networking.Ingress{
   464  		ObjectMeta: meta_v1.ObjectMeta{
   465  			Name:      "cafe-ingress-master",
   466  			Namespace: "default",
   467  			Annotations: map[string]string{
   468  				"kubernetes.io/ingress.class":      "nginx",
   469  				"nginx.org/mergeable-ingress-type": "master",
   470  			},
   471  		},
   472  		Spec: networking.IngressSpec{
   473  			TLS: []networking.IngressTLS{
   474  				{
   475  					Hosts:      []string{"cafe.example.com"},
   476  					SecretName: "cafe-secret",
   477  				},
   478  			},
   479  			Rules: []networking.IngressRule{
   480  				{
   481  					Host: "cafe.example.com",
   482  					IngressRuleValue: networking.IngressRuleValue{
   483  						HTTP: &networking.HTTPIngressRuleValue{ // HTTP must not be nil for Master
   484  							Paths: []networking.HTTPIngressPath{},
   485  						},
   486  					},
   487  				},
   488  			},
   489  		},
   490  	}
   491  
   492  	coffeeMinion := networking.Ingress{
   493  		ObjectMeta: meta_v1.ObjectMeta{
   494  			Name:      "cafe-ingress-coffee-minion",
   495  			Namespace: "default",
   496  			Annotations: map[string]string{
   497  				"kubernetes.io/ingress.class":      "nginx",
   498  				"nginx.org/mergeable-ingress-type": "minion",
   499  			},
   500  		},
   501  		Spec: networking.IngressSpec{
   502  			Rules: []networking.IngressRule{
   503  				{
   504  					Host: "cafe.example.com",
   505  					IngressRuleValue: networking.IngressRuleValue{
   506  						HTTP: &networking.HTTPIngressRuleValue{
   507  							Paths: []networking.HTTPIngressPath{
   508  								{
   509  									Path: "/coffee",
   510  									Backend: networking.IngressBackend{
   511  										ServiceName: "coffee-svc",
   512  										ServicePort: intstr.FromString("80"),
   513  									},
   514  								},
   515  							},
   516  						},
   517  					},
   518  				},
   519  			},
   520  		},
   521  	}
   522  
   523  	teaMinion := networking.Ingress{
   524  		ObjectMeta: meta_v1.ObjectMeta{
   525  			Name:      "cafe-ingress-tea-minion",
   526  			Namespace: "default",
   527  			Annotations: map[string]string{
   528  				"kubernetes.io/ingress.class":      "nginx",
   529  				"nginx.org/mergeable-ingress-type": "minion",
   530  			},
   531  		},
   532  		Spec: networking.IngressSpec{
   533  			Rules: []networking.IngressRule{
   534  				{
   535  					Host: "cafe.example.com",
   536  					IngressRuleValue: networking.IngressRuleValue{
   537  						HTTP: &networking.HTTPIngressRuleValue{
   538  							Paths: []networking.HTTPIngressPath{
   539  								{
   540  									Path: "/tea",
   541  									Backend: networking.IngressBackend{
   542  										ServiceName: "tea-svc",
   543  										ServicePort: intstr.FromString("80"),
   544  									},
   545  								},
   546  							},
   547  						},
   548  					},
   549  				},
   550  			},
   551  		},
   552  	}
   553  
   554  	mergeableIngresses := &MergeableIngresses{
   555  		Master: &IngressEx{
   556  			Ingress: &master,
   557  			Endpoints: map[string][]string{
   558  				"coffee-svc80": {"10.0.0.1:80"},
   559  				"tea-svc80":    {"10.0.0.2:80"},
   560  			},
   561  			ValidHosts: map[string]bool{
   562  				"cafe.example.com": true,
   563  			},
   564  			SecretRefs: map[string]*secrets.SecretReference{
   565  				"cafe-secret": {
   566  					Secret: &v1.Secret{
   567  						Type: v1.SecretTypeTLS,
   568  					},
   569  					Path:  "/etc/nginx/secrets/default-cafe-secret",
   570  					Error: nil,
   571  				},
   572  			},
   573  		},
   574  		Minions: []*IngressEx{
   575  			{
   576  				Ingress: &coffeeMinion,
   577  				Endpoints: map[string][]string{
   578  					"coffee-svc80": {"10.0.0.1:80"},
   579  				},
   580  				ValidHosts: map[string]bool{
   581  					"cafe.example.com": true,
   582  				},
   583  				ValidMinionPaths: map[string]bool{
   584  					"/coffee": true,
   585  				},
   586  				SecretRefs: map[string]*secrets.SecretReference{},
   587  			},
   588  			{
   589  				Ingress: &teaMinion,
   590  				Endpoints: map[string][]string{
   591  					"tea-svc80": {"10.0.0.2:80"},
   592  				},
   593  				ValidHosts: map[string]bool{
   594  					"cafe.example.com": true,
   595  				},
   596  				ValidMinionPaths: map[string]bool{
   597  					"/tea": true,
   598  				},
   599  				SecretRefs: map[string]*secrets.SecretReference{},
   600  			}},
   601  	}
   602  
   603  	return mergeableIngresses
   604  }
   605  
   606  func createExpectedConfigForMergeableCafeIngress(isPlus bool) version1.IngressNginxConfig {
   607  	coffeeUpstream := version1.Upstream{
   608  		Name:             "default-cafe-ingress-coffee-minion-cafe.example.com-coffee-svc-80",
   609  		LBMethod:         "random two least_conn",
   610  		UpstreamZoneSize: "256k",
   611  		UpstreamServers: []version1.UpstreamServer{
   612  			{
   613  				Address:     "10.0.0.1",
   614  				Port:        "80",
   615  				MaxFails:    1,
   616  				MaxConns:    0,
   617  				FailTimeout: "10s",
   618  			},
   619  		},
   620  	}
   621  	if isPlus {
   622  		coffeeUpstream.UpstreamLabels = version1.UpstreamLabels{
   623  			Service:           "coffee-svc",
   624  			ResourceType:      "ingress",
   625  			ResourceName:      "cafe-ingress-coffee-minion",
   626  			ResourceNamespace: "default",
   627  		}
   628  	}
   629  
   630  	teaUpstream := version1.Upstream{
   631  		Name:             "default-cafe-ingress-tea-minion-cafe.example.com-tea-svc-80",
   632  		LBMethod:         "random two least_conn",
   633  		UpstreamZoneSize: "256k",
   634  		UpstreamServers: []version1.UpstreamServer{
   635  			{
   636  				Address:     "10.0.0.2",
   637  				Port:        "80",
   638  				MaxFails:    1,
   639  				MaxConns:    0,
   640  				FailTimeout: "10s",
   641  			},
   642  		},
   643  	}
   644  	if isPlus {
   645  		teaUpstream.UpstreamLabels = version1.UpstreamLabels{
   646  			Service:           "tea-svc",
   647  			ResourceType:      "ingress",
   648  			ResourceName:      "cafe-ingress-tea-minion",
   649  			ResourceNamespace: "default",
   650  		}
   651  	}
   652  
   653  	expected := version1.IngressNginxConfig{
   654  		Upstreams: []version1.Upstream{
   655  			coffeeUpstream,
   656  			teaUpstream,
   657  		},
   658  		Servers: []version1.Server{
   659  			{
   660  				Name:         "cafe.example.com",
   661  				ServerTokens: "on",
   662  				Locations: []version1.Location{
   663  					{
   664  						Path:                "/coffee",
   665  						ServiceName:         "coffee-svc",
   666  						Upstream:            coffeeUpstream,
   667  						ProxyConnectTimeout: "60s",
   668  						ProxyReadTimeout:    "60s",
   669  						ProxySendTimeout:    "60s",
   670  						ClientMaxBodySize:   "1m",
   671  						ProxyBuffering:      true,
   672  						MinionIngress: &version1.Ingress{
   673  							Name:      "cafe-ingress-coffee-minion",
   674  							Namespace: "default",
   675  							Annotations: map[string]string{
   676  								"kubernetes.io/ingress.class":      "nginx",
   677  								"nginx.org/mergeable-ingress-type": "minion",
   678  							},
   679  						},
   680  						ProxySSLName: "coffee-svc.default.svc",
   681  					},
   682  					{
   683  						Path:                "/tea",
   684  						ServiceName:         "tea-svc",
   685  						Upstream:            teaUpstream,
   686  						ProxyConnectTimeout: "60s",
   687  						ProxyReadTimeout:    "60s",
   688  						ProxySendTimeout:    "60s",
   689  						ClientMaxBodySize:   "1m",
   690  						ProxyBuffering:      true,
   691  						MinionIngress: &version1.Ingress{
   692  							Name:      "cafe-ingress-tea-minion",
   693  							Namespace: "default",
   694  							Annotations: map[string]string{
   695  								"kubernetes.io/ingress.class":      "nginx",
   696  								"nginx.org/mergeable-ingress-type": "minion",
   697  							},
   698  						},
   699  						ProxySSLName: "tea-svc.default.svc",
   700  					},
   701  				},
   702  				SSL:               true,
   703  				SSLCertificate:    "/etc/nginx/secrets/default-cafe-secret",
   704  				SSLCertificateKey: "/etc/nginx/secrets/default-cafe-secret",
   705  				StatusZone:        "cafe.example.com",
   706  				HSTSMaxAge:        2592000,
   707  				Ports:             []int{80},
   708  				SSLPorts:          []int{443},
   709  				SSLRedirect:       true,
   710  				HealthChecks:      make(map[string]version1.HealthCheck),
   711  			},
   712  		},
   713  		Ingress: version1.Ingress{
   714  			Name:      "cafe-ingress-master",
   715  			Namespace: "default",
   716  			Annotations: map[string]string{
   717  				"kubernetes.io/ingress.class":      "nginx",
   718  				"nginx.org/mergeable-ingress-type": "master",
   719  			},
   720  		},
   721  	}
   722  
   723  	return expected
   724  }
   725  
   726  func createExpectedConfigForCrossNamespaceMergeableCafeIngress() version1.IngressNginxConfig {
   727  	coffeeUpstream := version1.Upstream{
   728  		Name:             "coffee-cafe-ingress-coffee-minion-cafe.example.com-coffee-svc-80",
   729  		LBMethod:         "random two least_conn",
   730  		UpstreamZoneSize: "256k",
   731  		UpstreamServers: []version1.UpstreamServer{
   732  			{
   733  				Address:     "10.0.0.1",
   734  				Port:        "80",
   735  				MaxFails:    1,
   736  				MaxConns:    0,
   737  				FailTimeout: "10s",
   738  			},
   739  		},
   740  	}
   741  	teaUpstream := version1.Upstream{
   742  		Name:             "tea-cafe-ingress-tea-minion-cafe.example.com-tea-svc-80",
   743  		LBMethod:         "random two least_conn",
   744  		UpstreamZoneSize: "256k",
   745  		UpstreamServers: []version1.UpstreamServer{
   746  			{
   747  				Address:     "10.0.0.2",
   748  				Port:        "80",
   749  				MaxFails:    1,
   750  				MaxConns:    0,
   751  				FailTimeout: "10s",
   752  			},
   753  		},
   754  	}
   755  	expected := version1.IngressNginxConfig{
   756  		Upstreams: []version1.Upstream{
   757  			coffeeUpstream,
   758  			teaUpstream,
   759  		},
   760  		Servers: []version1.Server{
   761  			{
   762  				Name:         "cafe.example.com",
   763  				ServerTokens: "on",
   764  				Locations: []version1.Location{
   765  					{
   766  						Path:                "/coffee",
   767  						ServiceName:         "coffee-svc",
   768  						Upstream:            coffeeUpstream,
   769  						ProxyConnectTimeout: "60s",
   770  						ProxyReadTimeout:    "60s",
   771  						ProxySendTimeout:    "60s",
   772  						ClientMaxBodySize:   "1m",
   773  						ProxyBuffering:      true,
   774  						MinionIngress: &version1.Ingress{
   775  							Name:      "cafe-ingress-coffee-minion",
   776  							Namespace: "coffee",
   777  							Annotations: map[string]string{
   778  								"kubernetes.io/ingress.class":      "nginx",
   779  								"nginx.org/mergeable-ingress-type": "minion",
   780  							},
   781  						},
   782  						ProxySSLName: "coffee-svc.coffee.svc",
   783  					},
   784  					{
   785  						Path:                "/tea",
   786  						ServiceName:         "tea-svc",
   787  						Upstream:            teaUpstream,
   788  						ProxyConnectTimeout: "60s",
   789  						ProxyReadTimeout:    "60s",
   790  						ProxySendTimeout:    "60s",
   791  						ClientMaxBodySize:   "1m",
   792  						ProxyBuffering:      true,
   793  						MinionIngress: &version1.Ingress{
   794  							Name:      "cafe-ingress-tea-minion",
   795  							Namespace: "tea",
   796  							Annotations: map[string]string{
   797  								"kubernetes.io/ingress.class":      "nginx",
   798  								"nginx.org/mergeable-ingress-type": "minion",
   799  							},
   800  						},
   801  						ProxySSLName: "tea-svc.tea.svc",
   802  					},
   803  				},
   804  				SSL:               true,
   805  				SSLCertificate:    "/etc/nginx/secrets/default-cafe-secret",
   806  				SSLCertificateKey: "/etc/nginx/secrets/default-cafe-secret",
   807  				StatusZone:        "cafe.example.com",
   808  				HSTSMaxAge:        2592000,
   809  				Ports:             []int{80},
   810  				SSLPorts:          []int{443},
   811  				SSLRedirect:       true,
   812  				HealthChecks:      make(map[string]version1.HealthCheck),
   813  			},
   814  		},
   815  		Ingress: version1.Ingress{
   816  			Name:      "cafe-ingress-master",
   817  			Namespace: "default",
   818  			Annotations: map[string]string{
   819  				"kubernetes.io/ingress.class":      "nginx",
   820  				"nginx.org/mergeable-ingress-type": "master",
   821  			},
   822  		},
   823  	}
   824  
   825  	return expected
   826  }
   827  
   828  func TestGenerateNginxCfgForSpiffe(t *testing.T) {
   829  	cafeIngressEx := createCafeIngressEx()
   830  	configParams := NewDefaultConfigParams()
   831  
   832  	isPlus := false
   833  
   834  	expected := createExpectedConfigForCafeIngressEx(isPlus)
   835  	expected.SpiffeClientCerts = true
   836  	for i := range expected.Servers[0].Locations {
   837  		expected.Servers[0].Locations[i].SSL = true
   838  	}
   839  
   840  	apResources := AppProtectResources{}
   841  	result, warnings := generateNginxCfg(&cafeIngressEx, apResources, false, configParams, false, false,
   842  		&StaticConfigParams{NginxServiceMesh: true}, false)
   843  
   844  	if diff := cmp.Diff(expected, result); diff != "" {
   845  		t.Errorf("generateNginxCfg() returned unexpected result (-want +got):\n%s", diff)
   846  	}
   847  	if len(warnings) != 0 {
   848  		t.Errorf("generateNginxCfg() returned warnings: %v", warnings)
   849  	}
   850  }
   851  
   852  func TestGenerateNginxCfgForInternalRoute(t *testing.T) {
   853  	internalRouteAnnotation := "nsm.nginx.com/internal-route"
   854  	cafeIngressEx := createCafeIngressEx()
   855  	cafeIngressEx.Ingress.Annotations[internalRouteAnnotation] = "true"
   856  	configParams := NewDefaultConfigParams()
   857  
   858  	isPlus := false
   859  
   860  	expected := createExpectedConfigForCafeIngressEx(isPlus)
   861  	expected.Servers[0].SpiffeCerts = true
   862  	expected.Ingress.Annotations[internalRouteAnnotation] = "true"
   863  
   864  	apResources := AppProtectResources{}
   865  	result, warnings := generateNginxCfg(&cafeIngressEx, apResources, false, configParams, false, false,
   866  		&StaticConfigParams{NginxServiceMesh: true, EnableInternalRoutes: true}, false)
   867  
   868  	if diff := cmp.Diff(expected, result); diff != "" {
   869  		t.Errorf("generateNginxCfg() returned unexpected result (-want +got):\n%s", diff)
   870  	}
   871  	if len(warnings) != 0 {
   872  		t.Errorf("generateNginxCfg() returned warnings: %v", warnings)
   873  	}
   874  }
   875  
   876  func TestIsSSLEnabled(t *testing.T) {
   877  	type testCase struct {
   878  		IsSSLService,
   879  		SpiffeServerCerts,
   880  		NginxServiceMesh,
   881  		Expected bool
   882  	}
   883  	var testCases = []testCase{
   884  		{
   885  			IsSSLService:      false,
   886  			SpiffeServerCerts: false,
   887  			NginxServiceMesh:  false,
   888  			Expected:          false,
   889  		},
   890  		{
   891  			IsSSLService:      false,
   892  			SpiffeServerCerts: true,
   893  			NginxServiceMesh:  true,
   894  			Expected:          false,
   895  		},
   896  		{
   897  			IsSSLService:      false,
   898  			SpiffeServerCerts: false,
   899  			NginxServiceMesh:  true,
   900  			Expected:          true,
   901  		},
   902  		{
   903  			IsSSLService:      false,
   904  			SpiffeServerCerts: true,
   905  			NginxServiceMesh:  false,
   906  			Expected:          false,
   907  		},
   908  		{
   909  			IsSSLService:      true,
   910  			SpiffeServerCerts: true,
   911  			NginxServiceMesh:  true,
   912  			Expected:          true,
   913  		},
   914  		{
   915  			IsSSLService:      true,
   916  			SpiffeServerCerts: false,
   917  			NginxServiceMesh:  true,
   918  			Expected:          true,
   919  		},
   920  		{
   921  			IsSSLService:      true,
   922  			SpiffeServerCerts: true,
   923  			NginxServiceMesh:  false,
   924  			Expected:          true,
   925  		},
   926  		{
   927  			IsSSLService:      true,
   928  			SpiffeServerCerts: false,
   929  			NginxServiceMesh:  false,
   930  			Expected:          true,
   931  		},
   932  	}
   933  	for i, tc := range testCases {
   934  		actual := isSSLEnabled(tc.IsSSLService, ConfigParams{SpiffeServerCerts: tc.SpiffeServerCerts}, &StaticConfigParams{NginxServiceMesh: tc.NginxServiceMesh})
   935  		if actual != tc.Expected {
   936  			t.Errorf("isSSLEnabled returned %v but expected %v for the case %v", actual, tc.Expected, i)
   937  		}
   938  	}
   939  }
   940  
   941  func TestAddSSLConfig(t *testing.T) {
   942  	tests := []struct {
   943  		host              string
   944  		tls               []networking.IngressTLS
   945  		secretRefs        map[string]*secrets.SecretReference
   946  		isWildcardEnabled bool
   947  		expectedServer    version1.Server
   948  		expectedWarnings  Warnings
   949  		msg               string
   950  	}{
   951  		{
   952  			host: "some.example.com",
   953  			tls: []networking.IngressTLS{
   954  				{
   955  					Hosts:      []string{"cafe.example.com"},
   956  					SecretName: "cafe-secret",
   957  				},
   958  			},
   959  			secretRefs: map[string]*secrets.SecretReference{
   960  				"cafe-secret": {
   961  					Secret: &v1.Secret{
   962  						Type: v1.SecretTypeTLS,
   963  					},
   964  					Path: "/etc/nginx/secrets/default-cafe-secret",
   965  				},
   966  			},
   967  			isWildcardEnabled: false,
   968  			expectedServer:    version1.Server{},
   969  			expectedWarnings:  Warnings{},
   970  			msg:               "TLS termination for different host",
   971  		},
   972  		{
   973  			host: "cafe.example.com",
   974  			tls: []networking.IngressTLS{
   975  				{
   976  					Hosts:      []string{"cafe.example.com"},
   977  					SecretName: "cafe-secret",
   978  				},
   979  			},
   980  			secretRefs: map[string]*secrets.SecretReference{
   981  				"cafe-secret": {
   982  					Secret: &v1.Secret{
   983  						Type: v1.SecretTypeTLS,
   984  					},
   985  					Path: "/etc/nginx/secrets/default-cafe-secret",
   986  				},
   987  			},
   988  			isWildcardEnabled: false,
   989  			expectedServer: version1.Server{
   990  				SSL:               true,
   991  				SSLCertificate:    "/etc/nginx/secrets/default-cafe-secret",
   992  				SSLCertificateKey: "/etc/nginx/secrets/default-cafe-secret",
   993  			},
   994  			expectedWarnings: Warnings{},
   995  			msg:              "TLS termination",
   996  		},
   997  		{
   998  			host: "cafe.example.com",
   999  			tls: []networking.IngressTLS{
  1000  				{
  1001  					Hosts:      []string{"cafe.example.com"},
  1002  					SecretName: "cafe-secret",
  1003  				},
  1004  			},
  1005  			secretRefs: map[string]*secrets.SecretReference{
  1006  				"cafe-secret": {
  1007  					Secret: &v1.Secret{
  1008  						Type: v1.SecretTypeTLS,
  1009  					},
  1010  					Error: errors.New("invalid secret"),
  1011  				},
  1012  			},
  1013  			isWildcardEnabled: false,
  1014  			expectedServer: version1.Server{
  1015  				SSL:                true,
  1016  				SSLRejectHandshake: true,
  1017  			},
  1018  			expectedWarnings: Warnings{
  1019  				nil: {
  1020  					"TLS secret cafe-secret is invalid: invalid secret",
  1021  				},
  1022  			},
  1023  			msg: "invalid secret",
  1024  		},
  1025  		{
  1026  			host: "cafe.example.com",
  1027  			tls: []networking.IngressTLS{
  1028  				{
  1029  					Hosts:      []string{"cafe.example.com"},
  1030  					SecretName: "cafe-secret",
  1031  				},
  1032  			},
  1033  			secretRefs: map[string]*secrets.SecretReference{
  1034  				"cafe-secret": {
  1035  					Secret: &v1.Secret{
  1036  						Type: secrets.SecretTypeCA,
  1037  					},
  1038  					Path: "/etc/nginx/secrets/default-cafe-secret",
  1039  				},
  1040  			},
  1041  			isWildcardEnabled: false,
  1042  			expectedServer: version1.Server{
  1043  				SSL:                true,
  1044  				SSLRejectHandshake: true,
  1045  			},
  1046  			expectedWarnings: Warnings{
  1047  				nil: {
  1048  					"TLS secret cafe-secret is of a wrong type 'nginx.org/ca', must be 'kubernetes.io/tls'",
  1049  				},
  1050  			},
  1051  			msg: "secret of wrong type without error",
  1052  		},
  1053  		{
  1054  			host: "cafe.example.com",
  1055  			tls: []networking.IngressTLS{
  1056  				{
  1057  					Hosts:      []string{"cafe.example.com"},
  1058  					SecretName: "cafe-secret",
  1059  				},
  1060  			},
  1061  			secretRefs: map[string]*secrets.SecretReference{
  1062  				"cafe-secret": {
  1063  					Secret: &v1.Secret{
  1064  						Type: secrets.SecretTypeCA,
  1065  					},
  1066  					Path:  "",
  1067  					Error: errors.New("CA secret must have the data field ca.crt"),
  1068  				},
  1069  			},
  1070  			isWildcardEnabled: false,
  1071  			expectedServer: version1.Server{
  1072  				SSL:                true,
  1073  				SSLRejectHandshake: true,
  1074  			},
  1075  			expectedWarnings: Warnings{
  1076  				nil: {
  1077  					"TLS secret cafe-secret is of a wrong type 'nginx.org/ca', must be 'kubernetes.io/tls'",
  1078  				},
  1079  			},
  1080  			msg: "secret of wrong type with error",
  1081  		},
  1082  		{
  1083  			host: "cafe.example.com",
  1084  			tls: []networking.IngressTLS{
  1085  				{
  1086  					Hosts:      []string{"cafe.example.com"},
  1087  					SecretName: "",
  1088  				},
  1089  			},
  1090  			isWildcardEnabled: true,
  1091  			expectedServer: version1.Server{
  1092  				SSL:               true,
  1093  				SSLCertificate:    pemFileNameForWildcardTLSSecret,
  1094  				SSLCertificateKey: pemFileNameForWildcardTLSSecret,
  1095  			},
  1096  			expectedWarnings: Warnings{},
  1097  			msg:              "no secret name with wildcard enabled",
  1098  		},
  1099  		{
  1100  			host: "cafe.example.com",
  1101  			tls: []networking.IngressTLS{
  1102  				{
  1103  					Hosts:      []string{"cafe.example.com"},
  1104  					SecretName: "",
  1105  				},
  1106  			},
  1107  			isWildcardEnabled: false,
  1108  			expectedServer: version1.Server{
  1109  				SSL:                true,
  1110  				SSLRejectHandshake: true,
  1111  			},
  1112  			expectedWarnings: Warnings{
  1113  				nil: {
  1114  					"TLS termination for host 'cafe.example.com' requires specifying a TLS secret or configuring a global wildcard TLS secret",
  1115  				},
  1116  			},
  1117  			msg: "no secret name with wildcard disabled",
  1118  		},
  1119  	}
  1120  
  1121  	for _, test := range tests {
  1122  		var server version1.Server
  1123  
  1124  		// it is ok to use nil as the owner
  1125  		warnings := addSSLConfig(&server, nil, test.host, test.tls, test.secretRefs, test.isWildcardEnabled)
  1126  
  1127  		if diff := cmp.Diff(test.expectedServer, server); diff != "" {
  1128  			t.Errorf("addSSLConfig() '%s' mismatch (-want +got):\n%s", test.msg, diff)
  1129  		}
  1130  		if !reflect.DeepEqual(test.expectedWarnings, warnings) {
  1131  			t.Errorf("addSSLConfig() returned %v but expected %v for the case of %s", warnings, test.expectedWarnings, test.msg)
  1132  		}
  1133  	}
  1134  }
  1135  
  1136  func TestGenerateJWTConfig(t *testing.T) {
  1137  	tests := []struct {
  1138  		secretRefs               map[string]*secrets.SecretReference
  1139  		cfgParams                *ConfigParams
  1140  		redirectLocationName     string
  1141  		expectedJWTAuth          *version1.JWTAuth
  1142  		expectedRedirectLocation *version1.JWTRedirectLocation
  1143  		expectedWarnings         Warnings
  1144  		msg                      string
  1145  	}{
  1146  		{
  1147  			secretRefs: map[string]*secrets.SecretReference{
  1148  				"cafe-jwk": {
  1149  					Secret: &v1.Secret{
  1150  						Type: secrets.SecretTypeJWK,
  1151  					},
  1152  					Path: "/etc/nginx/secrets/default-cafe-jwk",
  1153  				},
  1154  			},
  1155  			cfgParams: &ConfigParams{
  1156  				JWTKey:   "cafe-jwk",
  1157  				JWTRealm: "cafe",
  1158  				JWTToken: "$http_token",
  1159  			},
  1160  			redirectLocationName: "@loc",
  1161  			expectedJWTAuth: &version1.JWTAuth{
  1162  				Key:   "/etc/nginx/secrets/default-cafe-jwk",
  1163  				Realm: "cafe",
  1164  				Token: "$http_token",
  1165  			},
  1166  			expectedRedirectLocation: nil,
  1167  			expectedWarnings:         Warnings{},
  1168  			msg:                      "normal case",
  1169  		},
  1170  		{
  1171  			secretRefs: map[string]*secrets.SecretReference{
  1172  				"cafe-jwk": {
  1173  					Secret: &v1.Secret{
  1174  						Type: secrets.SecretTypeJWK,
  1175  					},
  1176  					Path: "/etc/nginx/secrets/default-cafe-jwk",
  1177  				},
  1178  			},
  1179  			cfgParams: &ConfigParams{
  1180  				JWTKey:      "cafe-jwk",
  1181  				JWTRealm:    "cafe",
  1182  				JWTToken:    "$http_token",
  1183  				JWTLoginURL: "http://cafe.example.com/login",
  1184  			},
  1185  			redirectLocationName: "@loc",
  1186  			expectedJWTAuth: &version1.JWTAuth{
  1187  				Key:                  "/etc/nginx/secrets/default-cafe-jwk",
  1188  				Realm:                "cafe",
  1189  				Token:                "$http_token",
  1190  				RedirectLocationName: "@loc",
  1191  			},
  1192  			expectedRedirectLocation: &version1.JWTRedirectLocation{
  1193  				Name:     "@loc",
  1194  				LoginURL: "http://cafe.example.com/login",
  1195  			},
  1196  			expectedWarnings: Warnings{},
  1197  			msg:              "normal case with login url",
  1198  		},
  1199  		{
  1200  			secretRefs: map[string]*secrets.SecretReference{
  1201  				"cafe-jwk": {
  1202  					Secret: &v1.Secret{
  1203  						Type: secrets.SecretTypeJWK,
  1204  					},
  1205  					Path:  "/etc/nginx/secrets/default-cafe-jwk",
  1206  					Error: errors.New("invalid secret"),
  1207  				},
  1208  			},
  1209  			cfgParams: &ConfigParams{
  1210  				JWTKey:   "cafe-jwk",
  1211  				JWTRealm: "cafe",
  1212  				JWTToken: "$http_token",
  1213  			},
  1214  			redirectLocationName: "@loc",
  1215  			expectedJWTAuth: &version1.JWTAuth{
  1216  				Key:   "/etc/nginx/secrets/default-cafe-jwk",
  1217  				Realm: "cafe",
  1218  				Token: "$http_token",
  1219  			},
  1220  			expectedRedirectLocation: nil,
  1221  			expectedWarnings: Warnings{
  1222  				nil: {
  1223  					"JWK secret cafe-jwk is invalid: invalid secret",
  1224  				},
  1225  			},
  1226  			msg: "invalid secret",
  1227  		},
  1228  		{
  1229  			secretRefs: map[string]*secrets.SecretReference{
  1230  				"cafe-jwk": {
  1231  					Secret: &v1.Secret{
  1232  						Type: secrets.SecretTypeCA,
  1233  					},
  1234  					Path: "/etc/nginx/secrets/default-cafe-jwk",
  1235  				},
  1236  			},
  1237  			cfgParams: &ConfigParams{
  1238  				JWTKey:   "cafe-jwk",
  1239  				JWTRealm: "cafe",
  1240  				JWTToken: "$http_token",
  1241  			},
  1242  			redirectLocationName: "@loc",
  1243  			expectedJWTAuth: &version1.JWTAuth{
  1244  				Key:   "/etc/nginx/secrets/default-cafe-jwk",
  1245  				Realm: "cafe",
  1246  				Token: "$http_token",
  1247  			},
  1248  			expectedRedirectLocation: nil,
  1249  			expectedWarnings: Warnings{
  1250  				nil: {
  1251  					"JWK secret cafe-jwk is of a wrong type 'nginx.org/ca', must be 'nginx.org/jwk'",
  1252  				},
  1253  			},
  1254  			msg: "secret of wrong type without error",
  1255  		},
  1256  		{
  1257  			secretRefs: map[string]*secrets.SecretReference{
  1258  				"cafe-jwk": {
  1259  					Secret: &v1.Secret{
  1260  						Type: secrets.SecretTypeCA,
  1261  					},
  1262  					Path:  "",
  1263  					Error: errors.New("CA secret must have the data field ca.crt"),
  1264  				},
  1265  			},
  1266  			cfgParams: &ConfigParams{
  1267  				JWTKey:   "cafe-jwk",
  1268  				JWTRealm: "cafe",
  1269  				JWTToken: "$http_token",
  1270  			},
  1271  			redirectLocationName: "@loc",
  1272  			expectedJWTAuth: &version1.JWTAuth{
  1273  				Key:   "",
  1274  				Realm: "cafe",
  1275  				Token: "$http_token",
  1276  			},
  1277  			expectedRedirectLocation: nil,
  1278  			expectedWarnings: Warnings{
  1279  				nil: {
  1280  					"JWK secret cafe-jwk is of a wrong type 'nginx.org/ca', must be 'nginx.org/jwk'",
  1281  				},
  1282  			},
  1283  			msg: "secret of wrong type with error",
  1284  		},
  1285  	}
  1286  
  1287  	for _, test := range tests {
  1288  		jwtAuth, redirectLocation, warnings := generateJWTConfig(nil, test.secretRefs, test.cfgParams, test.redirectLocationName)
  1289  
  1290  		if diff := cmp.Diff(test.expectedJWTAuth, jwtAuth); diff != "" {
  1291  			t.Errorf("generateJWTConfig() '%s' mismatch for jwtAuth (-want +got):\n%s", test.msg, diff)
  1292  		}
  1293  		if diff := cmp.Diff(test.expectedRedirectLocation, redirectLocation); diff != "" {
  1294  			t.Errorf("generateJWTConfig() '%s' mismatch for redirectLocation (-want +got):\n%s", test.msg, diff)
  1295  		}
  1296  		if !reflect.DeepEqual(test.expectedWarnings, warnings) {
  1297  			t.Errorf("generateJWTConfig() returned %v but expected %v for the case of %s", warnings, test.expectedWarnings, test.msg)
  1298  		}
  1299  	}
  1300  }
  1301  
  1302  func TestGenerateNginxCfgForAppProtect(t *testing.T) {
  1303  	cafeIngressEx := createCafeIngressEx()
  1304  	cafeIngressEx.Ingress.Annotations["appprotect.f5.com/app-protect-enable"] = "True"
  1305  	cafeIngressEx.Ingress.Annotations["appprotect.f5.com/app-protect-security-log-enable"] = "True"
  1306  	cafeIngressEx.AppProtectPolicy = &unstructured.Unstructured{
  1307  		Object: map[string]interface{}{
  1308  			"metadata": map[string]interface{}{
  1309  				"namespace": "default",
  1310  				"name":      "dataguard-alarm",
  1311  			},
  1312  		},
  1313  	}
  1314  	cafeIngressEx.AppProtectLogs = []AppProtectLog{
  1315  		{
  1316  			LogConf: &unstructured.Unstructured{
  1317  				Object: map[string]interface{}{
  1318  					"metadata": map[string]interface{}{
  1319  						"namespace": "default",
  1320  						"name":      "logconf",
  1321  					},
  1322  				},
  1323  			},
  1324  		},
  1325  	}
  1326  
  1327  	configParams := NewDefaultConfigParams()
  1328  	apRes := AppProtectResources{
  1329  		AppProtectPolicy:   "/etc/nginx/waf/nac-policies/default_dataguard-alarm",
  1330  		AppProtectLogconfs: []string{"/etc/nginx/waf/nac-logconfs/default_logconf syslog:server=127.0.0.1:514"},
  1331  	}
  1332  	staticCfgParams := &StaticConfigParams{
  1333  		MainAppProtectLoadModule: true,
  1334  	}
  1335  
  1336  	isPlus := true
  1337  
  1338  	expected := createExpectedConfigForCafeIngressEx(isPlus)
  1339  	expected.Servers[0].AppProtectEnable = "on"
  1340  	expected.Servers[0].AppProtectPolicy = "/etc/nginx/waf/nac-policies/default_dataguard-alarm"
  1341  	expected.Servers[0].AppProtectLogConfs = []string{"/etc/nginx/waf/nac-logconfs/default_logconf syslog:server=127.0.0.1:514"}
  1342  	expected.Servers[0].AppProtectLogEnable = "on"
  1343  	expected.Ingress.Annotations = cafeIngressEx.Ingress.Annotations
  1344  
  1345  	result, warnings := generateNginxCfg(&cafeIngressEx, apRes, false, configParams, isPlus, false, staticCfgParams, false)
  1346  	if diff := cmp.Diff(expected, result); diff != "" {
  1347  		t.Errorf("generateNginxCfg() returned unexpected result (-want +got):\n%s", diff)
  1348  	}
  1349  	if len(warnings) != 0 {
  1350  		t.Errorf("generateNginxCfg() returned warnings: %v", warnings)
  1351  	}
  1352  }
  1353  
  1354  func TestGenerateNginxCfgForMergeableIngressesForAppProtect(t *testing.T) {
  1355  	mergeableIngresses := createMergeableCafeIngress()
  1356  	mergeableIngresses.Master.Ingress.Annotations["appprotect.f5.com/app-protect-enable"] = "True"
  1357  	mergeableIngresses.Master.Ingress.Annotations["appprotect.f5.com/app-protect-security-log-enable"] = "True"
  1358  	mergeableIngresses.Master.AppProtectPolicy = &unstructured.Unstructured{
  1359  		Object: map[string]interface{}{
  1360  			"metadata": map[string]interface{}{
  1361  				"namespace": "default",
  1362  				"name":      "dataguard-alarm",
  1363  			},
  1364  		},
  1365  	}
  1366  	mergeableIngresses.Master.AppProtectLogs = []AppProtectLog{
  1367  		{
  1368  			LogConf: &unstructured.Unstructured{
  1369  				Object: map[string]interface{}{
  1370  					"metadata": map[string]interface{}{
  1371  						"namespace": "default",
  1372  						"name":      "logconf",
  1373  					},
  1374  				},
  1375  			},
  1376  		},
  1377  	}
  1378  
  1379  	configParams := NewDefaultConfigParams()
  1380  	apRes := AppProtectResources{
  1381  		AppProtectPolicy:   "/etc/nginx/waf/nac-policies/default_dataguard-alarm",
  1382  		AppProtectLogconfs: []string{"/etc/nginx/waf/nac-logconfs/default_logconf syslog:server=127.0.0.1:514"},
  1383  	}
  1384  	staticCfgParams := &StaticConfigParams{
  1385  		MainAppProtectLoadModule: true,
  1386  	}
  1387  
  1388  	isPlus := true
  1389  
  1390  	expected := createExpectedConfigForMergeableCafeIngress(isPlus)
  1391  	expected.Servers[0].AppProtectEnable = "on"
  1392  	expected.Servers[0].AppProtectPolicy = "/etc/nginx/waf/nac-policies/default_dataguard-alarm"
  1393  	expected.Servers[0].AppProtectLogConfs = []string{"/etc/nginx/waf/nac-logconfs/default_logconf syslog:server=127.0.0.1:514"}
  1394  	expected.Servers[0].AppProtectLogEnable = "on"
  1395  	expected.Ingress.Annotations = mergeableIngresses.Master.Ingress.Annotations
  1396  
  1397  	result, warnings := generateNginxCfgForMergeableIngresses(mergeableIngresses, apRes, configParams, isPlus, false, staticCfgParams, false)
  1398  	if diff := cmp.Diff(expected, result); diff != "" {
  1399  		t.Errorf("generateNginxCfgForMergeableIngresses() returned unexpected result (-want +got):\n%s", diff)
  1400  	}
  1401  	if len(warnings) != 0 {
  1402  		t.Errorf("generateNginxCfgForMergeableIngresses() returned warnings: %v", warnings)
  1403  	}
  1404  }