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

     1  package k8s
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/google/go-cmp/cmp"
     9  	conf_v1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1"
    10  	conf_v1alpha1 "github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/v1alpha1"
    11  	"github.com/nginxinc/kubernetes-ingress/pkg/apis/configuration/validation"
    12  	networking "k8s.io/api/networking/v1beta1"
    13  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  )
    15  
    16  func createTestConfiguration() *Configuration {
    17  	lbc := LoadBalancerController{
    18  		ingressClass:        "nginx",
    19  		useIngressClassOnly: true,
    20  	}
    21  	isPlus := false
    22  	appProtectEnabled := false
    23  	internalRoutesEnabled := false
    24  	isTLSPassthroughEnabled := true
    25  	snippetsEnabled := true
    26  	return NewConfiguration(
    27  		lbc.HasCorrectIngressClass,
    28  		isPlus,
    29  		appProtectEnabled,
    30  		internalRoutesEnabled,
    31  		validation.NewVirtualServerValidator(isTLSPassthroughEnabled),
    32  		validation.NewGlobalConfigurationValidator(map[int]bool{
    33  			80:  true,
    34  			443: true,
    35  		}),
    36  		validation.NewTransportServerValidator(isTLSPassthroughEnabled, snippetsEnabled, isPlus),
    37  		isTLSPassthroughEnabled,
    38  		snippetsEnabled,
    39  	)
    40  }
    41  
    42  func TestAddIngressForRegularIngress(t *testing.T) {
    43  	configuration := createTestConfiguration()
    44  
    45  	// no problems are expected for all cases
    46  	var expectedProblems []ConfigurationProblem
    47  
    48  	// Add a new Ingress
    49  
    50  	ing := createTestIngress("ingress", "foo.example.com")
    51  	expectedChanges := []ResourceChange{
    52  		{
    53  			Op: AddOrUpdate,
    54  			Resource: &IngressConfiguration{
    55  				Ingress: ing,
    56  				ValidHosts: map[string]bool{
    57  					"foo.example.com": true,
    58  				},
    59  				ChildWarnings: map[string][]string{},
    60  			},
    61  		},
    62  	}
    63  
    64  	changes, problems := configuration.AddOrUpdateIngress(ing)
    65  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
    66  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
    67  	}
    68  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
    69  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
    70  	}
    71  
    72  	// Update the Ingress
    73  
    74  	updatedIng := ing.DeepCopy()
    75  	updatedIng.Annotations["nginx.org/max_fails"] = "1"
    76  
    77  	expectedChanges = []ResourceChange{
    78  		{
    79  			Op: AddOrUpdate,
    80  			Resource: &IngressConfiguration{
    81  				Ingress: updatedIng,
    82  				ValidHosts: map[string]bool{
    83  					"foo.example.com": true,
    84  				},
    85  				ChildWarnings: map[string][]string{},
    86  			},
    87  		},
    88  	}
    89  
    90  	changes, problems = configuration.AddOrUpdateIngress(updatedIng)
    91  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
    92  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
    93  	}
    94  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
    95  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
    96  	}
    97  
    98  	// Make the Ingress invalid
    99  
   100  	invalidIng := updatedIng.DeepCopy()
   101  	invalidIng.Generation++
   102  	invalidIng.Spec.Rules = []networking.IngressRule{
   103  		{
   104  			Host:             "foo.example.com",
   105  			IngressRuleValue: networking.IngressRuleValue{},
   106  		},
   107  		{
   108  			Host:             "foo.example.com",
   109  			IngressRuleValue: networking.IngressRuleValue{},
   110  		},
   111  	}
   112  
   113  	expectedChanges = []ResourceChange{
   114  		{
   115  			Op: Delete,
   116  			Resource: &IngressConfiguration{
   117  				Ingress: updatedIng,
   118  				ValidHosts: map[string]bool{
   119  					"foo.example.com": true,
   120  				},
   121  				ChildWarnings: map[string][]string{},
   122  			},
   123  			Error: `spec.rules[1].host: Duplicate value: "foo.example.com"`,
   124  		},
   125  	}
   126  
   127  	changes, problems = configuration.AddOrUpdateIngress(invalidIng)
   128  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   129  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   130  	}
   131  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   132  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   133  	}
   134  
   135  	// Restore the Ingress
   136  
   137  	expectedChanges = []ResourceChange{
   138  		{
   139  			Op: AddOrUpdate,
   140  			Resource: &IngressConfiguration{
   141  				Ingress: updatedIng,
   142  				ValidHosts: map[string]bool{
   143  					"foo.example.com": true,
   144  				},
   145  				ChildWarnings: map[string][]string{},
   146  			},
   147  		},
   148  	}
   149  
   150  	changes, problems = configuration.AddOrUpdateIngress(updatedIng)
   151  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   152  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   153  	}
   154  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   155  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   156  	}
   157  
   158  	// Update the host of the Ingress
   159  
   160  	updatedHostIng := updatedIng.DeepCopy()
   161  	updatedHostIng.Generation++
   162  	updatedHostIng.Spec.Rules = []networking.IngressRule{
   163  		{
   164  			Host:             "bar.example.com",
   165  			IngressRuleValue: networking.IngressRuleValue{},
   166  		},
   167  	}
   168  
   169  	expectedChanges = []ResourceChange{
   170  		{
   171  			Op: AddOrUpdate,
   172  			Resource: &IngressConfiguration{
   173  				Ingress: updatedHostIng,
   174  				ValidHosts: map[string]bool{
   175  					"bar.example.com": true,
   176  				},
   177  				ChildWarnings: map[string][]string{},
   178  			},
   179  		},
   180  	}
   181  
   182  	changes, problems = configuration.AddOrUpdateIngress(updatedHostIng)
   183  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   184  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   185  	}
   186  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   187  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   188  	}
   189  
   190  	// Delete Ingress
   191  	expectedChanges = []ResourceChange{
   192  		{
   193  			Op: Delete,
   194  			Resource: &IngressConfiguration{
   195  				Ingress: updatedHostIng,
   196  				ValidHosts: map[string]bool{
   197  					"bar.example.com": true,
   198  				},
   199  				ChildWarnings: map[string][]string{},
   200  			},
   201  		},
   202  	}
   203  
   204  	changes, problems = configuration.DeleteIngress("default/ingress")
   205  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   206  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
   207  	}
   208  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   209  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
   210  	}
   211  }
   212  
   213  func TestAddInvalidIngress(t *testing.T) {
   214  	configuration := createTestConfiguration()
   215  
   216  	ing := createTestIngress("ingress", "foo.example.com", "foo.example.com")
   217  
   218  	var expectedChanges []ResourceChange
   219  	expectedProblems := []ConfigurationProblem{
   220  		{
   221  			Object:  ing,
   222  			IsError: true,
   223  			Reason:  "Rejected",
   224  			Message: `spec.rules[1].host: Duplicate value: "foo.example.com"`,
   225  		},
   226  	}
   227  
   228  	changes, problems := configuration.AddOrUpdateIngress(ing)
   229  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   230  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   231  	}
   232  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   233  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   234  	}
   235  }
   236  
   237  func TestDeleteNonExistingIngress(t *testing.T) {
   238  	configuration := createTestConfiguration()
   239  
   240  	var expectedChanges []ResourceChange
   241  	var expectedProblems []ConfigurationProblem
   242  
   243  	changes, problems := configuration.DeleteIngress("default/ingress")
   244  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   245  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
   246  	}
   247  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   248  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
   249  	}
   250  }
   251  
   252  func TestAddIngressForMergeableIngresses(t *testing.T) {
   253  	configuration := createTestConfiguration()
   254  
   255  	// Add  minion-1
   256  
   257  	minion1 := createTestIngressMinion("ingress-minion-1", "foo.example.com", "/path-1")
   258  	var expectedChanges []ResourceChange
   259  	expectedProblems := []ConfigurationProblem{
   260  		{
   261  			Object:  minion1,
   262  			Reason:  "NoIngressMasterFound",
   263  			Message: "Ingress master is invalid or doesn't exist",
   264  		},
   265  	}
   266  
   267  	changes, problems := configuration.AddOrUpdateIngress(minion1)
   268  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   269  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   270  	}
   271  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   272  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   273  	}
   274  
   275  	// Add master
   276  
   277  	master := createTestIngressMaster("ingress-master", "foo.example.com")
   278  	expectedChanges = []ResourceChange{
   279  		{
   280  			Op: AddOrUpdate,
   281  			Resource: &IngressConfiguration{
   282  				Ingress: master,
   283  				ValidHosts: map[string]bool{
   284  					"foo.example.com": true,
   285  				},
   286  				IsMaster: true,
   287  				Minions: []*MinionConfiguration{
   288  					{
   289  						Ingress: minion1,
   290  						ValidPaths: map[string]bool{
   291  							"/path-1": true,
   292  						},
   293  					},
   294  				},
   295  				ChildWarnings: map[string][]string{},
   296  			},
   297  		},
   298  	}
   299  	expectedProblems = nil
   300  
   301  	changes, problems = configuration.AddOrUpdateIngress(master)
   302  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   303  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   304  	}
   305  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   306  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   307  	}
   308  
   309  	// Add minion-2
   310  
   311  	minion2 := createTestIngressMinion("ingress-minion-2", "foo.example.com", "/path-2")
   312  	expectedChanges = []ResourceChange{
   313  		{
   314  			Op: AddOrUpdate,
   315  			Resource: &IngressConfiguration{
   316  				Ingress: master,
   317  				ValidHosts: map[string]bool{
   318  					"foo.example.com": true,
   319  				},
   320  				IsMaster: true,
   321  				Minions: []*MinionConfiguration{
   322  					{
   323  						Ingress: minion1,
   324  						ValidPaths: map[string]bool{
   325  							"/path-1": true,
   326  						},
   327  					},
   328  					{
   329  						Ingress: minion2,
   330  						ValidPaths: map[string]bool{
   331  							"/path-2": true,
   332  						},
   333  					},
   334  				},
   335  				ChildWarnings: map[string][]string{},
   336  			},
   337  		},
   338  	}
   339  	expectedProblems = nil
   340  
   341  	changes, problems = configuration.AddOrUpdateIngress(minion2)
   342  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   343  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   344  	}
   345  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   346  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   347  	}
   348  
   349  	// Update minion-1
   350  
   351  	updatedMinion1 := minion1.DeepCopy()
   352  	updatedMinion1.Annotations["nginx.org/proxy-connect-timeout"] = "10s"
   353  	expectedChanges = []ResourceChange{
   354  		{
   355  			Op: AddOrUpdate,
   356  			Resource: &IngressConfiguration{
   357  				Ingress: master,
   358  				ValidHosts: map[string]bool{
   359  					"foo.example.com": true,
   360  				},
   361  				IsMaster: true,
   362  				Minions: []*MinionConfiguration{
   363  					{
   364  						Ingress: updatedMinion1,
   365  						ValidPaths: map[string]bool{
   366  							"/path-1": true,
   367  						},
   368  					},
   369  					{
   370  						Ingress: minion2,
   371  						ValidPaths: map[string]bool{
   372  							"/path-2": true,
   373  						},
   374  					},
   375  				},
   376  				ChildWarnings: map[string][]string{},
   377  			},
   378  		},
   379  	}
   380  	expectedProblems = nil
   381  
   382  	changes, problems = configuration.AddOrUpdateIngress(updatedMinion1)
   383  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   384  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   385  	}
   386  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   387  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   388  	}
   389  
   390  	// Make minion-1 invalid
   391  
   392  	invalidMinion1 := updatedMinion1.DeepCopy()
   393  	invalidMinion1.Generation++
   394  	invalidMinion1.Spec.Rules = []networking.IngressRule{
   395  		{
   396  			Host:             "example.com",
   397  			IngressRuleValue: networking.IngressRuleValue{},
   398  		},
   399  		{
   400  			Host:             "example.com",
   401  			IngressRuleValue: networking.IngressRuleValue{},
   402  		},
   403  	}
   404  
   405  	expectedChanges = []ResourceChange{
   406  		{
   407  			Op: AddOrUpdate,
   408  			Resource: &IngressConfiguration{
   409  				Ingress: master,
   410  				ValidHosts: map[string]bool{
   411  					"foo.example.com": true,
   412  				},
   413  				IsMaster: true,
   414  				Minions: []*MinionConfiguration{
   415  					{
   416  						Ingress: minion2,
   417  						ValidPaths: map[string]bool{
   418  							"/path-2": true,
   419  						},
   420  					},
   421  				},
   422  				ChildWarnings: map[string][]string{},
   423  			},
   424  		},
   425  	}
   426  	expectedProblems = []ConfigurationProblem{
   427  		{
   428  			Object:  invalidMinion1,
   429  			IsError: true,
   430  			Reason:  "Rejected",
   431  			Message: `[spec.rules[1].host: Duplicate value: "example.com", spec.rules: Too many: 2: must have at most 1 items]`,
   432  		},
   433  	}
   434  
   435  	changes, problems = configuration.AddOrUpdateIngress(invalidMinion1)
   436  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   437  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   438  	}
   439  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   440  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   441  	}
   442  
   443  	// Restore minion-1
   444  
   445  	expectedChanges = []ResourceChange{
   446  		{
   447  			Op: AddOrUpdate,
   448  			Resource: &IngressConfiguration{
   449  				Ingress: master,
   450  				ValidHosts: map[string]bool{
   451  					"foo.example.com": true,
   452  				},
   453  				IsMaster: true,
   454  				Minions: []*MinionConfiguration{
   455  					{
   456  						Ingress: updatedMinion1,
   457  						ValidPaths: map[string]bool{
   458  							"/path-1": true,
   459  						},
   460  					},
   461  					{
   462  						Ingress: minion2,
   463  						ValidPaths: map[string]bool{
   464  							"/path-2": true,
   465  						},
   466  					},
   467  				},
   468  				ChildWarnings: map[string][]string{},
   469  			},
   470  		},
   471  	}
   472  	expectedProblems = nil
   473  
   474  	changes, problems = configuration.AddOrUpdateIngress(updatedMinion1)
   475  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   476  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   477  	}
   478  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   479  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   480  	}
   481  
   482  	// Update host of minion-2
   483  
   484  	updatedMinion2 := minion2.DeepCopy()
   485  	updatedMinion2.Generation++
   486  	updatedMinion2.Spec.Rules[0].Host = "bar.example.com"
   487  
   488  	expectedChanges = []ResourceChange{
   489  		{
   490  			Op: AddOrUpdate,
   491  			Resource: &IngressConfiguration{
   492  				Ingress: master,
   493  				ValidHosts: map[string]bool{
   494  					"foo.example.com": true,
   495  				},
   496  				IsMaster: true,
   497  				Minions: []*MinionConfiguration{
   498  					{
   499  						Ingress: updatedMinion1,
   500  						ValidPaths: map[string]bool{
   501  							"/path-1": true,
   502  						},
   503  					},
   504  				},
   505  				ChildWarnings: map[string][]string{},
   506  			},
   507  		},
   508  	}
   509  	expectedProblems = []ConfigurationProblem{
   510  		{
   511  			Object:  updatedMinion2,
   512  			Reason:  "NoIngressMasterFound",
   513  			Message: "Ingress master is invalid or doesn't exist",
   514  		},
   515  	}
   516  
   517  	changes, problems = configuration.AddOrUpdateIngress(updatedMinion2)
   518  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   519  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   520  	}
   521  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   522  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   523  	}
   524  
   525  	// Update host of master
   526  
   527  	updatedMaster := master.DeepCopy()
   528  	updatedMaster.Generation++
   529  	updatedMaster.Spec.Rules[0].Host = "bar.example.com"
   530  
   531  	expectedChanges = []ResourceChange{
   532  		{
   533  			Op: AddOrUpdate,
   534  			Resource: &IngressConfiguration{
   535  				Ingress: updatedMaster,
   536  				ValidHosts: map[string]bool{
   537  					"bar.example.com": true,
   538  				},
   539  				IsMaster: true,
   540  				Minions: []*MinionConfiguration{
   541  					{
   542  						Ingress: updatedMinion2,
   543  						ValidPaths: map[string]bool{
   544  							"/path-2": true,
   545  						},
   546  					},
   547  				},
   548  				ChildWarnings: map[string][]string{},
   549  			},
   550  		},
   551  	}
   552  	expectedProblems = []ConfigurationProblem{
   553  		{
   554  			Object:  updatedMinion1,
   555  			Reason:  "NoIngressMasterFound",
   556  			Message: "Ingress master is invalid or doesn't exist",
   557  		},
   558  	}
   559  
   560  	changes, problems = configuration.AddOrUpdateIngress(updatedMaster)
   561  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   562  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   563  	}
   564  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   565  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   566  	}
   567  
   568  	// Restore host
   569  	expectedChanges = []ResourceChange{
   570  		{
   571  			Op: AddOrUpdate,
   572  			Resource: &IngressConfiguration{
   573  				Ingress: master,
   574  				ValidHosts: map[string]bool{
   575  					"foo.example.com": true,
   576  				},
   577  				IsMaster: true,
   578  				Minions: []*MinionConfiguration{
   579  					{
   580  						Ingress: updatedMinion1,
   581  						ValidPaths: map[string]bool{
   582  							"/path-1": true,
   583  						},
   584  					},
   585  				},
   586  				ChildWarnings: map[string][]string{},
   587  			},
   588  		},
   589  	}
   590  	expectedProblems = []ConfigurationProblem{
   591  		{
   592  			Object:  updatedMinion2,
   593  			Reason:  "NoIngressMasterFound",
   594  			Message: "Ingress master is invalid or doesn't exist",
   595  		},
   596  	}
   597  
   598  	changes, problems = configuration.AddOrUpdateIngress(master)
   599  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   600  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   601  	}
   602  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   603  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   604  	}
   605  
   606  	// Restore host of minion-2
   607  
   608  	expectedChanges = []ResourceChange{
   609  		{
   610  			Op: AddOrUpdate,
   611  			Resource: &IngressConfiguration{
   612  				Ingress: master,
   613  				ValidHosts: map[string]bool{
   614  					"foo.example.com": true,
   615  				},
   616  				IsMaster: true,
   617  				Minions: []*MinionConfiguration{
   618  					{
   619  						Ingress: updatedMinion1,
   620  						ValidPaths: map[string]bool{
   621  							"/path-1": true,
   622  						},
   623  					},
   624  					{
   625  						Ingress: minion2,
   626  						ValidPaths: map[string]bool{
   627  							"/path-2": true,
   628  						},
   629  					},
   630  				},
   631  				ChildWarnings: map[string][]string{},
   632  			},
   633  		},
   634  	}
   635  	expectedProblems = nil
   636  
   637  	changes, problems = configuration.AddOrUpdateIngress(minion2)
   638  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   639  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   640  	}
   641  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   642  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   643  	}
   644  
   645  	// Remove minion-1
   646  
   647  	expectedChanges = []ResourceChange{
   648  		{
   649  			Op: AddOrUpdate,
   650  			Resource: &IngressConfiguration{
   651  				Ingress: master,
   652  				ValidHosts: map[string]bool{
   653  					"foo.example.com": true,
   654  				},
   655  				IsMaster: true,
   656  				Minions: []*MinionConfiguration{
   657  					{
   658  						Ingress: minion2,
   659  						ValidPaths: map[string]bool{
   660  							"/path-2": true,
   661  						},
   662  					},
   663  				},
   664  				ChildWarnings: map[string][]string{},
   665  			},
   666  		},
   667  	}
   668  	expectedProblems = nil
   669  
   670  	changes, problems = configuration.DeleteIngress("default/ingress-minion-1")
   671  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   672  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
   673  	}
   674  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   675  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
   676  	}
   677  
   678  	// Remove master
   679  
   680  	expectedChanges = []ResourceChange{
   681  		{
   682  			Op: Delete,
   683  			Resource: &IngressConfiguration{
   684  				Ingress: master,
   685  				ValidHosts: map[string]bool{
   686  					"foo.example.com": true,
   687  				},
   688  				IsMaster: true,
   689  				Minions: []*MinionConfiguration{
   690  					{
   691  						Ingress: minion2,
   692  						ValidPaths: map[string]bool{
   693  							"/path-2": true,
   694  						},
   695  					},
   696  				},
   697  				ChildWarnings: map[string][]string{},
   698  			},
   699  		},
   700  	}
   701  	expectedProblems = []ConfigurationProblem{
   702  		{
   703  			Object:  minion2,
   704  			Reason:  "NoIngressMasterFound",
   705  			Message: "Ingress master is invalid or doesn't exist",
   706  		},
   707  	}
   708  
   709  	changes, problems = configuration.DeleteIngress("default/ingress-master")
   710  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   711  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
   712  	}
   713  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   714  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
   715  	}
   716  
   717  	// Remove minion-2
   718  
   719  	expectedChanges = nil
   720  	expectedProblems = nil
   721  
   722  	changes, problems = configuration.DeleteIngress("default/ingress-minion-2")
   723  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   724  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
   725  	}
   726  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   727  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
   728  	}
   729  }
   730  
   731  func TestMinionPathCollisions(t *testing.T) {
   732  	configuration := createTestConfiguration()
   733  
   734  	// Add master
   735  
   736  	master := createTestIngressMaster("ingress-master", "foo.example.com")
   737  	expectedChanges := []ResourceChange{
   738  		{
   739  			Op: AddOrUpdate,
   740  			Resource: &IngressConfiguration{
   741  				Ingress: master,
   742  				ValidHosts: map[string]bool{
   743  					"foo.example.com": true,
   744  				},
   745  				IsMaster:      true,
   746  				ChildWarnings: map[string][]string{},
   747  			},
   748  		},
   749  	}
   750  	var expectedProblems []ConfigurationProblem
   751  
   752  	changes, problems := configuration.AddOrUpdateIngress(master)
   753  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   754  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   755  	}
   756  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   757  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   758  	}
   759  
   760  	// Add  minion-1
   761  
   762  	minion1 := createTestIngressMinion("ingress-minion-1", "foo.example.com", "/")
   763  	expectedChanges = []ResourceChange{
   764  		{
   765  			Op: AddOrUpdate,
   766  			Resource: &IngressConfiguration{
   767  				Ingress: master,
   768  				ValidHosts: map[string]bool{
   769  					"foo.example.com": true,
   770  				},
   771  				IsMaster: true,
   772  				Minions: []*MinionConfiguration{
   773  					{
   774  						Ingress: minion1,
   775  						ValidPaths: map[string]bool{
   776  							"/": true,
   777  						},
   778  					},
   779  				},
   780  				ChildWarnings: map[string][]string{},
   781  			},
   782  		},
   783  	}
   784  	expectedProblems = nil
   785  
   786  	changes, problems = configuration.AddOrUpdateIngress(minion1)
   787  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   788  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   789  	}
   790  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   791  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   792  	}
   793  
   794  	// Add minion-2
   795  
   796  	minion2 := createTestIngressMinion("ingress-minion-2", "foo.example.com", "/")
   797  	expectedChanges = []ResourceChange{
   798  		{
   799  			Op: AddOrUpdate,
   800  			Resource: &IngressConfiguration{
   801  				Ingress: master,
   802  				ValidHosts: map[string]bool{
   803  					"foo.example.com": true,
   804  				},
   805  				IsMaster: true,
   806  				Minions: []*MinionConfiguration{
   807  					{
   808  						Ingress: minion1,
   809  						ValidPaths: map[string]bool{
   810  							"/": true,
   811  						},
   812  					},
   813  					{
   814  						Ingress:    minion2,
   815  						ValidPaths: map[string]bool{},
   816  					},
   817  				},
   818  				ChildWarnings: map[string][]string{
   819  					"default/ingress-minion-2": {
   820  						"path / is taken by another resource",
   821  					},
   822  				},
   823  			},
   824  		},
   825  	}
   826  	expectedProblems = nil
   827  
   828  	changes, problems = configuration.AddOrUpdateIngress(minion2)
   829  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   830  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   831  	}
   832  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   833  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   834  	}
   835  
   836  	// Delete minion-1
   837  	expectedChanges = []ResourceChange{
   838  		{
   839  			Op: AddOrUpdate,
   840  			Resource: &IngressConfiguration{
   841  				Ingress: master,
   842  				ValidHosts: map[string]bool{
   843  					"foo.example.com": true,
   844  				},
   845  				IsMaster: true,
   846  				Minions: []*MinionConfiguration{
   847  					{
   848  						Ingress: minion2,
   849  						ValidPaths: map[string]bool{
   850  							"/": true,
   851  						},
   852  					},
   853  				},
   854  				ChildWarnings: map[string][]string{},
   855  			},
   856  		},
   857  	}
   858  	expectedProblems = nil
   859  
   860  	changes, problems = configuration.DeleteIngress("default/ingress-minion-1")
   861  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   862  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
   863  	}
   864  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   865  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
   866  	}
   867  }
   868  
   869  func TestAddIngressWithIncorrectClass(t *testing.T) {
   870  	configuration := createTestConfiguration()
   871  
   872  	// Add Ingress with incorrect class
   873  
   874  	ing := createTestIngress("regular-ingress", "foo.example.com")
   875  	ing.Annotations["kubernetes.io/ingress.class"] = "someproxy"
   876  
   877  	var expectedChanges []ResourceChange
   878  	var expectedProblems []ConfigurationProblem
   879  
   880  	changes, problems := configuration.AddOrUpdateIngress(ing)
   881  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   882  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   883  	}
   884  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   885  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   886  	}
   887  
   888  	// Make the class correct
   889  
   890  	updatedIng := ing.DeepCopy()
   891  	updatedIng.Annotations["kubernetes.io/ingress.class"] = "nginx"
   892  
   893  	expectedChanges = []ResourceChange{
   894  		{
   895  			Op: AddOrUpdate,
   896  			Resource: &IngressConfiguration{
   897  				Ingress: updatedIng,
   898  				ValidHosts: map[string]bool{
   899  					"foo.example.com": true,
   900  				},
   901  				ChildWarnings: map[string][]string{},
   902  			},
   903  		},
   904  	}
   905  	expectedProblems = nil
   906  
   907  	changes, problems = configuration.AddOrUpdateIngress(updatedIng)
   908  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   909  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   910  	}
   911  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   912  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   913  	}
   914  
   915  	// Make the class incorrect
   916  
   917  	expectedChanges = []ResourceChange{
   918  		{
   919  			Op: Delete,
   920  			Resource: &IngressConfiguration{
   921  				Ingress: updatedIng,
   922  				ValidHosts: map[string]bool{
   923  					"foo.example.com": true,
   924  				},
   925  				ChildWarnings: map[string][]string{},
   926  			},
   927  		},
   928  	}
   929  	expectedProblems = nil
   930  
   931  	changes, problems = configuration.AddOrUpdateIngress(ing)
   932  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   933  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   934  	}
   935  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   936  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
   937  	}
   938  }
   939  
   940  func TestAddVirtualServer(t *testing.T) {
   941  	configuration := createTestConfiguration()
   942  
   943  	// no problems are expected for all cases
   944  	var expectedProblems []ConfigurationProblem
   945  
   946  	// Add a VirtualServer
   947  
   948  	vs := createTestVirtualServer("virtualserver", "foo.example.com")
   949  	expectedChanges := []ResourceChange{
   950  		{
   951  			Op: AddOrUpdate,
   952  			Resource: &VirtualServerConfiguration{
   953  				VirtualServer: vs,
   954  			},
   955  		},
   956  	}
   957  
   958  	changes, problems := configuration.AddOrUpdateVirtualServer(vs)
   959  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   960  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
   961  	}
   962  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   963  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
   964  	}
   965  
   966  	// Update VirtualServer
   967  
   968  	updatedVS := vs.DeepCopy()
   969  	updatedVS.Generation++
   970  	updatedVS.Spec.ServerSnippets = "# snippet"
   971  
   972  	expectedChanges = []ResourceChange{
   973  		{
   974  			Op: AddOrUpdate,
   975  			Resource: &VirtualServerConfiguration{
   976  				VirtualServer: updatedVS,
   977  			},
   978  		},
   979  	}
   980  
   981  	changes, problems = configuration.AddOrUpdateVirtualServer(updatedVS)
   982  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
   983  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
   984  	}
   985  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
   986  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
   987  	}
   988  
   989  	// Make VirtualServer invalid
   990  
   991  	invalidVS := updatedVS.DeepCopy()
   992  	invalidVS.Generation++
   993  	invalidVS.Spec.Host = ""
   994  
   995  	expectedChanges = []ResourceChange{
   996  		{
   997  			Op: Delete,
   998  			Resource: &VirtualServerConfiguration{
   999  				VirtualServer: updatedVS,
  1000  			},
  1001  			Error: "spec.host: Required value",
  1002  		},
  1003  	}
  1004  
  1005  	changes, problems = configuration.AddOrUpdateVirtualServer(invalidVS)
  1006  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1007  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1008  	}
  1009  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1010  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1011  	}
  1012  
  1013  	// Restore VirtualServer
  1014  
  1015  	expectedChanges = []ResourceChange{
  1016  		{
  1017  			Op: AddOrUpdate,
  1018  			Resource: &VirtualServerConfiguration{
  1019  				VirtualServer: updatedVS,
  1020  			},
  1021  		},
  1022  	}
  1023  
  1024  	changes, problems = configuration.AddOrUpdateVirtualServer(updatedVS)
  1025  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1026  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1027  	}
  1028  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1029  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1030  	}
  1031  
  1032  	// Update VirtualServer host
  1033  
  1034  	updatedHostVS := updatedVS.DeepCopy()
  1035  	updatedHostVS.Generation++
  1036  	updatedHostVS.Spec.Host = "bar.example.com"
  1037  
  1038  	expectedChanges = []ResourceChange{
  1039  		{
  1040  			Op: AddOrUpdate,
  1041  			Resource: &VirtualServerConfiguration{
  1042  				VirtualServer: updatedHostVS,
  1043  			},
  1044  		},
  1045  	}
  1046  
  1047  	changes, problems = configuration.AddOrUpdateVirtualServer(updatedHostVS)
  1048  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1049  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1050  	}
  1051  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1052  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1053  	}
  1054  
  1055  	// Delete VirtualServer
  1056  	expectedChanges = []ResourceChange{
  1057  		{
  1058  			Op: Delete,
  1059  			Resource: &VirtualServerConfiguration{
  1060  				VirtualServer: updatedHostVS,
  1061  			},
  1062  		},
  1063  	}
  1064  
  1065  	changes, problems = configuration.DeleteVirtualServer("default/virtualserver")
  1066  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1067  		t.Errorf("DeleteVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1068  	}
  1069  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1070  		t.Errorf("DeleteVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1071  	}
  1072  }
  1073  
  1074  func TestAddInvalidVirtualServer(t *testing.T) {
  1075  	configuration := createTestConfiguration()
  1076  
  1077  	vs := createTestVirtualServer("virtualserver", "")
  1078  
  1079  	var expectedChanges []ResourceChange
  1080  	expectedProblems := []ConfigurationProblem{
  1081  		{
  1082  			Object:  vs,
  1083  			IsError: true,
  1084  			Reason:  "Rejected",
  1085  			Message: "VirtualServer default/virtualserver was rejected with error: spec.host: Required value",
  1086  		},
  1087  	}
  1088  
  1089  	changes, problems := configuration.AddOrUpdateVirtualServer(vs)
  1090  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1091  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1092  	}
  1093  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1094  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1095  	}
  1096  }
  1097  
  1098  func TestAddInvalidVirtualServerWithIncorrectClass(t *testing.T) {
  1099  	configuration := createTestConfiguration()
  1100  
  1101  	// Add VirtualServer with incorrect class
  1102  
  1103  	vs := createTestVirtualServer("virtualserver", "example.com")
  1104  	vs.Spec.IngressClass = "someproxy"
  1105  
  1106  	var expectedChanges []ResourceChange
  1107  	var expectedProblems []ConfigurationProblem
  1108  
  1109  	changes, problems := configuration.AddOrUpdateVirtualServer(vs)
  1110  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1111  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1112  	}
  1113  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1114  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1115  	}
  1116  
  1117  	// Make the class correct
  1118  
  1119  	updatedVS := vs.DeepCopy()
  1120  	updatedVS.Generation++
  1121  	updatedVS.Spec.IngressClass = "nginx"
  1122  
  1123  	expectedChanges = []ResourceChange{
  1124  		{
  1125  			Op: AddOrUpdate,
  1126  			Resource: &VirtualServerConfiguration{
  1127  				VirtualServer: updatedVS,
  1128  			},
  1129  		},
  1130  	}
  1131  	expectedProblems = nil
  1132  
  1133  	changes, problems = configuration.AddOrUpdateVirtualServer(updatedVS)
  1134  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1135  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1136  	}
  1137  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1138  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1139  	}
  1140  
  1141  	// Make the class incorrect
  1142  
  1143  	expectedChanges = []ResourceChange{
  1144  		{
  1145  			Op: Delete,
  1146  			Resource: &VirtualServerConfiguration{
  1147  				VirtualServer: updatedVS,
  1148  			},
  1149  		},
  1150  	}
  1151  	expectedProblems = nil
  1152  
  1153  	changes, problems = configuration.AddOrUpdateVirtualServer(vs)
  1154  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1155  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1156  	}
  1157  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1158  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1159  	}
  1160  }
  1161  
  1162  func TestDeleteNonExistingVirtualServer(t *testing.T) {
  1163  	configuration := createTestConfiguration()
  1164  
  1165  	var expectedChanges []ResourceChange
  1166  	var expectedProblems []ConfigurationProblem
  1167  
  1168  	changes, problems := configuration.DeleteVirtualServer("default/virtualserver")
  1169  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1170  		t.Errorf("DeleteVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1171  	}
  1172  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1173  		t.Errorf("DeleteVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1174  	}
  1175  }
  1176  
  1177  func TestAddVirtualServerWithVirtualServerRoutes(t *testing.T) {
  1178  	configuration := createTestConfiguration()
  1179  
  1180  	// Add VirtualServerRoute-1
  1181  
  1182  	vsr1 := createTestVirtualServerRoute("virtualserverroute-1", "foo.example.com", "/first")
  1183  	var expectedChanges []ResourceChange
  1184  	expectedProblems := []ConfigurationProblem{
  1185  		{
  1186  			Object:  vsr1,
  1187  			Reason:  "NoVirtualServerFound",
  1188  			Message: "VirtualServer is invalid or doesn't exist",
  1189  		},
  1190  	}
  1191  
  1192  	changes, problems := configuration.AddOrUpdateVirtualServerRoute(vsr1)
  1193  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1194  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1195  	}
  1196  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1197  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1198  	}
  1199  
  1200  	// Add VirtualServer
  1201  
  1202  	vs := createTestVirtualServerWithRoutes(
  1203  		"virtualserver",
  1204  		"foo.example.com",
  1205  		[]conf_v1.Route{
  1206  			{
  1207  				Path:  "/first",
  1208  				Route: "virtualserverroute-1",
  1209  			},
  1210  			{
  1211  				Path:  "/second",
  1212  				Route: "virtualserverroute-2",
  1213  			},
  1214  		})
  1215  	expectedChanges = []ResourceChange{
  1216  		{
  1217  			Op: AddOrUpdate,
  1218  			Resource: &VirtualServerConfiguration{
  1219  				VirtualServer:       vs,
  1220  				VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1},
  1221  				Warnings:            []string{"VirtualServerRoute default/virtualserverroute-2 doesn't exist or invalid"},
  1222  			},
  1223  		},
  1224  	}
  1225  	expectedProblems = nil
  1226  
  1227  	changes, problems = configuration.AddOrUpdateVirtualServer(vs)
  1228  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1229  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1230  	}
  1231  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1232  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1233  	}
  1234  
  1235  	vsr2 := createTestVirtualServerRoute("virtualserverroute-2", "foo.example.com", "/second")
  1236  
  1237  	// Add VirtualServerRoute-2
  1238  
  1239  	expectedChanges = []ResourceChange{
  1240  		{
  1241  			Op: AddOrUpdate,
  1242  			Resource: &VirtualServerConfiguration{
  1243  				VirtualServer:       vs,
  1244  				VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr2},
  1245  			},
  1246  		},
  1247  	}
  1248  	expectedProblems = nil
  1249  
  1250  	changes, problems = configuration.AddOrUpdateVirtualServerRoute(vsr2)
  1251  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1252  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1253  	}
  1254  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1255  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1256  	}
  1257  
  1258  	// Update VirtualServerRoute-1
  1259  
  1260  	updatedVSR1 := vsr1.DeepCopy()
  1261  	updatedVSR1.Generation++
  1262  	updatedVSR1.Spec.Subroutes[0].LocationSnippets = "# snippet"
  1263  	expectedChanges = []ResourceChange{
  1264  		{
  1265  			Op: AddOrUpdate,
  1266  			Resource: &VirtualServerConfiguration{
  1267  				VirtualServer:       vs,
  1268  				VirtualServerRoutes: []*conf_v1.VirtualServerRoute{updatedVSR1, vsr2},
  1269  			},
  1270  		},
  1271  	}
  1272  	expectedProblems = nil
  1273  
  1274  	changes, problems = configuration.AddOrUpdateVirtualServerRoute(updatedVSR1)
  1275  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1276  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1277  	}
  1278  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1279  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1280  	}
  1281  
  1282  	// Make VirtualServerRoute-1 invalid
  1283  
  1284  	invalidVSR1 := updatedVSR1.DeepCopy()
  1285  	invalidVSR1.Generation++
  1286  	invalidVSR1.Spec.Host = ""
  1287  	expectedChanges = []ResourceChange{
  1288  		{
  1289  			Op: AddOrUpdate,
  1290  			Resource: &VirtualServerConfiguration{
  1291  				VirtualServer:       vs,
  1292  				VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr2},
  1293  				Warnings:            []string{"VirtualServerRoute default/virtualserverroute-1 doesn't exist or invalid"},
  1294  			},
  1295  		},
  1296  	}
  1297  	expectedProblems = []ConfigurationProblem{
  1298  		{
  1299  			Object:  invalidVSR1,
  1300  			IsError: true,
  1301  			Reason:  "Rejected",
  1302  			Message: "VirtualServerRoute default/virtualserverroute-1 was rejected with error: spec.host: Required value",
  1303  		},
  1304  	}
  1305  
  1306  	changes, problems = configuration.AddOrUpdateVirtualServerRoute(invalidVSR1)
  1307  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1308  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1309  	}
  1310  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1311  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1312  	}
  1313  
  1314  	// Restore VirtualServerRoute-1
  1315  
  1316  	expectedChanges = []ResourceChange{
  1317  		{
  1318  			Op: AddOrUpdate,
  1319  			Resource: &VirtualServerConfiguration{
  1320  				VirtualServer:       vs,
  1321  				VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr2},
  1322  			},
  1323  		},
  1324  	}
  1325  	expectedProblems = nil
  1326  
  1327  	changes, problems = configuration.AddOrUpdateVirtualServerRoute(vsr1)
  1328  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1329  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1330  	}
  1331  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1332  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1333  	}
  1334  
  1335  	// Make VirtualServerRoute-1 invalid for VirtualServer
  1336  
  1337  	invalidForVSVSR1 := vsr1.DeepCopy()
  1338  	invalidForVSVSR1.Generation++
  1339  	invalidForVSVSR1.Spec.Subroutes[0].Path = "/"
  1340  	expectedChanges = []ResourceChange{
  1341  		{
  1342  			Op: AddOrUpdate,
  1343  			Resource: &VirtualServerConfiguration{
  1344  				VirtualServer:       vs,
  1345  				VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr2},
  1346  				Warnings:            []string{"VirtualServerRoute default/virtualserverroute-1 is invalid: spec.subroutes[0]: Invalid value: \"/\": must start with '/first'"},
  1347  			},
  1348  		},
  1349  	}
  1350  	expectedProblems = []ConfigurationProblem{
  1351  		{
  1352  			Object:  invalidForVSVSR1,
  1353  			Reason:  "Ignored",
  1354  			Message: "VirtualServer default/virtualserver ignores VirtualServerRoute",
  1355  		},
  1356  	}
  1357  
  1358  	changes, problems = configuration.AddOrUpdateVirtualServerRoute(invalidForVSVSR1)
  1359  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1360  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1361  	}
  1362  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1363  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1364  	}
  1365  
  1366  	// Restore VirtualServerRoute-1
  1367  
  1368  	expectedChanges = []ResourceChange{
  1369  		{
  1370  			Op: AddOrUpdate,
  1371  			Resource: &VirtualServerConfiguration{
  1372  				VirtualServer:       vs,
  1373  				VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr2},
  1374  			},
  1375  		},
  1376  	}
  1377  	expectedProblems = nil
  1378  
  1379  	changes, problems = configuration.AddOrUpdateVirtualServerRoute(vsr1)
  1380  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1381  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1382  	}
  1383  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1384  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1385  	}
  1386  
  1387  	// Update host of VirtualServerRoute-2
  1388  
  1389  	updatedVSR2 := vsr2.DeepCopy()
  1390  	updatedVSR2.Generation++
  1391  	updatedVSR2.Spec.Host = "bar.example.com"
  1392  	expectedChanges = []ResourceChange{
  1393  		{
  1394  			Op: AddOrUpdate,
  1395  			Resource: &VirtualServerConfiguration{
  1396  				VirtualServer:       vs,
  1397  				VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1},
  1398  				Warnings:            []string{"VirtualServerRoute default/virtualserverroute-2 is invalid: spec.host: Invalid value: \"bar.example.com\": must be equal to 'foo.example.com'"},
  1399  			},
  1400  		},
  1401  	}
  1402  	expectedProblems = []ConfigurationProblem{
  1403  		{
  1404  			Object:  updatedVSR2,
  1405  			Reason:  "NoVirtualServerFound",
  1406  			Message: "VirtualServer is invalid or doesn't exist",
  1407  		},
  1408  	}
  1409  
  1410  	changes, problems = configuration.AddOrUpdateVirtualServerRoute(updatedVSR2)
  1411  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1412  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1413  	}
  1414  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1415  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1416  	}
  1417  
  1418  	// Update host of VirtualServer
  1419  
  1420  	updatedVS := vs.DeepCopy()
  1421  	updatedVS.Generation++
  1422  	updatedVS.Spec.Host = "bar.example.com"
  1423  	expectedChanges = []ResourceChange{
  1424  		{
  1425  			Op: AddOrUpdate,
  1426  			Resource: &VirtualServerConfiguration{
  1427  				VirtualServer:       updatedVS,
  1428  				VirtualServerRoutes: []*conf_v1.VirtualServerRoute{updatedVSR2},
  1429  				Warnings:            []string{"VirtualServerRoute default/virtualserverroute-1 is invalid: spec.host: Invalid value: \"foo.example.com\": must be equal to 'bar.example.com'"},
  1430  			},
  1431  		},
  1432  	}
  1433  	expectedProblems = []ConfigurationProblem{
  1434  		{
  1435  			Object:  vsr1,
  1436  			Reason:  "NoVirtualServerFound",
  1437  			Message: "VirtualServer is invalid or doesn't exist",
  1438  		},
  1439  	}
  1440  
  1441  	changes, problems = configuration.AddOrUpdateVirtualServer(updatedVS)
  1442  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1443  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1444  	}
  1445  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1446  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1447  	}
  1448  
  1449  	// Restore host of VirtualServer
  1450  
  1451  	expectedChanges = []ResourceChange{
  1452  		{
  1453  			Op: AddOrUpdate,
  1454  			Resource: &VirtualServerConfiguration{
  1455  				VirtualServer:       vs,
  1456  				VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1},
  1457  				Warnings:            []string{"VirtualServerRoute default/virtualserverroute-2 is invalid: spec.host: Invalid value: \"bar.example.com\": must be equal to 'foo.example.com'"},
  1458  			},
  1459  		},
  1460  	}
  1461  	expectedProblems = []ConfigurationProblem{
  1462  		{
  1463  			Object:  updatedVSR2,
  1464  			Reason:  "NoVirtualServerFound",
  1465  			Message: "VirtualServer is invalid or doesn't exist",
  1466  		},
  1467  	}
  1468  
  1469  	changes, problems = configuration.AddOrUpdateVirtualServer(vs)
  1470  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1471  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1472  	}
  1473  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1474  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1475  	}
  1476  
  1477  	// Restore host of VirtualServerRoute-2
  1478  
  1479  	expectedChanges = []ResourceChange{
  1480  		{
  1481  			Op: AddOrUpdate,
  1482  			Resource: &VirtualServerConfiguration{
  1483  				VirtualServer:       vs,
  1484  				VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr1, vsr2},
  1485  			},
  1486  		},
  1487  	}
  1488  	expectedProblems = nil
  1489  
  1490  	changes, problems = configuration.AddOrUpdateVirtualServerRoute(vsr2)
  1491  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1492  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1493  	}
  1494  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1495  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1496  	}
  1497  
  1498  	// Remove VirtualServerRoute-1
  1499  
  1500  	expectedChanges = []ResourceChange{
  1501  		{
  1502  			Op: AddOrUpdate,
  1503  			Resource: &VirtualServerConfiguration{
  1504  				VirtualServer:       vs,
  1505  				VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr2},
  1506  				Warnings:            []string{"VirtualServerRoute default/virtualserverroute-1 doesn't exist or invalid"},
  1507  			},
  1508  		},
  1509  	}
  1510  	expectedProblems = nil
  1511  
  1512  	changes, problems = configuration.DeleteVirtualServerRoute("default/virtualserverroute-1")
  1513  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1514  		t.Errorf("DeleteVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1515  	}
  1516  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1517  		t.Errorf("DeleteVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1518  	}
  1519  
  1520  	// Remove VirtualServer
  1521  
  1522  	expectedChanges = []ResourceChange{
  1523  		{
  1524  			Op: Delete,
  1525  			Resource: &VirtualServerConfiguration{
  1526  				VirtualServer:       vs,
  1527  				VirtualServerRoutes: []*conf_v1.VirtualServerRoute{vsr2},
  1528  				Warnings:            []string{"VirtualServerRoute default/virtualserverroute-1 doesn't exist or invalid"},
  1529  			},
  1530  		},
  1531  	}
  1532  	expectedProblems = []ConfigurationProblem{
  1533  		{
  1534  			Object:  vsr2,
  1535  			Reason:  "NoVirtualServerFound",
  1536  			Message: "VirtualServer is invalid or doesn't exist",
  1537  		},
  1538  	}
  1539  
  1540  	changes, problems = configuration.DeleteVirtualServer("default/virtualserver")
  1541  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1542  		t.Errorf("DeleteVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1543  	}
  1544  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1545  		t.Errorf("DeleteVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1546  	}
  1547  
  1548  	// Remove VirtualServerRoute-2
  1549  
  1550  	expectedChanges = nil
  1551  	expectedProblems = nil
  1552  
  1553  	changes, problems = configuration.DeleteVirtualServerRoute("default/virtualserverroute-2")
  1554  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1555  		t.Errorf("DeleteVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1556  	}
  1557  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1558  		t.Errorf("DeleteVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1559  	}
  1560  }
  1561  
  1562  func TestAddInvalidVirtualServerRoute(t *testing.T) {
  1563  	configuration := createTestConfiguration()
  1564  
  1565  	vsr := createTestVirtualServerRoute("virtualserverroute", "", "/")
  1566  
  1567  	var expectedChanges []ResourceChange
  1568  	expectedProblems := []ConfigurationProblem{
  1569  		{
  1570  			Object:  vsr,
  1571  			IsError: true,
  1572  			Reason:  "Rejected",
  1573  			Message: "VirtualServerRoute default/virtualserverroute was rejected with error: spec.host: Required value",
  1574  		},
  1575  	}
  1576  
  1577  	changes, problems := configuration.AddOrUpdateVirtualServerRoute(vsr)
  1578  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1579  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1580  	}
  1581  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1582  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1583  	}
  1584  }
  1585  
  1586  func TestAddVirtualServerWithIncorrectClass(t *testing.T) {
  1587  	configuration := createTestConfiguration()
  1588  
  1589  	vsr := createTestVirtualServerRoute("virtualserver", "foo.example.com", "/")
  1590  	vsr.Spec.IngressClass = "someproxy"
  1591  
  1592  	var expectedChanges []ResourceChange
  1593  	var expectedProblems []ConfigurationProblem
  1594  
  1595  	changes, problems := configuration.AddOrUpdateVirtualServerRoute(vsr)
  1596  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1597  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1598  	}
  1599  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1600  		t.Errorf("AddOrUpdateVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1601  	}
  1602  }
  1603  
  1604  func TestDeleteNonExistingVirtualServerRoute(t *testing.T) {
  1605  	configuration := createTestConfiguration()
  1606  
  1607  	var expectedChanges []ResourceChange
  1608  	var expectedProblems []ConfigurationProblem
  1609  
  1610  	changes, problems := configuration.DeleteVirtualServerRoute("default/virtualserverroute")
  1611  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1612  		t.Errorf("DeleteVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1613  	}
  1614  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1615  		t.Errorf("DeleteVirtualServerRoute() returned unexpected result (-want +got):\n%s", diff)
  1616  	}
  1617  }
  1618  
  1619  func TestHostCollisions(t *testing.T) {
  1620  	configuration := createTestConfiguration()
  1621  
  1622  	var expectedProblems []ConfigurationProblem
  1623  
  1624  	masterIng := createTestIngressMaster("master-ingress", "foo.example.com")
  1625  	regularIng := createTestIngress("regular-ingress", "foo.example.com", "bar.example.com")
  1626  	vs := createTestVirtualServer("virtualserver", "foo.example.com")
  1627  	regularIng2 := createTestIngress("regular-ingress-2", "foo.example.com")
  1628  	ts := createTestTLSPassthroughTransportServer("transportserver", "foo.example.com")
  1629  
  1630  	// Add TransportServer
  1631  
  1632  	expectedChanges := []ResourceChange{
  1633  		{
  1634  			Op: AddOrUpdate,
  1635  			Resource: &TransportServerConfiguration{
  1636  				ListenerPort:    0,
  1637  				TransportServer: ts,
  1638  			},
  1639  		},
  1640  	}
  1641  	expectedProblems = nil
  1642  
  1643  	changes, problems := configuration.AddOrUpdateTransportServer(ts)
  1644  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1645  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  1646  	}
  1647  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1648  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  1649  	}
  1650  
  1651  	// Add VirtualServer
  1652  
  1653  	expectedChanges = []ResourceChange{
  1654  		{
  1655  			Op: Delete,
  1656  			Resource: &TransportServerConfiguration{
  1657  				ListenerPort:    0,
  1658  				TransportServer: ts,
  1659  				Warnings:        []string{"host foo.example.com is taken by another resource"},
  1660  			},
  1661  		},
  1662  		{
  1663  			Op: AddOrUpdate,
  1664  			Resource: &VirtualServerConfiguration{
  1665  				VirtualServer: vs,
  1666  			},
  1667  		},
  1668  	}
  1669  	expectedProblems = []ConfigurationProblem{
  1670  		{
  1671  			Object:  ts,
  1672  			IsError: false,
  1673  			Reason:  "Rejected",
  1674  			Message: "Host is taken by another resource",
  1675  		},
  1676  	}
  1677  
  1678  	changes, problems = configuration.AddOrUpdateVirtualServer(vs)
  1679  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1680  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1681  	}
  1682  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1683  		t.Errorf("AddOrUpdateVirtualServer() returned unexpected result (-want +got):\n%s", diff)
  1684  	}
  1685  
  1686  	// Add regular Ingress
  1687  
  1688  	expectedChanges = []ResourceChange{
  1689  		{
  1690  			Op: Delete,
  1691  			Resource: &VirtualServerConfiguration{
  1692  				VirtualServer: vs,
  1693  				Warnings:      []string{"host foo.example.com is taken by another resource"},
  1694  			},
  1695  		},
  1696  		{
  1697  			Op: AddOrUpdate,
  1698  			Resource: &IngressConfiguration{
  1699  				Ingress:       regularIng,
  1700  				ValidHosts:    map[string]bool{"foo.example.com": true, "bar.example.com": true},
  1701  				ChildWarnings: map[string][]string{},
  1702  			},
  1703  		},
  1704  	}
  1705  	expectedProblems = []ConfigurationProblem{
  1706  		{
  1707  			Object:  vs,
  1708  			IsError: false,
  1709  			Reason:  "Rejected",
  1710  			Message: "Host is taken by another resource",
  1711  		},
  1712  	}
  1713  
  1714  	changes, problems = configuration.AddOrUpdateIngress(regularIng)
  1715  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1716  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
  1717  	}
  1718  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1719  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
  1720  	}
  1721  
  1722  	// Add master Ingress
  1723  
  1724  	expectedChanges = []ResourceChange{
  1725  		{
  1726  			Op: AddOrUpdate,
  1727  			Resource: &IngressConfiguration{
  1728  				Ingress:       regularIng,
  1729  				ValidHosts:    map[string]bool{"bar.example.com": true, "foo.example.com": false},
  1730  				Warnings:      []string{"host foo.example.com is taken by another resource"},
  1731  				ChildWarnings: map[string][]string{},
  1732  			},
  1733  		},
  1734  		{
  1735  			Op: AddOrUpdate,
  1736  			Resource: &IngressConfiguration{
  1737  				Ingress:       masterIng,
  1738  				IsMaster:      true,
  1739  				ValidHosts:    map[string]bool{"foo.example.com": true},
  1740  				ChildWarnings: map[string][]string{},
  1741  			},
  1742  		},
  1743  	}
  1744  	expectedProblems = nil
  1745  
  1746  	changes, problems = configuration.AddOrUpdateIngress(masterIng)
  1747  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1748  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
  1749  	}
  1750  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1751  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
  1752  	}
  1753  
  1754  	// Add regular Ingress-2
  1755  
  1756  	expectedChanges = nil
  1757  	expectedProblems = []ConfigurationProblem{
  1758  		{
  1759  			Object:  regularIng2,
  1760  			IsError: false,
  1761  			Reason:  "Rejected",
  1762  			Message: "All hosts are taken by other resources",
  1763  		},
  1764  	}
  1765  
  1766  	changes, problems = configuration.AddOrUpdateIngress(regularIng2)
  1767  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1768  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
  1769  	}
  1770  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1771  		t.Errorf("AddOrUpdateIngress() returned unexpected result (-want +got):\n%s", diff)
  1772  	}
  1773  
  1774  	// Delete regular Ingress-2
  1775  	expectedChanges = nil
  1776  	expectedProblems = nil
  1777  
  1778  	changes, problems = configuration.DeleteIngress("default/regular-ingress-2")
  1779  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1780  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
  1781  	}
  1782  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1783  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
  1784  	}
  1785  
  1786  	// Delete master Ingress
  1787  
  1788  	expectedChanges = []ResourceChange{
  1789  		{
  1790  			Op: Delete,
  1791  			Resource: &IngressConfiguration{
  1792  				Ingress:       masterIng,
  1793  				IsMaster:      true,
  1794  				ValidHosts:    map[string]bool{"foo.example.com": true},
  1795  				ChildWarnings: map[string][]string{},
  1796  			},
  1797  		},
  1798  		{
  1799  			Op: AddOrUpdate,
  1800  			Resource: &IngressConfiguration{
  1801  				Ingress:       regularIng,
  1802  				ValidHosts:    map[string]bool{"foo.example.com": true, "bar.example.com": true},
  1803  				ChildWarnings: map[string][]string{},
  1804  			},
  1805  		},
  1806  	}
  1807  	expectedProblems = nil
  1808  
  1809  	changes, problems = configuration.DeleteIngress("default/master-ingress")
  1810  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1811  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
  1812  	}
  1813  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1814  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
  1815  	}
  1816  
  1817  	// Delete regular Ingress
  1818  
  1819  	expectedChanges = []ResourceChange{
  1820  		{
  1821  			Op: Delete,
  1822  			Resource: &IngressConfiguration{
  1823  				Ingress:       regularIng,
  1824  				ValidHosts:    map[string]bool{"foo.example.com": true, "bar.example.com": true},
  1825  				ChildWarnings: map[string][]string{},
  1826  			},
  1827  		},
  1828  		{
  1829  			Op: AddOrUpdate,
  1830  			Resource: &VirtualServerConfiguration{
  1831  				VirtualServer: vs,
  1832  			},
  1833  		},
  1834  	}
  1835  	expectedProblems = nil
  1836  
  1837  	changes, problems = configuration.DeleteIngress("default/regular-ingress")
  1838  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1839  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
  1840  	}
  1841  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1842  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
  1843  	}
  1844  
  1845  	// Delete VirtualServer
  1846  
  1847  	expectedChanges = []ResourceChange{
  1848  		{
  1849  			Op: Delete,
  1850  			Resource: &VirtualServerConfiguration{
  1851  				VirtualServer: vs,
  1852  			},
  1853  		},
  1854  		{
  1855  			Op: AddOrUpdate,
  1856  			Resource: &TransportServerConfiguration{
  1857  				ListenerPort:    0,
  1858  				TransportServer: ts,
  1859  			},
  1860  		},
  1861  	}
  1862  	expectedProblems = nil
  1863  
  1864  	changes, problems = configuration.DeleteVirtualServer("default/virtualserver")
  1865  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1866  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
  1867  	}
  1868  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1869  		t.Errorf("DeleteIngress() returned unexpected result (-want +got):\n%s", diff)
  1870  	}
  1871  }
  1872  
  1873  func TestAddTransportServer(t *testing.T) {
  1874  	configuration := createTestConfiguration()
  1875  
  1876  	listeners := []conf_v1alpha1.Listener{
  1877  		{
  1878  			Name:     "tcp-7777",
  1879  			Port:     7777,
  1880  			Protocol: "TCP",
  1881  		},
  1882  	}
  1883  	gc := createTestGlobalConfiguration(listeners)
  1884  	mustInitGlobalConfiguration(configuration, gc)
  1885  
  1886  	ts := createTestTransportServer("transportserver", "tcp-7777", "TCP")
  1887  
  1888  	// no problems are expected for all cases
  1889  	var expectedProblems []ConfigurationProblem
  1890  	var expectedChanges []ResourceChange
  1891  
  1892  	// Add TransportServer
  1893  
  1894  	expectedChanges = []ResourceChange{
  1895  		{
  1896  			Op: AddOrUpdate,
  1897  			Resource: &TransportServerConfiguration{
  1898  				ListenerPort:    7777,
  1899  				TransportServer: ts,
  1900  			},
  1901  		},
  1902  	}
  1903  
  1904  	changes, problems := configuration.AddOrUpdateTransportServer(ts)
  1905  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1906  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  1907  	}
  1908  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1909  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  1910  	}
  1911  
  1912  	// Update TransportServer
  1913  
  1914  	updatedTS := ts.DeepCopy()
  1915  	updatedTS.Generation++
  1916  
  1917  	expectedChanges = []ResourceChange{
  1918  		{
  1919  			Op: AddOrUpdate,
  1920  			Resource: &TransportServerConfiguration{
  1921  				ListenerPort:    7777,
  1922  				TransportServer: updatedTS,
  1923  			},
  1924  		},
  1925  	}
  1926  
  1927  	changes, problems = configuration.AddOrUpdateTransportServer(updatedTS)
  1928  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1929  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  1930  	}
  1931  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1932  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  1933  	}
  1934  
  1935  	// Make TransportServer invalid
  1936  
  1937  	invalidTS := updatedTS.DeepCopy()
  1938  	invalidTS.Generation++
  1939  	invalidTS.Spec.Upstreams = nil
  1940  
  1941  	expectedChanges = []ResourceChange{
  1942  		{
  1943  			Op: Delete,
  1944  			Resource: &TransportServerConfiguration{
  1945  				ListenerPort:    7777,
  1946  				TransportServer: updatedTS,
  1947  			},
  1948  			Error: `spec.action.pass: Not found: "myapp"`,
  1949  		},
  1950  	}
  1951  
  1952  	changes, problems = configuration.AddOrUpdateTransportServer(invalidTS)
  1953  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1954  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  1955  	}
  1956  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1957  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  1958  	}
  1959  
  1960  	// Restore TransportServer
  1961  
  1962  	expectedChanges = []ResourceChange{
  1963  		{
  1964  			Op: AddOrUpdate,
  1965  			Resource: &TransportServerConfiguration{
  1966  				ListenerPort:    7777,
  1967  				TransportServer: updatedTS,
  1968  			},
  1969  		},
  1970  	}
  1971  
  1972  	changes, problems = configuration.AddOrUpdateTransportServer(updatedTS)
  1973  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1974  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  1975  	}
  1976  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1977  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  1978  	}
  1979  
  1980  	// Delete TransportServer
  1981  
  1982  	expectedChanges = []ResourceChange{
  1983  		{
  1984  			Op: Delete,
  1985  			Resource: &TransportServerConfiguration{
  1986  				ListenerPort:    7777,
  1987  				TransportServer: updatedTS,
  1988  			},
  1989  		},
  1990  	}
  1991  
  1992  	changes, problems = configuration.DeleteTransportServer("default/transportserver")
  1993  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  1994  		t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff)
  1995  	}
  1996  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  1997  		t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff)
  1998  	}
  1999  }
  2000  
  2001  func TestAddTransportServerForTLSPassthrough(t *testing.T) {
  2002  	configuration := createTestConfiguration()
  2003  
  2004  	ts := createTestTLSPassthroughTransportServer("transportserver", "foo.example.com")
  2005  
  2006  	// no problems are expected for all cases
  2007  	var expectedProblems []ConfigurationProblem
  2008  
  2009  	// Add TransportServer
  2010  
  2011  	expectedChanges := []ResourceChange{
  2012  		{
  2013  			Op: AddOrUpdate,
  2014  			Resource: &TransportServerConfiguration{
  2015  				ListenerPort:    0,
  2016  				TransportServer: ts,
  2017  			},
  2018  		},
  2019  	}
  2020  
  2021  	changes, problems := configuration.AddOrUpdateTransportServer(ts)
  2022  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2023  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2024  	}
  2025  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2026  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2027  	}
  2028  
  2029  	// DeleteTransportServer
  2030  
  2031  	expectedChanges = []ResourceChange{
  2032  		{
  2033  			Op: Delete,
  2034  			Resource: &TransportServerConfiguration{
  2035  				ListenerPort:    0,
  2036  				TransportServer: ts,
  2037  			},
  2038  		},
  2039  	}
  2040  
  2041  	changes, problems = configuration.DeleteTransportServer("default/transportserver")
  2042  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2043  		t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2044  	}
  2045  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2046  		t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2047  	}
  2048  }
  2049  
  2050  func TestListenerFlip(t *testing.T) {
  2051  	configuration := createTestConfiguration()
  2052  
  2053  	listeners := []conf_v1alpha1.Listener{
  2054  		{
  2055  			Name:     "tcp-7777",
  2056  			Port:     7777,
  2057  			Protocol: "TCP",
  2058  		},
  2059  		{
  2060  			Name:     "tcp-8888",
  2061  			Port:     8888,
  2062  			Protocol: "TCP",
  2063  		},
  2064  	}
  2065  	gc := createTestGlobalConfiguration(listeners)
  2066  	mustInitGlobalConfiguration(configuration, gc)
  2067  
  2068  	ts := createTestTransportServer("transportserver", "tcp-7777", "TCP")
  2069  
  2070  	// no problems are expected for all cases
  2071  	var expectedProblems []ConfigurationProblem
  2072  	var expectedChanges []ResourceChange
  2073  
  2074  	// Add TransportServer
  2075  
  2076  	expectedChanges = []ResourceChange{
  2077  		{
  2078  			Op: AddOrUpdate,
  2079  			Resource: &TransportServerConfiguration{
  2080  				ListenerPort:    7777,
  2081  				TransportServer: ts,
  2082  			},
  2083  		},
  2084  	}
  2085  
  2086  	changes, problems := configuration.AddOrUpdateTransportServer(ts)
  2087  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2088  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2089  	}
  2090  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2091  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2092  	}
  2093  
  2094  	// Update TransportServer listener
  2095  
  2096  	updatedListenerTS := ts.DeepCopy()
  2097  	updatedListenerTS.Generation++
  2098  	updatedListenerTS.Spec.Listener.Name = "tcp-8888"
  2099  
  2100  	expectedChanges = []ResourceChange{
  2101  		{
  2102  			Op: AddOrUpdate,
  2103  			Resource: &TransportServerConfiguration{
  2104  				ListenerPort:    8888,
  2105  				TransportServer: updatedListenerTS,
  2106  			},
  2107  		},
  2108  	}
  2109  
  2110  	changes, problems = configuration.AddOrUpdateTransportServer(updatedListenerTS)
  2111  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2112  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2113  	}
  2114  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2115  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2116  	}
  2117  
  2118  	// Update TransportSever listener to TLS Passthrough
  2119  
  2120  	updatedWithPassthroughTS := updatedListenerTS.DeepCopy()
  2121  	updatedWithPassthroughTS.Generation++
  2122  	updatedWithPassthroughTS.Spec.Listener.Name = "tls-passthrough"
  2123  	updatedWithPassthroughTS.Spec.Listener.Protocol = "TLS_PASSTHROUGH"
  2124  	updatedWithPassthroughTS.Spec.Host = "example.com"
  2125  
  2126  	expectedChanges = []ResourceChange{
  2127  		{
  2128  			Op: Delete,
  2129  			Resource: &TransportServerConfiguration{
  2130  				ListenerPort:    8888,
  2131  				TransportServer: updatedListenerTS,
  2132  			},
  2133  		},
  2134  		{
  2135  			Op: AddOrUpdate,
  2136  			Resource: &TransportServerConfiguration{
  2137  				ListenerPort:    0,
  2138  				TransportServer: updatedWithPassthroughTS,
  2139  			},
  2140  		},
  2141  	}
  2142  
  2143  	changes, problems = configuration.AddOrUpdateTransportServer(updatedWithPassthroughTS)
  2144  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2145  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2146  	}
  2147  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2148  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2149  	}
  2150  }
  2151  
  2152  func TestAddInvalidTransportServer(t *testing.T) {
  2153  	configuration := createTestConfiguration()
  2154  
  2155  	ts := createTestTransportServer("transportserver", "", "TCP")
  2156  
  2157  	expectedProblems := []ConfigurationProblem{
  2158  		{
  2159  			Object:  ts,
  2160  			IsError: true,
  2161  			Reason:  "Rejected",
  2162  			Message: "TransportServer default/transportserver was rejected with error: spec.listener.name: Required value",
  2163  		},
  2164  	}
  2165  	var expectedChanges []ResourceChange
  2166  
  2167  	changes, problems := configuration.AddOrUpdateTransportServer(ts)
  2168  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2169  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2170  	}
  2171  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2172  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2173  	}
  2174  }
  2175  
  2176  func TestAddTransportServerWithIncorrectClass(t *testing.T) {
  2177  	configuration := createTestConfiguration()
  2178  
  2179  	// Add TransportServer with incorrect class
  2180  
  2181  	ts := createTestTLSPassthroughTransportServer("transportserver", "foo.example.com")
  2182  	ts.Spec.IngressClass = "someproxy"
  2183  
  2184  	var expectedProblems []ConfigurationProblem
  2185  	var expectedChanges []ResourceChange
  2186  
  2187  	changes, problems := configuration.AddOrUpdateTransportServer(ts)
  2188  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2189  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2190  	}
  2191  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2192  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2193  	}
  2194  
  2195  	// Make the class correct
  2196  
  2197  	updatedTS := ts.DeepCopy()
  2198  	updatedTS.Generation++
  2199  	updatedTS.Spec.IngressClass = "nginx"
  2200  
  2201  	expectedChanges = []ResourceChange{
  2202  		{
  2203  			Op: AddOrUpdate,
  2204  			Resource: &TransportServerConfiguration{
  2205  				TransportServer: updatedTS,
  2206  			},
  2207  		},
  2208  	}
  2209  	expectedProblems = nil
  2210  
  2211  	changes, problems = configuration.AddOrUpdateTransportServer(updatedTS)
  2212  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2213  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2214  	}
  2215  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2216  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2217  	}
  2218  
  2219  	// Make the class incorrect
  2220  
  2221  	expectedChanges = []ResourceChange{
  2222  		{
  2223  			Op: Delete,
  2224  			Resource: &TransportServerConfiguration{
  2225  				TransportServer: updatedTS,
  2226  			},
  2227  		},
  2228  	}
  2229  	expectedProblems = nil
  2230  
  2231  	changes, problems = configuration.AddOrUpdateTransportServer(ts)
  2232  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2233  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2234  	}
  2235  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2236  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2237  	}
  2238  }
  2239  
  2240  func TestAddTransportServerWithNonExistingListener(t *testing.T) {
  2241  	configuration := createTestConfiguration()
  2242  
  2243  	gc := createTestGlobalConfiguration([]conf_v1alpha1.Listener{})
  2244  	mustInitGlobalConfiguration(configuration, gc)
  2245  
  2246  	ts := createTestTransportServer("transportserver", "tcp-7777", "TCP")
  2247  
  2248  	expectedProblems := []ConfigurationProblem{
  2249  		{
  2250  			Object:  ts,
  2251  			IsError: false,
  2252  			Reason:  "Rejected",
  2253  			Message: `Listener tcp-7777 doesn't exist`,
  2254  		},
  2255  	}
  2256  	var expectedChanges []ResourceChange
  2257  
  2258  	changes, problems := configuration.AddOrUpdateTransportServer(ts)
  2259  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2260  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2261  	}
  2262  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2263  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2264  	}
  2265  }
  2266  
  2267  func TestDeleteNonExistingTransportServer(t *testing.T) {
  2268  	configuration := createTestConfiguration()
  2269  
  2270  	var expectedChanges []ResourceChange
  2271  	var expectedProblems []ConfigurationProblem
  2272  
  2273  	changes, problems := configuration.DeleteTransportServer("default/transportserver")
  2274  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2275  		t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2276  	}
  2277  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2278  		t.Errorf("DeleteTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2279  	}
  2280  }
  2281  
  2282  func TestAddGlobalConfiguration(t *testing.T) {
  2283  	configuration := createTestConfiguration()
  2284  
  2285  	listeners := []conf_v1alpha1.Listener{
  2286  		{
  2287  			Name:     "tcp-7777",
  2288  			Port:     7777,
  2289  			Protocol: "TCP",
  2290  		},
  2291  		{
  2292  			Name:     "tcp-8888",
  2293  			Port:     8888,
  2294  			Protocol: "TCP",
  2295  		},
  2296  	}
  2297  	gc := createTestGlobalConfiguration(listeners)
  2298  
  2299  	var expectedChanges []ResourceChange
  2300  	var expectedProblems []ConfigurationProblem
  2301  
  2302  	changes, problems, err := configuration.AddOrUpdateGlobalConfiguration(gc)
  2303  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2304  		t.Errorf("AddOrUpdateGlobalConfiguration() returned unexpected result (-want +got):\n%s", diff)
  2305  	}
  2306  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2307  		t.Errorf("AddOrUpdateGlobalConfiguration() returned unexpected result (-want +got):\n%s", diff)
  2308  	}
  2309  	if err != nil {
  2310  		t.Errorf("AddOrUpdateGlobalConfiguration() returned unexpected error: %v", err)
  2311  	}
  2312  
  2313  	ts1 := createTestTransportServer("transportserver-1", "tcp-7777", "TCP")
  2314  	ts2 := createTestTransportServer("transportserver-2", "tcp-8888", "TCP")
  2315  
  2316  	// Add first TransportServer
  2317  
  2318  	expectedChanges = []ResourceChange{
  2319  		{
  2320  			Op: AddOrUpdate,
  2321  			Resource: &TransportServerConfiguration{
  2322  				ListenerPort:    7777,
  2323  				TransportServer: ts1,
  2324  			},
  2325  		},
  2326  	}
  2327  	expectedProblems = nil
  2328  
  2329  	changes, problems = configuration.AddOrUpdateTransportServer(ts1)
  2330  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2331  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2332  	}
  2333  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2334  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2335  	}
  2336  
  2337  	// Update GlobalConfiguration
  2338  
  2339  	updatedGC1 := gc.DeepCopy()
  2340  	updatedGC1.Spec.Listeners[0].Port = 7000
  2341  
  2342  	expectedChanges = []ResourceChange{
  2343  		{
  2344  			Op: AddOrUpdate,
  2345  			Resource: &TransportServerConfiguration{
  2346  				ListenerPort:    7000,
  2347  				TransportServer: ts1,
  2348  			},
  2349  		},
  2350  	}
  2351  	expectedProblems = nil
  2352  
  2353  	changes, problems, err = configuration.AddOrUpdateGlobalConfiguration(updatedGC1)
  2354  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2355  		t.Errorf("AddOrUpdateGlobalConfiguration() returned unexpected result (-want +got):\n%s", diff)
  2356  	}
  2357  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2358  		t.Errorf("AddOrUpdateGlobalConfiguration() returned unexpected result (-want +got):\n%s", diff)
  2359  	}
  2360  	if err != nil {
  2361  		t.Errorf("AddOrUpdateGlobalConfiguration() returned unexpected error: %v", err)
  2362  	}
  2363  
  2364  	// Add second TransportServer
  2365  
  2366  	expectedChanges = []ResourceChange{
  2367  		{
  2368  			Op: AddOrUpdate,
  2369  			Resource: &TransportServerConfiguration{
  2370  				ListenerPort:    8888,
  2371  				TransportServer: ts2,
  2372  			},
  2373  		},
  2374  	}
  2375  	expectedProblems = nil
  2376  
  2377  	changes, problems = configuration.AddOrUpdateTransportServer(ts2)
  2378  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2379  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2380  	}
  2381  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2382  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2383  	}
  2384  
  2385  	// Swap listeners
  2386  
  2387  	// We need to hanlde this case in Controller propoperly - update config for all TransportServers and reload once
  2388  	// to avoid any race conditions
  2389  	// and errors like nginx: [emerg] duplicate "0.0.0.0:8888" address and port pair in /etc/nginx/nginx.conf:73
  2390  
  2391  	updatedGC2 := updatedGC1.DeepCopy()
  2392  	updatedGC2.Spec.Listeners[0].Port = 8888
  2393  	updatedGC2.Spec.Listeners[1].Port = 7000
  2394  
  2395  	expectedChanges = []ResourceChange{
  2396  		{
  2397  			Op: AddOrUpdate,
  2398  			Resource: &TransportServerConfiguration{
  2399  				ListenerPort:    8888,
  2400  				TransportServer: ts1,
  2401  			},
  2402  		},
  2403  		{
  2404  			Op: AddOrUpdate,
  2405  			Resource: &TransportServerConfiguration{
  2406  				ListenerPort:    7000,
  2407  				TransportServer: ts2,
  2408  			},
  2409  		},
  2410  	}
  2411  	expectedProblems = nil
  2412  
  2413  	changes, problems, err = configuration.AddOrUpdateGlobalConfiguration(updatedGC2)
  2414  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2415  		t.Errorf("AddOrUpdateGlobalConfiguration() returned unexpected result (-want +got):\n%s", diff)
  2416  	}
  2417  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2418  		t.Errorf("AddOrUpdateGlobalConfiguration() returned unexpected result (-want +got):\n%s", diff)
  2419  	}
  2420  	if err != nil {
  2421  		t.Errorf("AddOrUpdateGlobalConfiguration() returned unexpected error: %v", err)
  2422  	}
  2423  
  2424  	// Make GlobalConfiguration Invalid
  2425  
  2426  	invalidGC := updatedGC2.DeepCopy()
  2427  	invalidGC.Spec.Listeners[0].Port = -1
  2428  
  2429  	expectedChanges = []ResourceChange{
  2430  		{
  2431  			Op: Delete,
  2432  			Resource: &TransportServerConfiguration{
  2433  				ListenerPort:    8888,
  2434  				TransportServer: ts1,
  2435  			},
  2436  		},
  2437  		{
  2438  			Op: Delete,
  2439  			Resource: &TransportServerConfiguration{
  2440  				ListenerPort:    7000,
  2441  				TransportServer: ts2,
  2442  			},
  2443  		},
  2444  	}
  2445  	expectedProblems = []ConfigurationProblem{
  2446  		{
  2447  			Object:  ts1,
  2448  			IsError: false,
  2449  			Reason:  "Rejected",
  2450  			Message: "Listener tcp-7777 doesn't exist",
  2451  		},
  2452  		{
  2453  			Object:  ts2,
  2454  			IsError: false,
  2455  			Reason:  "Rejected",
  2456  			Message: "Listener tcp-8888 doesn't exist",
  2457  		},
  2458  	}
  2459  	expectedErrMsg := "spec.listeners[0].port: Invalid value: -1: must be between 1 and 65535, inclusive"
  2460  
  2461  	changes, problems, err = configuration.AddOrUpdateGlobalConfiguration(invalidGC)
  2462  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2463  		t.Errorf("AddOrUpdateGlobalConfiguration() returned unexpected result (-want +got):\n%s", diff)
  2464  	}
  2465  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2466  		t.Errorf("AddOrUpdateGlobalConfiguration() returned unexpected result (-want +got):\n%s", diff)
  2467  	}
  2468  	if err.Error() != expectedErrMsg {
  2469  		t.Errorf("AddOrUpdateGlobalConfiguration() returned error %v but expected %v", err, expectedErrMsg)
  2470  	}
  2471  
  2472  	// Restore
  2473  
  2474  	expectedChanges = []ResourceChange{
  2475  		{
  2476  			Op: AddOrUpdate,
  2477  			Resource: &TransportServerConfiguration{
  2478  				ListenerPort:    8888,
  2479  				TransportServer: ts1,
  2480  			},
  2481  		},
  2482  		{
  2483  			Op: AddOrUpdate,
  2484  			Resource: &TransportServerConfiguration{
  2485  				ListenerPort:    7000,
  2486  				TransportServer: ts2,
  2487  			},
  2488  		},
  2489  	}
  2490  	expectedProblems = nil
  2491  
  2492  	changes, problems, err = configuration.AddOrUpdateGlobalConfiguration(updatedGC2)
  2493  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2494  		t.Errorf("AddOrUpdateGlobalConfiguration() returned unexpected result (-want +got):\n%s", diff)
  2495  	}
  2496  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2497  		t.Errorf("AddOrUpdateGlobalConfiguration() returned unexpected result (-want +got):\n%s", diff)
  2498  	}
  2499  	if err != nil {
  2500  		t.Errorf("AddOrUpdateGlobalConfiguration() returned unexpected error: %v", err)
  2501  	}
  2502  
  2503  	// Delete GlobalConfiguration
  2504  
  2505  	expectedChanges = []ResourceChange{
  2506  		{
  2507  			Op: Delete,
  2508  			Resource: &TransportServerConfiguration{
  2509  				ListenerPort:    8888,
  2510  				TransportServer: ts1,
  2511  			},
  2512  		},
  2513  		{
  2514  			Op: Delete,
  2515  			Resource: &TransportServerConfiguration{
  2516  				ListenerPort:    7000,
  2517  				TransportServer: ts2,
  2518  			},
  2519  		},
  2520  	}
  2521  	expectedProblems = []ConfigurationProblem{
  2522  		{
  2523  			Object:  ts1,
  2524  			IsError: false,
  2525  			Reason:  "Rejected",
  2526  			Message: "Listener tcp-7777 doesn't exist",
  2527  		},
  2528  		{
  2529  			Object:  ts2,
  2530  			IsError: false,
  2531  			Reason:  "Rejected",
  2532  			Message: "Listener tcp-8888 doesn't exist",
  2533  		},
  2534  	}
  2535  
  2536  	changes, problems = configuration.DeleteGlobalConfiguration()
  2537  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2538  		t.Errorf("DeleteGlobalConfiguration() returned unexpected result (-want +got):\n%s", diff)
  2539  	}
  2540  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2541  		t.Errorf("DeleteGlobalConfiguration() returned unexpected result (-want +got):\n%s", diff)
  2542  	}
  2543  }
  2544  
  2545  func TestPortCollisions(t *testing.T) {
  2546  	configuration := createTestConfiguration()
  2547  
  2548  	listeners := []conf_v1alpha1.Listener{
  2549  		{
  2550  			Name:     "tcp-7777",
  2551  			Port:     7777,
  2552  			Protocol: "TCP",
  2553  		},
  2554  	}
  2555  	gc := createTestGlobalConfiguration(listeners)
  2556  	mustInitGlobalConfiguration(configuration, gc)
  2557  
  2558  	var expectedChanges []ResourceChange
  2559  	var expectedProblems []ConfigurationProblem
  2560  
  2561  	ts1 := createTestTransportServer("transportserver-1", "tcp-7777", "TCP")
  2562  	ts2 := createTestTransportServer("transportserver-2", "tcp-7777", "TCP")
  2563  	ts3 := createTestTransportServer("transportserver-3", "tcp-7777", "TCP")
  2564  
  2565  	expectedChanges = []ResourceChange{
  2566  		{
  2567  			Op: AddOrUpdate,
  2568  			Resource: &TransportServerConfiguration{
  2569  				ListenerPort:    7777,
  2570  				TransportServer: ts1,
  2571  			},
  2572  		},
  2573  	}
  2574  	expectedProblems = nil
  2575  
  2576  	// Add first TransportServer
  2577  
  2578  	changes, problems := configuration.AddOrUpdateTransportServer(ts1)
  2579  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2580  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2581  	}
  2582  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2583  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2584  	}
  2585  
  2586  	// Add second TransportServer
  2587  
  2588  	expectedChanges = nil
  2589  	expectedProblems = []ConfigurationProblem{
  2590  		{
  2591  			Object:  ts2,
  2592  			IsError: false,
  2593  			Reason:  "Rejected",
  2594  			Message: "Listener tcp-7777 is taken by another resource",
  2595  		},
  2596  	}
  2597  
  2598  	changes, problems = configuration.AddOrUpdateTransportServer(ts2)
  2599  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2600  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2601  	}
  2602  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2603  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2604  	}
  2605  
  2606  	// Add third TransportServer
  2607  
  2608  	expectedChanges = nil
  2609  	expectedProblems = []ConfigurationProblem{
  2610  		{
  2611  			Object:  ts3,
  2612  			IsError: false,
  2613  			Reason:  "Rejected",
  2614  			Message: "Listener tcp-7777 is taken by another resource",
  2615  		},
  2616  	}
  2617  
  2618  	changes, problems = configuration.AddOrUpdateTransportServer(ts3)
  2619  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2620  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2621  	}
  2622  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2623  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2624  	}
  2625  
  2626  	// Remove first TransportServer
  2627  
  2628  	expectedChanges = []ResourceChange{
  2629  		{
  2630  			Op: Delete,
  2631  			Resource: &TransportServerConfiguration{
  2632  				ListenerPort:    7777,
  2633  				TransportServer: ts1,
  2634  			},
  2635  		},
  2636  		{
  2637  			Op: AddOrUpdate,
  2638  			Resource: &TransportServerConfiguration{
  2639  				ListenerPort:    7777,
  2640  				TransportServer: ts2,
  2641  			},
  2642  		},
  2643  	}
  2644  	expectedProblems = nil
  2645  
  2646  	changes, problems = configuration.DeleteTransportServer("default/transportserver-1")
  2647  	if diff := cmp.Diff(expectedChanges, changes); diff != "" {
  2648  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2649  	}
  2650  	if diff := cmp.Diff(expectedProblems, problems); diff != "" {
  2651  		t.Errorf("AddOrUpdateTransportServer() returned unexpected result (-want +got):\n%s", diff)
  2652  	}
  2653  }
  2654  
  2655  func mustInitGlobalConfiguration(c *Configuration, gc *conf_v1alpha1.GlobalConfiguration) {
  2656  	changes, problems, err := c.AddOrUpdateGlobalConfiguration(gc)
  2657  
  2658  	// when adding a valid GlobalConfiguration to a new Configuration, no changes, problems and errors are expected
  2659  
  2660  	if len(changes) > 0 {
  2661  		panic(fmt.Sprintf("AddOrUpdateGlobalConfiguration() returned %d changes, expected 0", len(changes)))
  2662  	}
  2663  	if len(problems) > 0 {
  2664  		panic(fmt.Sprintf("AddOrUpdateGlobalConfiguration() returned %d problems, expected 0", len(problems)))
  2665  	}
  2666  	if err != nil {
  2667  		panic(fmt.Sprintf("AddOrUpdateGlobalConfiguration() returned an unexpected error %v", err))
  2668  	}
  2669  }
  2670  
  2671  func createTestIngressMaster(name string, host string) *networking.Ingress {
  2672  	ing := createTestIngress(name, host)
  2673  	ing.Annotations["nginx.org/mergeable-ingress-type"] = "master"
  2674  	return ing
  2675  }
  2676  
  2677  func createTestIngressMinion(name string, host string, path string) *networking.Ingress {
  2678  	ing := createTestIngress(name, host)
  2679  	ing.Spec.Rules[0].IngressRuleValue = networking.IngressRuleValue{
  2680  		HTTP: &networking.HTTPIngressRuleValue{
  2681  			Paths: []networking.HTTPIngressPath{
  2682  				{
  2683  					Path: path,
  2684  				},
  2685  			},
  2686  		},
  2687  	}
  2688  
  2689  	ing.Annotations["nginx.org/mergeable-ingress-type"] = "minion"
  2690  
  2691  	return ing
  2692  }
  2693  
  2694  func createTestIngress(name string, hosts ...string) *networking.Ingress {
  2695  	var rules []networking.IngressRule
  2696  
  2697  	for _, h := range hosts {
  2698  		rules = append(rules, networking.IngressRule{
  2699  			Host:             h,
  2700  			IngressRuleValue: networking.IngressRuleValue{},
  2701  		})
  2702  	}
  2703  
  2704  	return &networking.Ingress{
  2705  		ObjectMeta: metav1.ObjectMeta{
  2706  			Name:              name,
  2707  			Namespace:         "default",
  2708  			CreationTimestamp: metav1.Now(),
  2709  			Annotations: map[string]string{
  2710  				"kubernetes.io/ingress.class": "nginx",
  2711  			},
  2712  		},
  2713  		Spec: networking.IngressSpec{
  2714  			Rules: rules,
  2715  		},
  2716  	}
  2717  }
  2718  
  2719  func createTestVirtualServer(name string, host string) *conf_v1.VirtualServer {
  2720  	return &conf_v1.VirtualServer{
  2721  		ObjectMeta: metav1.ObjectMeta{
  2722  			Namespace:         "default",
  2723  			Name:              name,
  2724  			CreationTimestamp: metav1.Now(),
  2725  		},
  2726  		Spec: conf_v1.VirtualServerSpec{
  2727  			IngressClass: "nginx",
  2728  			Host:         host,
  2729  		},
  2730  	}
  2731  }
  2732  
  2733  func createTestVirtualServerWithRoutes(name string, host string, routes []conf_v1.Route) *conf_v1.VirtualServer {
  2734  	vs := createTestVirtualServer(name, host)
  2735  	vs.Spec.Routes = routes
  2736  	return vs
  2737  }
  2738  
  2739  func createTestVirtualServerRoute(name string, host string, path string) *conf_v1.VirtualServerRoute {
  2740  	return &conf_v1.VirtualServerRoute{
  2741  		ObjectMeta: metav1.ObjectMeta{
  2742  			Namespace: "default",
  2743  			Name:      name,
  2744  		},
  2745  		Spec: conf_v1.VirtualServerRouteSpec{
  2746  			IngressClass: "nginx",
  2747  			Host:         host,
  2748  			Subroutes: []conf_v1.Route{
  2749  				{
  2750  					Path: path,
  2751  					Action: &conf_v1.Action{
  2752  						Return: &conf_v1.ActionReturn{
  2753  							Body: "vsr",
  2754  						},
  2755  					},
  2756  				},
  2757  			},
  2758  		},
  2759  	}
  2760  }
  2761  
  2762  func createTestTransportServer(name string, listenerName string, listenerProtocol string) *conf_v1alpha1.TransportServer {
  2763  	return &conf_v1alpha1.TransportServer{
  2764  		ObjectMeta: metav1.ObjectMeta{
  2765  			Name:              name,
  2766  			Namespace:         "default",
  2767  			CreationTimestamp: metav1.Now(),
  2768  			Generation:        1,
  2769  		},
  2770  		Spec: conf_v1alpha1.TransportServerSpec{
  2771  			Listener: conf_v1alpha1.TransportServerListener{
  2772  				Name:     listenerName,
  2773  				Protocol: listenerProtocol,
  2774  			},
  2775  			Upstreams: []conf_v1alpha1.Upstream{
  2776  				{
  2777  					Name:    "myapp",
  2778  					Service: "myapp-svc",
  2779  					Port:    1234,
  2780  				},
  2781  			},
  2782  			Action: &conf_v1alpha1.Action{
  2783  				Pass: "myapp",
  2784  			},
  2785  		},
  2786  	}
  2787  }
  2788  
  2789  func createTestTLSPassthroughTransportServer(name string, host string) *conf_v1alpha1.TransportServer {
  2790  	ts := createTestTransportServer(name, conf_v1alpha1.TLSPassthroughListenerName, conf_v1alpha1.TLSPassthroughListenerProtocol)
  2791  	ts.Spec.Host = host
  2792  
  2793  	return ts
  2794  }
  2795  
  2796  func createTestGlobalConfiguration(listeners []conf_v1alpha1.Listener) *conf_v1alpha1.GlobalConfiguration {
  2797  	return &conf_v1alpha1.GlobalConfiguration{
  2798  		ObjectMeta: metav1.ObjectMeta{
  2799  			Name:      "globalconfiguration",
  2800  			Namespace: "nginx-ingress",
  2801  		},
  2802  		Spec: conf_v1alpha1.GlobalConfigurationSpec{
  2803  			Listeners: listeners,
  2804  		},
  2805  	}
  2806  }
  2807  
  2808  func TestChooseObjectMetaWinner(t *testing.T) {
  2809  	now := metav1.Now()
  2810  	afterNow := metav1.NewTime(now.Add(1 * time.Second))
  2811  
  2812  	tests := []struct {
  2813  		meta1    *metav1.ObjectMeta
  2814  		meta2    *metav1.ObjectMeta
  2815  		msg      string
  2816  		expected bool
  2817  	}{
  2818  		{
  2819  			meta1: &metav1.ObjectMeta{
  2820  				UID:               "a",
  2821  				CreationTimestamp: now,
  2822  			},
  2823  			meta2: &metav1.ObjectMeta{
  2824  				UID:               "b",
  2825  				CreationTimestamp: afterNow,
  2826  			},
  2827  			msg:      "first is older",
  2828  			expected: true,
  2829  		},
  2830  		{
  2831  			meta1: &metav1.ObjectMeta{
  2832  				UID:               "a",
  2833  				CreationTimestamp: afterNow,
  2834  			},
  2835  			meta2: &metav1.ObjectMeta{
  2836  				UID:               "b",
  2837  				CreationTimestamp: now,
  2838  			},
  2839  			msg:      "second is older",
  2840  			expected: false,
  2841  		},
  2842  		{
  2843  			meta1: &metav1.ObjectMeta{
  2844  				UID:               "a",
  2845  				CreationTimestamp: now,
  2846  			},
  2847  			meta2: &metav1.ObjectMeta{
  2848  				UID:               "b",
  2849  				CreationTimestamp: now,
  2850  			},
  2851  			msg:      "both not older, but second wins",
  2852  			expected: false,
  2853  		},
  2854  	}
  2855  
  2856  	for _, test := range tests {
  2857  		result := chooseObjectMetaWinner(test.meta1, test.meta2)
  2858  		if result != test.expected {
  2859  			t.Errorf("chooseObjectMetaWinner() returned %v but expected %v for the case %s", result, test.expected, test.msg)
  2860  		}
  2861  	}
  2862  }
  2863  
  2864  func TestSquashResourceChanges(t *testing.T) {
  2865  	ingConfig := &IngressConfiguration{
  2866  		Ingress: createTestIngress("test", "foo.example.com"),
  2867  	}
  2868  
  2869  	vsConfig := &VirtualServerConfiguration{
  2870  		VirtualServer: createTestVirtualServer("test", "bar.example.com"),
  2871  	}
  2872  
  2873  	tests := []struct {
  2874  		changes  []ResourceChange
  2875  		expected []ResourceChange
  2876  		msg      string
  2877  	}{
  2878  		{
  2879  			changes: []ResourceChange{
  2880  				{
  2881  					Op:       Delete,
  2882  					Resource: ingConfig,
  2883  				},
  2884  				{
  2885  					Op:       Delete,
  2886  					Resource: ingConfig,
  2887  				},
  2888  			},
  2889  			expected: []ResourceChange{
  2890  				{
  2891  					Op:       Delete,
  2892  					Resource: ingConfig,
  2893  				},
  2894  			},
  2895  			msg: "squash deletes",
  2896  		},
  2897  		{
  2898  			changes: []ResourceChange{
  2899  				{
  2900  					Op:       AddOrUpdate,
  2901  					Resource: ingConfig,
  2902  				},
  2903  				{
  2904  					Op:       AddOrUpdate,
  2905  					Resource: ingConfig,
  2906  				},
  2907  			},
  2908  			expected: []ResourceChange{
  2909  				{
  2910  					Op:       AddOrUpdate,
  2911  					Resource: ingConfig,
  2912  				},
  2913  			},
  2914  			msg: "squash updates",
  2915  		},
  2916  		{
  2917  			changes: []ResourceChange{
  2918  				{
  2919  					Op:       Delete,
  2920  					Resource: ingConfig,
  2921  				},
  2922  				{
  2923  					Op:       AddOrUpdate,
  2924  					Resource: ingConfig,
  2925  				},
  2926  			},
  2927  			expected: []ResourceChange{
  2928  				{
  2929  					Op:       AddOrUpdate,
  2930  					Resource: ingConfig,
  2931  				},
  2932  			},
  2933  			msg: "squash update and delete",
  2934  		},
  2935  		{
  2936  			changes: []ResourceChange{
  2937  				{
  2938  					Op:       Delete,
  2939  					Resource: vsConfig,
  2940  				},
  2941  				{
  2942  					Op:       AddOrUpdate,
  2943  					Resource: ingConfig,
  2944  				},
  2945  			},
  2946  			expected: []ResourceChange{
  2947  				{
  2948  					Op:       Delete,
  2949  					Resource: vsConfig,
  2950  				},
  2951  				{
  2952  					Op:       AddOrUpdate,
  2953  					Resource: ingConfig,
  2954  				},
  2955  			},
  2956  			msg: "preserve the order",
  2957  		},
  2958  		{
  2959  			changes: []ResourceChange{
  2960  				{
  2961  					Op:       Delete,
  2962  					Resource: ingConfig,
  2963  				},
  2964  				{
  2965  					Op:       AddOrUpdate,
  2966  					Resource: vsConfig,
  2967  				},
  2968  			},
  2969  			expected: []ResourceChange{
  2970  				{
  2971  					Op:       Delete,
  2972  					Resource: ingConfig,
  2973  				},
  2974  				{
  2975  					Op:       AddOrUpdate,
  2976  					Resource: vsConfig,
  2977  				},
  2978  			},
  2979  			msg: "do not squash different resource with same ns/name",
  2980  		},
  2981  		{
  2982  			changes: []ResourceChange{
  2983  				{
  2984  					Op:       Delete,
  2985  					Resource: ingConfig,
  2986  				},
  2987  				{
  2988  					Op:       AddOrUpdate,
  2989  					Resource: ingConfig,
  2990  				},
  2991  				{
  2992  					Op:       Delete,
  2993  					Resource: vsConfig,
  2994  				},
  2995  			},
  2996  			expected: []ResourceChange{
  2997  				{
  2998  					Op:       Delete,
  2999  					Resource: vsConfig,
  3000  				},
  3001  				{
  3002  					Op:       AddOrUpdate,
  3003  					Resource: ingConfig,
  3004  				},
  3005  			},
  3006  			msg: "squashed delete and update must follow delete",
  3007  		},
  3008  	}
  3009  
  3010  	for _, test := range tests {
  3011  		result := squashResourceChanges(test.changes)
  3012  		if diff := cmp.Diff(test.expected, result); diff != "" {
  3013  			t.Errorf("squashResourceChanges() returned unexpected result for the case of %s (-want +got):\n%s", test.msg, diff)
  3014  		}
  3015  	}
  3016  }
  3017  
  3018  type testReferenceChecker struct {
  3019  	resourceName            string
  3020  	resourceNamespace       string
  3021  	onlyIngresses           bool
  3022  	onlyMinions             bool
  3023  	onlyVirtualServers      bool
  3024  	onlyVirtualServerRoutes bool
  3025  	onlyTransportServers    bool
  3026  }
  3027  
  3028  func (rc *testReferenceChecker) IsReferencedByIngress(namespace string, name string, ing *networking.Ingress) bool {
  3029  	return rc.onlyIngresses && namespace == rc.resourceNamespace && name == rc.resourceName
  3030  }
  3031  
  3032  func (rc *testReferenceChecker) IsReferencedByMinion(namespace string, name string, ing *networking.Ingress) bool {
  3033  	return rc.onlyMinions && namespace == rc.resourceNamespace && name == rc.resourceName
  3034  }
  3035  
  3036  func (rc *testReferenceChecker) IsReferencedByVirtualServer(namespace string, name string, vs *conf_v1.VirtualServer) bool {
  3037  	return rc.onlyVirtualServers && namespace == rc.resourceNamespace && name == rc.resourceName
  3038  }
  3039  
  3040  func (rc *testReferenceChecker) IsReferencedByVirtualServerRoute(namespace string, name string, vsr *conf_v1.VirtualServerRoute) bool {
  3041  	return rc.onlyVirtualServerRoutes && namespace == rc.resourceNamespace && name == rc.resourceName
  3042  }
  3043  
  3044  func (rc *testReferenceChecker) IsReferencedByTransportServer(namespace string, name string, ts *conf_v1alpha1.TransportServer) bool {
  3045  	return rc.onlyTransportServers && namespace == rc.resourceNamespace && name == rc.resourceName
  3046  }
  3047  
  3048  func TestFindResourcesForResourceReference(t *testing.T) {
  3049  	regularIng := createTestIngress("regular-ingress", "foo.example.com")
  3050  	master := createTestIngressMaster("master-ingress", "bar.example.com")
  3051  	minion := createTestIngressMinion("minion-ingress", "bar.example.com", "/")
  3052  	vs := createTestVirtualServer("virtualserver-1", "qwe.example.com")
  3053  	vsWithVSR := createTestVirtualServerWithRoutes(
  3054  		"virtualserver-2",
  3055  		"asd.example.com",
  3056  		[]conf_v1.Route{
  3057  			{
  3058  				Path:  "/",
  3059  				Route: "virtualserverroute",
  3060  			},
  3061  		})
  3062  	vsr := createTestVirtualServerRoute("virtualserverroute", "asd.example.com", "/")
  3063  	tsPassthrough := createTestTLSPassthroughTransportServer("transportserver-passthrough", "ts.example.com")
  3064  	listeners := []conf_v1alpha1.Listener{
  3065  		{
  3066  			Name:     "tcp-7777",
  3067  			Port:     7777,
  3068  			Protocol: "TCP",
  3069  		},
  3070  	}
  3071  	gc := createTestGlobalConfiguration(listeners)
  3072  	tsTCP := createTestTransportServer("transportserver-tcp", "tcp-7777", "TCP")
  3073  
  3074  	configuration := createTestConfiguration()
  3075  
  3076  	configuration.AddOrUpdateIngress(regularIng)
  3077  	configuration.AddOrUpdateIngress(master)
  3078  	configuration.AddOrUpdateIngress(minion)
  3079  	configuration.AddOrUpdateVirtualServer(vs)
  3080  	configuration.AddOrUpdateVirtualServer(vsWithVSR)
  3081  	configuration.AddOrUpdateVirtualServerRoute(vsr)
  3082  	_, _, _ = configuration.AddOrUpdateGlobalConfiguration(gc)
  3083  	configuration.AddOrUpdateTransportServer(tsPassthrough)
  3084  	configuration.AddOrUpdateTransportServer(tsTCP)
  3085  
  3086  	tests := []struct {
  3087  		rc       resourceReferenceChecker
  3088  		expected []Resource
  3089  		msg      string
  3090  	}{
  3091  		{
  3092  			rc: &testReferenceChecker{
  3093  				resourceNamespace: "default",
  3094  				resourceName:      "test",
  3095  				onlyIngresses:     true,
  3096  			},
  3097  			expected: []Resource{
  3098  				configuration.hosts["bar.example.com"],
  3099  				configuration.hosts["foo.example.com"],
  3100  			},
  3101  			msg: "only Ingresses",
  3102  		},
  3103  		{
  3104  			rc: &testReferenceChecker{
  3105  				resourceNamespace: "default",
  3106  				resourceName:      "test",
  3107  				onlyMinions:       true,
  3108  			},
  3109  			expected: []Resource{
  3110  				configuration.hosts["bar.example.com"],
  3111  			},
  3112  			msg: "only Minions",
  3113  		},
  3114  		{
  3115  			rc: &testReferenceChecker{
  3116  				resourceNamespace:  "default",
  3117  				resourceName:       "test",
  3118  				onlyVirtualServers: true,
  3119  			},
  3120  			expected: []Resource{
  3121  				configuration.hosts["asd.example.com"],
  3122  				configuration.hosts["qwe.example.com"],
  3123  			},
  3124  			msg: "only VirtualServers",
  3125  		},
  3126  		{
  3127  			rc: &testReferenceChecker{
  3128  				resourceNamespace:       "default",
  3129  				resourceName:            "test",
  3130  				onlyVirtualServerRoutes: true,
  3131  			},
  3132  			expected: []Resource{
  3133  				configuration.hosts["asd.example.com"],
  3134  			},
  3135  			msg: "only VirtualServerRoutes",
  3136  		},
  3137  		{
  3138  			rc: &testReferenceChecker{
  3139  				resourceNamespace:    "default",
  3140  				resourceName:         "test",
  3141  				onlyTransportServers: true,
  3142  			},
  3143  			expected: []Resource{
  3144  				configuration.hosts["ts.example.com"],
  3145  				configuration.listeners["tcp-7777"],
  3146  			},
  3147  			msg: "only TransportServers",
  3148  		},
  3149  	}
  3150  
  3151  	for _, test := range tests {
  3152  		result := configuration.findResourcesForResourceReference("default", "test", test.rc)
  3153  		if diff := cmp.Diff(test.expected, result); diff != "" {
  3154  			t.Errorf("findResourcesForResourceReference() returned unexpected result for the case of %s (-want +got):\n%s", test.msg, diff)
  3155  		}
  3156  
  3157  		var noResources []Resource
  3158  
  3159  		result = configuration.findResourcesForResourceReference("default", "wrong", test.rc)
  3160  		if diff := cmp.Diff(noResources, result); diff != "" {
  3161  			t.Errorf("findResourcesForResourceReference() returned unexpected result for the case of %s and wrong name (-want +got):\n%s", test.msg, diff)
  3162  		}
  3163  
  3164  		result = configuration.findResourcesForResourceReference("wrong", "test", test.rc)
  3165  		if diff := cmp.Diff(noResources, result); diff != "" {
  3166  			t.Errorf("findResourcesForResourceReference() returned unexpected result for the case of %s and wrong namespace (-want +got):\n%s", test.msg, diff)
  3167  		}
  3168  	}
  3169  }
  3170  
  3171  func TestGetResources(t *testing.T) {
  3172  	ing := createTestIngress("ingress", "foo.example.com", "bar.example.com")
  3173  	vs := createTestVirtualServer("virtualserver", "qwe.example.com")
  3174  
  3175  	configuration := createTestConfiguration()
  3176  	configuration.AddOrUpdateIngress(ing)
  3177  	configuration.AddOrUpdateVirtualServer(vs)
  3178  
  3179  	expected := []Resource{
  3180  		configuration.hosts["foo.example.com"],
  3181  		configuration.hosts["qwe.example.com"],
  3182  	}
  3183  
  3184  	result := configuration.GetResources()
  3185  	if diff := cmp.Diff(expected, result); diff != "" {
  3186  		t.Errorf("GetResources() returned unexpected result (-want +got):\n%s", diff)
  3187  	}
  3188  
  3189  	expected = []Resource{
  3190  		configuration.hosts["foo.example.com"],
  3191  	}
  3192  
  3193  	result = configuration.GetResourcesWithFilter(resourceFilter{Ingresses: true})
  3194  	if diff := cmp.Diff(expected, result); diff != "" {
  3195  		t.Errorf("GetResources() returned unexpected result (-want +got):\n%s", diff)
  3196  	}
  3197  
  3198  	expected = []Resource{
  3199  		configuration.hosts["qwe.example.com"],
  3200  	}
  3201  
  3202  	result = configuration.GetResourcesWithFilter(resourceFilter{VirtualServers: true})
  3203  	if diff := cmp.Diff(expected, result); diff != "" {
  3204  		t.Errorf("GetResources() returned unexpected result (-want +got):\n%s", diff)
  3205  	}
  3206  }
  3207  
  3208  func TestIsEqualForIngressConfigurationes(t *testing.T) {
  3209  	regularIng := createTestIngress("regular-ingress", "foo.example.com")
  3210  
  3211  	ingConfigWithInvalidHost := NewRegularIngressConfiguration(regularIng)
  3212  	ingConfigWithInvalidHost.ValidHosts["foo.example.com"] = false
  3213  
  3214  	ingConfigWithUpdatedIsMaster := NewRegularIngressConfiguration(regularIng)
  3215  	ingConfigWithUpdatedIsMaster.IsMaster = true
  3216  
  3217  	regularIngWithUpdatedGen := regularIng.DeepCopy()
  3218  	regularIngWithUpdatedGen.Generation++
  3219  
  3220  	regularIngWithUpdatedAnnotations := regularIng.DeepCopy()
  3221  	regularIngWithUpdatedAnnotations.Annotations["new"] = "value"
  3222  
  3223  	masterIng := createTestIngressMaster("master-ingress", "bar.example.com")
  3224  	minionIng := createTestIngressMinion("minion-ingress", "bar.example.com", "/")
  3225  
  3226  	minionIngWithUpdatedGen := minionIng.DeepCopy()
  3227  	minionIngWithUpdatedGen.Generation++
  3228  
  3229  	minionIngWithUpdatedAnnotations := minionIng.DeepCopy()
  3230  	minionIngWithUpdatedAnnotations.Annotations["new"] = "value"
  3231  
  3232  	tests := []struct {
  3233  		ingConfig1 *IngressConfiguration
  3234  		ingConfig2 *IngressConfiguration
  3235  		expected   bool
  3236  		msg        string
  3237  	}{
  3238  		{
  3239  			ingConfig1: NewRegularIngressConfiguration(regularIng),
  3240  			ingConfig2: NewRegularIngressConfiguration(regularIng),
  3241  			expected:   true,
  3242  			msg:        "equal regular ingresses",
  3243  		},
  3244  		{
  3245  			ingConfig1: NewRegularIngressConfiguration(regularIng),
  3246  			ingConfig2: ingConfigWithInvalidHost,
  3247  			expected:   false,
  3248  			msg:        "regular ingresses with different valid hosts",
  3249  		},
  3250  		{
  3251  			ingConfig1: NewRegularIngressConfiguration(regularIng),
  3252  			ingConfig2: ingConfigWithUpdatedIsMaster,
  3253  			expected:   false,
  3254  			msg:        "regular ingresses with different IsMaster value",
  3255  		},
  3256  		{
  3257  			ingConfig1: NewRegularIngressConfiguration(regularIng),
  3258  			ingConfig2: NewRegularIngressConfiguration(regularIngWithUpdatedGen),
  3259  			expected:   false,
  3260  			msg:        "regular ingresses with different generation",
  3261  		},
  3262  		{
  3263  			ingConfig1: NewRegularIngressConfiguration(regularIng),
  3264  			ingConfig2: NewRegularIngressConfiguration(regularIngWithUpdatedAnnotations),
  3265  			expected:   false,
  3266  			msg:        "regular ingresses with different annotations",
  3267  		},
  3268  		{
  3269  			ingConfig1: NewMasterIngressConfiguration(masterIng, []*MinionConfiguration{NewMinionConfiguration(minionIng)}, map[string][]string{}),
  3270  			ingConfig2: NewMasterIngressConfiguration(masterIng, []*MinionConfiguration{NewMinionConfiguration(minionIng)}, map[string][]string{}),
  3271  			expected:   true,
  3272  			msg:        "equal master ingresses",
  3273  		},
  3274  		{
  3275  			ingConfig1: NewMasterIngressConfiguration(masterIng, []*MinionConfiguration{NewMinionConfiguration(minionIng)}, map[string][]string{}),
  3276  			ingConfig2: NewMasterIngressConfiguration(masterIng, []*MinionConfiguration{}, map[string][]string{}),
  3277  			expected:   false,
  3278  			msg:        "masters with different number of minions",
  3279  		},
  3280  		{
  3281  			ingConfig1: NewMasterIngressConfiguration(masterIng, []*MinionConfiguration{NewMinionConfiguration(minionIng)}, map[string][]string{}),
  3282  			ingConfig2: NewMasterIngressConfiguration(masterIng, []*MinionConfiguration{NewMinionConfiguration(minionIngWithUpdatedGen)}, map[string][]string{}),
  3283  			expected:   false,
  3284  			msg:        "masters with minions with different generation",
  3285  		},
  3286  		{
  3287  			ingConfig1: NewMasterIngressConfiguration(masterIng, []*MinionConfiguration{NewMinionConfiguration(minionIng)}, map[string][]string{}),
  3288  			ingConfig2: NewMasterIngressConfiguration(masterIng, []*MinionConfiguration{NewMinionConfiguration(minionIngWithUpdatedAnnotations)}, map[string][]string{}),
  3289  			expected:   false,
  3290  			msg:        "masters with minions with different annotations",
  3291  		},
  3292  	}
  3293  
  3294  	for _, test := range tests {
  3295  		result := test.ingConfig1.IsEqual(test.ingConfig2)
  3296  		if result != test.expected {
  3297  			t.Errorf("IsEqual() returned %v but expected %v for the case of %s", result, test.expected, test.msg)
  3298  		}
  3299  	}
  3300  }
  3301  
  3302  func TestIsEqualForVirtualServers(t *testing.T) {
  3303  	vs := createTestVirtualServerWithRoutes(
  3304  		"virtualserver",
  3305  		"foo.example.com",
  3306  		[]conf_v1.Route{
  3307  			{
  3308  				Path:  "/",
  3309  				Route: "virtualserverroute",
  3310  			},
  3311  		})
  3312  	vsr := createTestVirtualServerRoute("virtualserverroute", "foo.example.com", "/")
  3313  
  3314  	vsWithUpdatedGen := vs.DeepCopy()
  3315  	vsWithUpdatedGen.Generation++
  3316  
  3317  	vsrWithUpdatedGen := vsr.DeepCopy()
  3318  	vsrWithUpdatedGen.Generation++
  3319  
  3320  	tests := []struct {
  3321  		vsConfig1 *VirtualServerConfiguration
  3322  		vsConfig2 *VirtualServerConfiguration
  3323  		expected  bool
  3324  		msg       string
  3325  	}{
  3326  		{
  3327  			vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, []string{}),
  3328  			vsConfig2: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, []string{}),
  3329  			expected:  true,
  3330  			msg:       "equal virtual servers",
  3331  		},
  3332  		{
  3333  			vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, []string{}),
  3334  			vsConfig2: NewVirtualServerConfiguration(vsWithUpdatedGen, []*conf_v1.VirtualServerRoute{vsr}, []string{}),
  3335  			expected:  false,
  3336  			msg:       "virtual servers with different generation",
  3337  		},
  3338  		{
  3339  			vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, []string{}),
  3340  			vsConfig2: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{}, []string{}),
  3341  			expected:  false,
  3342  			msg:       "virtual servers with different number of virtual server routes",
  3343  		},
  3344  		{
  3345  			vsConfig1: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsr}, []string{}),
  3346  			vsConfig2: NewVirtualServerConfiguration(vs, []*conf_v1.VirtualServerRoute{vsrWithUpdatedGen}, []string{}),
  3347  			expected:  false,
  3348  			msg:       "virtual servers with virtual server routes with different generation",
  3349  		},
  3350  	}
  3351  
  3352  	for _, test := range tests {
  3353  		result := test.vsConfig1.IsEqual(test.vsConfig2)
  3354  		if result != test.expected {
  3355  			t.Errorf("IsEqual() returned %v but expected %v for the case of %s", result, test.expected, test.msg)
  3356  		}
  3357  	}
  3358  }
  3359  
  3360  func TestIsEqualForDifferentResources(t *testing.T) {
  3361  	ingConfig := NewRegularIngressConfiguration(createTestIngress("ingress", "foo.example.com"))
  3362  	vsConfig := NewVirtualServerConfiguration(createTestVirtualServer("virtualserver", "bar.example.com"), []*conf_v1.VirtualServerRoute{}, []string{})
  3363  
  3364  	result := ingConfig.IsEqual(vsConfig)
  3365  	if result != false {
  3366  		t.Error("IsEqual() for different resources returned true but expected false")
  3367  	}
  3368  }
  3369  
  3370  func TestCompareConfigurationProblems(t *testing.T) {
  3371  	tests := []struct {
  3372  		problem1 *ConfigurationProblem
  3373  		problem2 *ConfigurationProblem
  3374  		expected bool
  3375  		msg      string
  3376  	}{
  3377  		{
  3378  			problem1: &ConfigurationProblem{
  3379  				IsError: false,
  3380  				Reason:  "reason",
  3381  				Message: "message",
  3382  			},
  3383  			problem2: &ConfigurationProblem{
  3384  				IsError: false,
  3385  				Reason:  "reason",
  3386  				Message: "message",
  3387  			},
  3388  			expected: true,
  3389  			msg:      "equal problems",
  3390  		},
  3391  		{
  3392  			problem1: &ConfigurationProblem{
  3393  				Object:  createTestIngress("ingress-1", "foo.example.com"),
  3394  				IsError: false,
  3395  				Reason:  "reason",
  3396  				Message: "message",
  3397  			},
  3398  			problem2: &ConfigurationProblem{
  3399  				Object:  createTestIngress("ingress-2", "bar.example.com"),
  3400  				IsError: false,
  3401  				Reason:  "reason",
  3402  				Message: "message",
  3403  			},
  3404  			expected: true,
  3405  			msg:      "equal problems although objects are different",
  3406  		},
  3407  		{
  3408  			problem1: &ConfigurationProblem{
  3409  				IsError: true,
  3410  				Reason:  "reason",
  3411  				Message: "message",
  3412  			},
  3413  			problem2: &ConfigurationProblem{
  3414  				IsError: false,
  3415  				Reason:  "reason",
  3416  				Message: "message",
  3417  			},
  3418  			expected: false,
  3419  			msg:      "different isError",
  3420  		},
  3421  		{
  3422  			problem1: &ConfigurationProblem{
  3423  				IsError: false,
  3424  				Reason:  "reason",
  3425  				Message: "message",
  3426  			},
  3427  			problem2: &ConfigurationProblem{
  3428  				IsError: false,
  3429  				Reason:  "another reason",
  3430  				Message: "message",
  3431  			},
  3432  			expected: false,
  3433  			msg:      "different Reason",
  3434  		},
  3435  		{
  3436  			problem1: &ConfigurationProblem{
  3437  				IsError: false,
  3438  				Reason:  "reason",
  3439  				Message: "message",
  3440  			},
  3441  			problem2: &ConfigurationProblem{
  3442  				IsError: false,
  3443  				Reason:  "reason",
  3444  				Message: "another message",
  3445  			},
  3446  			expected: false,
  3447  			msg:      "different Message",
  3448  		},
  3449  	}
  3450  
  3451  	for _, test := range tests {
  3452  		result := compareConfigurationProblems(test.problem1, test.problem2)
  3453  		if result != test.expected {
  3454  			t.Errorf("compareConfigurationProblems() returned %v but expected %v for the case of %s", result, test.expected, test.msg)
  3455  		}
  3456  	}
  3457  }