github.com/jlmeeker/kismatic@v1.10.1-0.20180612190640-57f9005a1f1a/pkg/install/validate_test.go (about)

     1  package install
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  )
     7  
     8  func validPlan() Plan {
     9  	return Plan{
    10  		Cluster: Cluster{
    11  			Name:    "test",
    12  			Version: "v1.10.3",
    13  			Networking: NetworkConfig{
    14  				Type:             "overlay",
    15  				PodCIDRBlock:     "172.16.0.0/16",
    16  				ServiceCIDRBlock: "172.20.0.0/16",
    17  			},
    18  			Certificates: CertsConfig{
    19  				Expiry: "17250h",
    20  			},
    21  			SSH: SSHConfig{
    22  				User: "root",
    23  				Key:  "/bin/sh",
    24  				Port: 22,
    25  			},
    26  		},
    27  		AdditionalFiles: []AdditionalFile{
    28  			{
    29  				Source:      "/bin/sh",
    30  				Destination: "/bin/sh",
    31  				Hosts:       []string{"master01"},
    32  			},
    33  		},
    34  		AddOns: AddOns{
    35  			CNI: &CNI{
    36  				Provider: "calico",
    37  				Options: CNIOptions{
    38  					Calico: CalicoOptions{
    39  						Mode:     "overlay",
    40  						LogLevel: "info",
    41  					},
    42  				},
    43  			},
    44  			DNS: DNS{
    45  				Provider: "kubedns",
    46  			},
    47  			Dashboard: Dashboard{
    48  				Options: DashboardOptions{
    49  					ServiceType: "ClusterIP",
    50  				},
    51  			},
    52  			HeapsterMonitoring: &HeapsterMonitoring{
    53  				Options: HeapsterOptions{
    54  					Heapster: Heapster{
    55  						Replicas:    2,
    56  						ServiceType: "ClusterIP",
    57  					},
    58  				},
    59  			},
    60  		},
    61  		Etcd: NodeGroup{
    62  			ExpectedCount: 1,
    63  			Nodes: []Node{
    64  				{
    65  					Host: "etcd01",
    66  					IP:   "192.168.205.10",
    67  				},
    68  			},
    69  		},
    70  		Master: MasterNodeGroup{
    71  			ExpectedCount: 1,
    72  			Nodes: []Node{
    73  				{
    74  					Host: "master01",
    75  					IP:   "192.168.205.11",
    76  				},
    77  			},
    78  			LoadBalancedFQDN:      "test",
    79  			LoadBalancedShortName: "test",
    80  		},
    81  		Worker: NodeGroup{
    82  			ExpectedCount: 1,
    83  			Nodes: []Node{
    84  				{
    85  					Host: "worker01",
    86  					IP:   "192.168.205.12",
    87  				},
    88  			},
    89  		},
    90  		Ingress: OptionalNodeGroup{
    91  			ExpectedCount: 1,
    92  			Nodes: []Node{
    93  				{
    94  					Host: "etcd01",
    95  					IP:   "192.168.205.10",
    96  				},
    97  			},
    98  		},
    99  		NFS: &NFS{
   100  			Volumes: []NFSVolume{
   101  				{
   102  					Host: "10.10.2.20",
   103  					Path: "/",
   104  				},
   105  			},
   106  		},
   107  	}
   108  }
   109  
   110  func assertInvalidPlan(t *testing.T, p Plan) {
   111  	valid, _ := ValidatePlan(&p)
   112  	if valid {
   113  		t.Errorf("expected invalid, but got valid")
   114  	}
   115  }
   116  
   117  func TestValidateBlankPlan(t *testing.T) {
   118  	p := Plan{}
   119  	assertInvalidPlan(t, p)
   120  }
   121  
   122  func TestValidateValidPlan(t *testing.T) {
   123  	p := validPlan()
   124  	valid, errs := ValidatePlan(&p)
   125  	if !valid {
   126  		t.Errorf("expected valid, but got invalid")
   127  	}
   128  	fmt.Println(errs)
   129  }
   130  
   131  func TestClusterVersion(t *testing.T) {
   132  	tests := []struct {
   133  		c     Cluster
   134  		valid bool
   135  	}{
   136  		{c: Cluster{
   137  			Name:    "test",
   138  			Version: "v1.10.3",
   139  			Networking: NetworkConfig{
   140  				Type:             "overlay",
   141  				PodCIDRBlock:     "172.16.0.0/16",
   142  				ServiceCIDRBlock: "172.20.0.0/16",
   143  			},
   144  			Certificates: CertsConfig{
   145  				Expiry: "17250h",
   146  			},
   147  			SSH: SSHConfig{
   148  				User: "root",
   149  				Key:  "/bin/sh",
   150  				Port: 22,
   151  			},
   152  		},
   153  			valid: true,
   154  		},
   155  		{c: Cluster{
   156  			Name:    "test",
   157  			Version: "v1.10.3",
   158  			Networking: NetworkConfig{
   159  				Type:             "overlay",
   160  				PodCIDRBlock:     "172.16.0.0/16",
   161  				ServiceCIDRBlock: "172.20.0.0/16",
   162  			},
   163  			Certificates: CertsConfig{
   164  				Expiry: "17250h",
   165  			},
   166  			SSH: SSHConfig{
   167  				User: "root",
   168  				Key:  "/bin/sh",
   169  				Port: 22,
   170  			},
   171  		},
   172  			valid: true,
   173  		},
   174  		{c: Cluster{
   175  			Name:    "test",
   176  			Version: "foo",
   177  			Networking: NetworkConfig{
   178  				Type:             "overlay",
   179  				PodCIDRBlock:     "172.16.0.0/16",
   180  				ServiceCIDRBlock: "172.20.0.0/16",
   181  			},
   182  			Certificates: CertsConfig{
   183  				Expiry: "17250h",
   184  			},
   185  			SSH: SSHConfig{
   186  				User: "root",
   187  				Key:  "/bin/sh",
   188  				Port: 22,
   189  			},
   190  		},
   191  			valid: false,
   192  		},
   193  		{c: Cluster{
   194  			Name:    "test",
   195  			Version: "v1.10.300",
   196  			Networking: NetworkConfig{
   197  				Type:             "overlay",
   198  				PodCIDRBlock:     "172.16.0.0/16",
   199  				ServiceCIDRBlock: "172.20.0.0/16",
   200  			},
   201  			Certificates: CertsConfig{
   202  				Expiry: "17250h",
   203  			},
   204  			SSH: SSHConfig{
   205  				User: "root",
   206  				Key:  "/bin/sh",
   207  				Port: 22,
   208  			},
   209  		},
   210  			valid: false,
   211  		},
   212  		{c: Cluster{
   213  			Name:    "test",
   214  			Version: "v1.8.0",
   215  			Networking: NetworkConfig{
   216  				Type:             "overlay",
   217  				PodCIDRBlock:     "172.16.0.0/16",
   218  				ServiceCIDRBlock: "172.20.0.0/16",
   219  			},
   220  			Certificates: CertsConfig{
   221  				Expiry: "17250h",
   222  			},
   223  			SSH: SSHConfig{
   224  				User: "root",
   225  				Key:  "/bin/sh",
   226  				Port: 22,
   227  			},
   228  		},
   229  			valid: false,
   230  		},
   231  		{c: Cluster{
   232  			Name:    "test",
   233  			Version: "v1.20.0",
   234  			Networking: NetworkConfig{
   235  				Type:             "overlay",
   236  				PodCIDRBlock:     "172.16.0.0/16",
   237  				ServiceCIDRBlock: "172.20.0.0/16",
   238  			},
   239  			Certificates: CertsConfig{
   240  				Expiry: "17250h",
   241  			},
   242  			SSH: SSHConfig{
   243  				User: "root",
   244  				Key:  "/bin/sh",
   245  				Port: 22,
   246  			},
   247  		},
   248  			valid: false,
   249  		},
   250  		{c: Cluster{
   251  			Name:                     "test",
   252  			Version:                  "v1.8.0",
   253  			DisconnectedInstallation: true,
   254  			Networking: NetworkConfig{
   255  				Type:             "overlay",
   256  				PodCIDRBlock:     "172.16.0.0/16",
   257  				ServiceCIDRBlock: "172.20.0.0/16",
   258  			},
   259  			Certificates: CertsConfig{
   260  				Expiry: "17250h",
   261  			},
   262  			SSH: SSHConfig{
   263  				User: "root",
   264  				Key:  "/bin/sh",
   265  				Port: 22,
   266  			},
   267  		},
   268  			valid: false,
   269  		},
   270  		{c: Cluster{
   271  			Name:                     "test",
   272  			Version:                  "v1.20.0",
   273  			DisconnectedInstallation: true,
   274  			Networking: NetworkConfig{
   275  				Type:             "overlay",
   276  				PodCIDRBlock:     "172.16.0.0/16",
   277  				ServiceCIDRBlock: "172.20.0.0/16",
   278  			},
   279  			Certificates: CertsConfig{
   280  				Expiry: "17250h",
   281  			},
   282  			SSH: SSHConfig{
   283  				User: "root",
   284  				Key:  "/bin/sh",
   285  				Port: 22,
   286  			},
   287  		},
   288  			valid: false,
   289  		},
   290  	}
   291  	for n, test := range tests {
   292  		if valid, _ := test.c.validate(); valid != test.valid {
   293  			t.Errorf("%d: expected %v with %+v, but got %v - %q", n, test.valid, test.c, !test.valid)
   294  		}
   295  	}
   296  }
   297  
   298  func TestValidatePlanEmptyPodCIDR(t *testing.T) {
   299  	p := validPlan()
   300  	p.Cluster.Networking.PodCIDRBlock = ""
   301  	assertInvalidPlan(t, p)
   302  }
   303  
   304  func TestValidatePlanInvalidPodCIDR(t *testing.T) {
   305  	p := validPlan()
   306  	p.Cluster.Networking.PodCIDRBlock = "foo"
   307  	assertInvalidPlan(t, p)
   308  }
   309  
   310  func TestValidatePlanEmptyServicesCIDR(t *testing.T) {
   311  	p := validPlan()
   312  	p.Cluster.Networking.ServiceCIDRBlock = ""
   313  	assertInvalidPlan(t, p)
   314  }
   315  
   316  func TestValidatePlanInvalidServicesCIDR(t *testing.T) {
   317  	p := validPlan()
   318  	p.Cluster.Networking.ServiceCIDRBlock = "foo"
   319  	assertInvalidPlan(t, p)
   320  }
   321  
   322  func TestValidatePlanEmptyCertificatesExpiry(t *testing.T) {
   323  	p := validPlan()
   324  	p.Cluster.Certificates.Expiry = ""
   325  	assertInvalidPlan(t, p)
   326  }
   327  
   328  func TestValidatePlanInvalidCertExpiry(t *testing.T) {
   329  	p := validPlan()
   330  	p.Cluster.Certificates.Expiry = "foo"
   331  	assertInvalidPlan(t, p)
   332  }
   333  
   334  func TestValidatePlanEmptyCACertExpiryIsValid(t *testing.T) {
   335  	p := validPlan()
   336  	p.Cluster.Certificates.CAExpiry = ""
   337  	valid, _ := p.validate()
   338  	if !valid {
   339  		t.Errorf("plan was found invalid")
   340  	}
   341  }
   342  
   343  func TestValidatePlanInvalidCACertificatesExpiry(t *testing.T) {
   344  	p := validPlan()
   345  	p.Cluster.Certificates.CAExpiry = "foo"
   346  	assertInvalidPlan(t, p)
   347  }
   348  
   349  func TestValidatePlanEmptySSHUser(t *testing.T) {
   350  	p := validPlan()
   351  	p.Cluster.SSH.User = ""
   352  	assertInvalidPlan(t, p)
   353  }
   354  
   355  func TestValidatePlanEmptySSHKey(t *testing.T) {
   356  	p := validPlan()
   357  	p.Cluster.SSH.Key = ""
   358  	assertInvalidPlan(t, p)
   359  }
   360  
   361  func TestValidatePlanNonExistentSSHKey(t *testing.T) {
   362  	p := validPlan()
   363  	p.Cluster.SSH.Key = "/foo"
   364  	assertInvalidPlan(t, p)
   365  }
   366  
   367  func TestValidatePlanNegativeSSHPort(t *testing.T) {
   368  	p := validPlan()
   369  	p.Cluster.SSH.Port = -1
   370  	assertInvalidPlan(t, p)
   371  }
   372  
   373  func TestValidatePlanEmptyLoadBalancedFQDN(t *testing.T) {
   374  	p := validPlan()
   375  	p.Master.LoadBalancedFQDN = ""
   376  	assertInvalidPlan(t, p)
   377  }
   378  
   379  func TestValidatePlanEmptyLoadBalancedShortName(t *testing.T) {
   380  	p := validPlan()
   381  	p.Master.LoadBalancedShortName = ""
   382  	assertInvalidPlan(t, p)
   383  }
   384  
   385  func TestValidatePlanNoEtcdNodes(t *testing.T) {
   386  	p := validPlan()
   387  	p.Etcd.ExpectedCount = 0
   388  	p.Etcd.Nodes = []Node{}
   389  	assertInvalidPlan(t, p)
   390  }
   391  
   392  func TestValidatePlanNoMasterNodes(t *testing.T) {
   393  	p := validPlan()
   394  	p.Master.ExpectedCount = 0
   395  	p.Master.Nodes = []Node{}
   396  	assertInvalidPlan(t, p)
   397  }
   398  
   399  func TestValidatePlanNoWorkerNodes(t *testing.T) {
   400  	p := validPlan()
   401  	p.Worker.ExpectedCount = 0
   402  	p.Worker.Nodes = []Node{}
   403  	assertInvalidPlan(t, p)
   404  }
   405  
   406  func TestValidatePlanEtcdNodesMismatch(t *testing.T) {
   407  	p := validPlan()
   408  	p.Etcd.ExpectedCount = 100
   409  	assertInvalidPlan(t, p)
   410  }
   411  
   412  func TestValidatePlanMasterNodesMismatch(t *testing.T) {
   413  	p := validPlan()
   414  	p.Master.ExpectedCount = 100
   415  	assertInvalidPlan(t, p)
   416  }
   417  
   418  func TestValidatePlanWorkerNodesMismatch(t *testing.T) {
   419  	p := validPlan()
   420  	p.Worker.ExpectedCount = 100
   421  	assertInvalidPlan(t, p)
   422  }
   423  
   424  func TestValidatePlanUnexpectedEtcdNodes(t *testing.T) {
   425  	p := validPlan()
   426  	p.Etcd.ExpectedCount = 1
   427  	p.Etcd.Nodes = []Node{
   428  		{
   429  			Host: "etcd01",
   430  			IP:   "192.168.205.10",
   431  		},
   432  		{
   433  			Host: "etcd02",
   434  			IP:   "192.168.205.11",
   435  		},
   436  	}
   437  	assertInvalidPlan(t, p)
   438  }
   439  
   440  func TestValidatePlanUnexpectedMasterNodes(t *testing.T) {
   441  	p := validPlan()
   442  	p.Master.ExpectedCount = 1
   443  	p.Master.Nodes = []Node{
   444  		{
   445  			Host: "master01",
   446  			IP:   "192.168.205.10",
   447  		},
   448  		{
   449  			Host: "master02",
   450  			IP:   "192.168.205.11",
   451  		},
   452  	}
   453  	assertInvalidPlan(t, p)
   454  }
   455  
   456  func TestValidatePlanUnexpectedWorkerNodes(t *testing.T) {
   457  	p := validPlan()
   458  	p.Worker.ExpectedCount = 1
   459  	p.Worker.Nodes = []Node{
   460  		{
   461  			Host: "worker01",
   462  			IP:   "192.168.205.10",
   463  		},
   464  		{
   465  			Host: "worker02",
   466  			IP:   "192.168.205.11",
   467  		},
   468  	}
   469  	assertInvalidPlan(t, p)
   470  }
   471  
   472  func TestValidatePlanNoIngress(t *testing.T) {
   473  	p := validPlan()
   474  	p.Ingress.ExpectedCount = 0
   475  	p.Ingress.Nodes = []Node{}
   476  	valid, _ := ValidatePlan(&p)
   477  	if !valid {
   478  		t.Errorf("expected valid, but got invalid")
   479  	}
   480  }
   481  
   482  func TestValidatePlanIngressExpected(t *testing.T) {
   483  	p := validPlan()
   484  	p.Ingress.ExpectedCount = 1
   485  	p.Ingress.Nodes = []Node{}
   486  	assertInvalidPlan(t, p)
   487  }
   488  
   489  func TestValidatePlanIngressProvidedNotExpected(t *testing.T) {
   490  	p := validPlan()
   491  	p.Ingress.ExpectedCount = 0
   492  	p.Ingress.Nodes = []Node{
   493  		{
   494  			Host: "ingress",
   495  			IP:   "192.168.205.10",
   496  		},
   497  	}
   498  	assertInvalidPlan(t, p)
   499  }
   500  
   501  func TestValidateStorageVolume(t *testing.T) {
   502  	tests := []struct {
   503  		sv    StorageVolume
   504  		valid bool
   505  	}{
   506  		{
   507  			sv: StorageVolume{
   508  				Name:              "foo",
   509  				SizeGB:            100,
   510  				DistributionCount: 2,
   511  				ReplicateCount:    2,
   512  				ReclaimPolicy:     "Retain",
   513  				AccessModes:       []string{"ReadWriteMany"},
   514  			},
   515  			valid: true,
   516  		},
   517  		{
   518  			sv: StorageVolume{
   519  				Name:              "foo",
   520  				SizeGB:            100,
   521  				DistributionCount: 1,
   522  				ReplicateCount:    1,
   523  				ReclaimPolicy:     "Retain",
   524  				AccessModes:       []string{"ReadWriteMany"},
   525  			},
   526  			valid: true,
   527  		},
   528  		{
   529  			sv: StorageVolume{
   530  				Name:              "foo",
   531  				SizeGB:            100,
   532  				DistributionCount: 0,
   533  				ReplicateCount:    1,
   534  				ReclaimPolicy:     "Retain",
   535  				AccessModes:       []string{"ReadWriteMany"},
   536  			},
   537  			valid: false,
   538  		},
   539  		{
   540  			sv: StorageVolume{
   541  				Name:              "foo",
   542  				SizeGB:            100,
   543  				DistributionCount: 1,
   544  				ReplicateCount:    0,
   545  				ReclaimPolicy:     "Retain",
   546  				AccessModes:       []string{"ReadWriteMany"},
   547  			},
   548  			valid: false,
   549  		},
   550  		{
   551  			sv: StorageVolume{
   552  				Name:              "bad name with spaces",
   553  				SizeGB:            100,
   554  				DistributionCount: 2,
   555  				ReplicateCount:    2,
   556  				ReclaimPolicy:     "Retain",
   557  				AccessModes:       []string{"ReadWriteMany"},
   558  			},
   559  			valid: false,
   560  		},
   561  		{
   562  			sv: StorageVolume{
   563  				Name:              "bad:name2",
   564  				SizeGB:            100,
   565  				DistributionCount: 2,
   566  				ReplicateCount:    2,
   567  				ReclaimPolicy:     "Retain",
   568  				AccessModes:       []string{"ReadWriteMany"},
   569  			},
   570  			valid: false,
   571  		},
   572  		{
   573  			sv: StorageVolume{
   574  				Name:              "goodName",
   575  				SizeGB:            0,
   576  				DistributionCount: 2,
   577  				ReplicateCount:    2,
   578  				ReclaimPolicy:     "Retain",
   579  				AccessModes:       []string{"ReadWriteMany"},
   580  			},
   581  			valid: false,
   582  		},
   583  		{
   584  			sv: StorageVolume{
   585  				Name:              "goodName",
   586  				SizeGB:            -1,
   587  				DistributionCount: 2,
   588  				ReplicateCount:    2,
   589  				ReclaimPolicy:     "Retain",
   590  				AccessModes:       []string{"ReadWriteMany"},
   591  			},
   592  			valid: false,
   593  		},
   594  		{
   595  			sv: StorageVolume{
   596  				Name:              "goodName",
   597  				SizeGB:            100,
   598  				DistributionCount: -1,
   599  				ReplicateCount:    2,
   600  				ReclaimPolicy:     "Retain",
   601  				AccessModes:       []string{"ReadWriteMany"},
   602  			},
   603  			valid: false,
   604  		},
   605  		{
   606  			sv: StorageVolume{
   607  				Name:              "goodName",
   608  				SizeGB:            100,
   609  				DistributionCount: 2,
   610  				ReplicateCount:    -1,
   611  				ReclaimPolicy:     "Retain",
   612  				AccessModes:       []string{"ReadWriteMany"},
   613  			},
   614  			valid: false,
   615  		},
   616  		{
   617  			sv:    StorageVolume{},
   618  			valid: false,
   619  		},
   620  		{
   621  			sv: StorageVolume{
   622  				Name:              "goodName",
   623  				SizeGB:            100,
   624  				DistributionCount: 1,
   625  				ReplicateCount:    1,
   626  				ReclaimPolicy:     "",
   627  				AccessModes:       []string{"ReadWriteMany"},
   628  			},
   629  			valid: false,
   630  		},
   631  		{
   632  			sv: StorageVolume{
   633  				Name:              "goodName",
   634  				SizeGB:            100,
   635  				DistributionCount: 1,
   636  				ReplicateCount:    1,
   637  				ReclaimPolicy:     "foo",
   638  				AccessModes:       []string{"ReadWriteMany"},
   639  			},
   640  			valid: false,
   641  		},
   642  		{
   643  			sv: StorageVolume{
   644  				Name:              "goodName",
   645  				SizeGB:            100,
   646  				DistributionCount: 1,
   647  				ReplicateCount:    1,
   648  				ReclaimPolicy:     "Retain",
   649  				AccessModes:       []string{"ReadWriteMany"},
   650  			},
   651  			valid: true,
   652  		},
   653  		{
   654  			sv: StorageVolume{
   655  				Name:              "goodName",
   656  				SizeGB:            100,
   657  				DistributionCount: 1,
   658  				ReplicateCount:    1,
   659  				ReclaimPolicy:     "Recycle",
   660  				AccessModes:       []string{"ReadWriteMany"},
   661  			},
   662  			valid: true,
   663  		},
   664  		{
   665  			sv: StorageVolume{
   666  				Name:              "goodName",
   667  				SizeGB:            100,
   668  				DistributionCount: 1,
   669  				ReplicateCount:    1,
   670  				ReclaimPolicy:     "Delete",
   671  				AccessModes:       []string{"ReadWriteMany"},
   672  			},
   673  			valid: true,
   674  		},
   675  		{
   676  			sv: StorageVolume{
   677  				Name:              "goodName",
   678  				SizeGB:            100,
   679  				DistributionCount: 1,
   680  				ReplicateCount:    1,
   681  				ReclaimPolicy:     "Delete",
   682  				AccessModes:       []string{"ReadWriteOnce"},
   683  			},
   684  			valid: true,
   685  		},
   686  		{
   687  			sv: StorageVolume{
   688  				Name:              "goodName",
   689  				SizeGB:            100,
   690  				DistributionCount: 1,
   691  				ReplicateCount:    1,
   692  				ReclaimPolicy:     "Delete",
   693  				AccessModes:       []string{"ReadOnlyMany"},
   694  			},
   695  			valid: true,
   696  		},
   697  		{
   698  			sv: StorageVolume{
   699  				Name:              "goodName",
   700  				SizeGB:            100,
   701  				DistributionCount: 1,
   702  				ReplicateCount:    1,
   703  				ReclaimPolicy:     "Delete",
   704  				AccessModes:       []string{"ReadWriteMany", "ReadWriteOnce", "ReadOnlyMany"},
   705  			},
   706  			valid: true,
   707  		},
   708  		{
   709  			sv: StorageVolume{
   710  				Name:              "goodName",
   711  				SizeGB:            100,
   712  				DistributionCount: 1,
   713  				ReplicateCount:    1,
   714  				ReclaimPolicy:     "Delete",
   715  				AccessModes:       []string{"someBadAccessMode"},
   716  			},
   717  			valid: false,
   718  		},
   719  	}
   720  	for _, test := range tests {
   721  		if valid, _ := test.sv.validate(); valid != test.valid {
   722  			t.Errorf("expected %v with %+v, but got %v", test.valid, test.sv, !test.valid)
   723  		}
   724  	}
   725  }
   726  
   727  func TestValidateAllowAddress(t *testing.T) {
   728  	tests := []struct {
   729  		address string
   730  		valid   bool
   731  	}{
   732  		{"192.168.205.10", true},
   733  		{"192.168.205.*", true},
   734  		{"192.168.*.*", true},
   735  		{"192.*.*.*", true},
   736  		{"*.168.205.10", true},
   737  		{"*.*.205.10", true},
   738  		{"*.*.*.10", true},
   739  		{"*.*.*.*", true},
   740  		{"-1.-1.-1.-1", false},
   741  		{"-1.*.*.*", false},
   742  		{"*.-1.*.*", false},
   743  		{"*.*.-1.*", false},
   744  		{"*.*.*.-1", false},
   745  		{"256.256.256.256", false},
   746  		{"256.*.*.*", false},
   747  		{"*.256.*.*", false},
   748  		{"*.*.256.*", false},
   749  		{"*.*.*.256", false},
   750  		{"a.a.a.a", false},
   751  		{"*.*.*.a", false},
   752  		{"*.*.a.*", false},
   753  		{"*.a.*.*", false},
   754  		{"a.*.*.*", false},
   755  		{"", false},
   756  		{"foo", false},
   757  		{"192", false},
   758  		{"192.168", false},
   759  		{"192.168.205", false},
   760  		{"...", false},
   761  		{"192...", false},
   762  		{"192.168..", false},
   763  		{"192.168.205.", false},
   764  	}
   765  	for _, test := range tests {
   766  		if validateAllowedAddress(test.address) != test.valid {
   767  			t.Errorf("expected %v with address %q, but got %v", test.valid, test.address, !test.valid)
   768  		}
   769  	}
   770  }
   771  
   772  func TestValidatePlanNFSDupes(t *testing.T) {
   773  	p := validPlan()
   774  
   775  	p.NFS.Volumes = append(p.NFS.Volumes, NFSVolume{
   776  		Host: "10.10.2.20",
   777  		Path: "/",
   778  	})
   779  
   780  	assertInvalidPlan(t, p)
   781  }
   782  
   783  func TestValidateNFSVolume(t *testing.T) {
   784  	tests := []struct {
   785  		host  string
   786  		path  string
   787  		valid bool
   788  	}{
   789  		{
   790  			host:  "10.10.2.10",
   791  			path:  "/foo",
   792  			valid: true,
   793  		},
   794  		{
   795  			host:  "10.10.2.10",
   796  			path:  "",
   797  			valid: false,
   798  		},
   799  		{
   800  			host:  "10.10.2.10",
   801  			path:  "../someRelativePath",
   802  			valid: false,
   803  		},
   804  		{
   805  			host:  "",
   806  			path:  "/foo",
   807  			valid: false,
   808  		},
   809  	}
   810  	for _, test := range tests {
   811  		v := NFSVolume{
   812  			Host: test.host,
   813  			Path: test.path,
   814  		}
   815  		if valid, _ := v.validate(); valid != test.valid {
   816  			t.Errorf("Expected valid = %v, but got %v", test.valid, valid)
   817  		}
   818  	}
   819  }
   820  
   821  func TestValidatePlanCerts(t *testing.T) {
   822  	p := validPlan()
   823  
   824  	pki := getPKI(t)
   825  	defer cleanup(pki.GeneratedCertsDirectory, t)
   826  	ca, err := pki.GenerateClusterCA(&p)
   827  	if err != nil {
   828  		t.Fatalf("error generating CA for test: %v", err)
   829  	}
   830  	proxyClientCA, err := pki.GenerateProxyClientCA(&p)
   831  	if err != nil {
   832  		t.Fatalf("error generating proxy-client CA for test: %v", err)
   833  	}
   834  	if err := pki.GenerateClusterCertificates(&p, ca, proxyClientCA); err != nil {
   835  		t.Fatalf("failed to generate certs: %v", err)
   836  	}
   837  
   838  	valid, errs := ValidateCertificates(&p, &pki)
   839  	if !valid {
   840  		t.Errorf("expected valid, but got invalid")
   841  		fmt.Println(errs)
   842  	}
   843  }
   844  
   845  func TestValidatePlanBadCerts(t *testing.T) {
   846  	p := validPlan()
   847  
   848  	pki := getPKI(t)
   849  	defer cleanup(pki.GeneratedCertsDirectory, t)
   850  
   851  	ca, err := pki.GenerateClusterCA(&p)
   852  	if err != nil {
   853  		t.Fatalf("error generating CA for test: %v", err)
   854  	}
   855  	proxyClientCA, err := pki.GenerateProxyClientCA(&p)
   856  	if err != nil {
   857  		t.Fatalf("error generating proxy-client CA for test: %v", err)
   858  	}
   859  	if err := pki.GenerateClusterCertificates(&p, ca, proxyClientCA); err != nil {
   860  		t.Fatalf("failed to generate certs: %v", err)
   861  	}
   862  	p.Master.Nodes[0] = Node{
   863  		Host:       "master01",
   864  		IP:         "11.12.13.14",
   865  		InternalIP: "22.33.44.55",
   866  	}
   867  
   868  	valid, _ := ValidateCertificates(&p, &pki)
   869  	if valid {
   870  		t.Errorf("expected an error, but got valid")
   871  	}
   872  }
   873  
   874  func TestValidatePlanMissingCerts(t *testing.T) {
   875  	p := validPlan()
   876  
   877  	pki := getPKI(t)
   878  	defer cleanup(pki.GeneratedCertsDirectory, t)
   879  
   880  	valid, errs := ValidateCertificates(&p, &pki)
   881  	if !valid {
   882  		t.Errorf("expected valid, but got invalid")
   883  		fmt.Println(errs)
   884  	}
   885  }
   886  
   887  func TestValidatePlanMissingSomeCerts(t *testing.T) {
   888  	p := validPlan()
   889  
   890  	pki := getPKI(t)
   891  	defer cleanup(pki.GeneratedCertsDirectory, t)
   892  
   893  	ca, err := pki.GenerateClusterCA(&p)
   894  	if err != nil {
   895  		t.Fatalf("error generating CA for test: %v", err)
   896  	}
   897  	proxyClientCA, err := pki.GenerateProxyClientCA(&p)
   898  	if err != nil {
   899  		t.Fatalf("error generating proxy-client CA for test: %v", err)
   900  	}
   901  	if err := pki.GenerateClusterCertificates(&p, ca, proxyClientCA); err != nil {
   902  		t.Fatalf("failed to generate certs: %v", err)
   903  	}
   904  
   905  	newNode := Node{
   906  		Host:       "master2",
   907  		IP:         "11.12.13.14",
   908  		InternalIP: "22.33.44.55",
   909  	}
   910  	p.Master.Nodes = append(p.Master.Nodes, newNode)
   911  
   912  	valid, errs := ValidateCertificates(&p, &pki)
   913  	if !valid {
   914  		t.Errorf("expected valid, but got invalid")
   915  		fmt.Println(errs)
   916  	}
   917  }
   918  
   919  func TestValidateNodeListDuplicate(t *testing.T) {
   920  	tests := []struct {
   921  		nl    nodeList
   922  		valid bool
   923  	}{
   924  		{
   925  			nl: nodeList{
   926  				[]Node{
   927  					{
   928  						Host: "host1",
   929  						IP:   "10.0.0.1",
   930  					},
   931  				},
   932  			},
   933  			valid: true,
   934  		},
   935  		{
   936  			nl: nodeList{
   937  				[]Node{
   938  					{
   939  						Host: "host1",
   940  						IP:   "10.0.0.1",
   941  					},
   942  					{
   943  						Host: "host1",
   944  						IP:   "10.0.0.1",
   945  					},
   946  				},
   947  			},
   948  			valid: true,
   949  		},
   950  		{
   951  			nl: nodeList{
   952  				[]Node{
   953  					{
   954  						Host: "host1",
   955  						IP:   "10.0.0.1",
   956  					},
   957  					{
   958  						Host: "host2",
   959  						IP:   "10.0.0.2",
   960  					},
   961  				},
   962  			},
   963  			valid: true,
   964  		},
   965  		{
   966  			nl: nodeList{
   967  				[]Node{
   968  					{
   969  						Host: "host1",
   970  						IP:   "10.0.0.1",
   971  					},
   972  					{
   973  						Host: "host1",
   974  						IP:   "10.0.0.2",
   975  					},
   976  				},
   977  			},
   978  			valid: false,
   979  		},
   980  		{
   981  			nl: nodeList{
   982  				[]Node{
   983  					{
   984  						Host: "host1",
   985  						IP:   "10.0.0.2",
   986  					},
   987  					{
   988  						Host: "host2",
   989  						IP:   "10.0.0.2",
   990  					},
   991  				},
   992  			},
   993  			valid: false,
   994  		},
   995  		{
   996  			nl: nodeList{
   997  				[]Node{
   998  					{
   999  						Host:       "host1",
  1000  						IP:         "10.0.0.1",
  1001  						InternalIP: "192.168.205.10",
  1002  					},
  1003  					{
  1004  						Host:       "host2",
  1005  						IP:         "10.0.0.2",
  1006  						InternalIP: "192.168.205.10",
  1007  					},
  1008  				},
  1009  			},
  1010  			valid: false,
  1011  		},
  1012  	}
  1013  	for i, test := range tests {
  1014  		ok, _ := test.nl.validate()
  1015  		if ok != test.valid {
  1016  			t.Errorf("test %d: expect %t, but got %t", i, test.valid, ok)
  1017  		}
  1018  	}
  1019  }
  1020  
  1021  func TestValidatePlanDisconnectedInstallationFailsDueToMissingRegistry(t *testing.T) {
  1022  	plan := validPlan()
  1023  	plan.Cluster.DisconnectedInstallation = true
  1024  	ok, errs := plan.validate()
  1025  	if ok {
  1026  		t.Errorf("expected validation to fail due to missing external registry information in plan")
  1027  	}
  1028  	var found bool
  1029  	for _, err := range errs {
  1030  		if err.Error() == "A container image registry is required when disconnected_installation is true" {
  1031  			found = true
  1032  		}
  1033  	}
  1034  	if !found {
  1035  		t.Errorf("validation did not return the expected failure message")
  1036  	}
  1037  }
  1038  
  1039  func TestValidatePlanDisconnectedInstallationSucceeds(t *testing.T) {
  1040  	plan := validPlan()
  1041  	plan.Cluster.DisconnectedInstallation = true
  1042  	plan.DockerRegistry.Server = "localhost:5000"
  1043  	if ok, errs := plan.validate(); !ok {
  1044  		t.Error("expected validation to succeed, but it failed")
  1045  		t.Logf("errors were: %v\n", errs)
  1046  	}
  1047  }
  1048  
  1049  func TestDockerRegistry(t *testing.T) {
  1050  	tests := []struct {
  1051  		d     DockerRegistry
  1052  		valid bool
  1053  	}{
  1054  		{
  1055  			d:     DockerRegistry{},
  1056  			valid: true,
  1057  		},
  1058  		{
  1059  			d: DockerRegistry{
  1060  				Server: "172.0.0.1",
  1061  				CAPath: "/bin/sh",
  1062  			},
  1063  			valid: true,
  1064  		},
  1065  		{
  1066  			d: DockerRegistry{
  1067  				Server:   "172.0.0.1",
  1068  				Username: "user",
  1069  				Password: "password",
  1070  			},
  1071  			valid: true,
  1072  		},
  1073  		{
  1074  			d: DockerRegistry{
  1075  				Address: "172.0.0.1",
  1076  				CAPath:  "/bin/sh",
  1077  			},
  1078  			valid: true,
  1079  		},
  1080  		{
  1081  			d: DockerRegistry{
  1082  				Address:  "172.0.0.1",
  1083  				Username: "user",
  1084  				Password: "password",
  1085  			},
  1086  			valid: true,
  1087  		},
  1088  		{
  1089  			d: DockerRegistry{
  1090  				CAPath: "user",
  1091  			},
  1092  			valid: false,
  1093  		},
  1094  		{
  1095  			d: DockerRegistry{
  1096  				Username: "user",
  1097  			},
  1098  			valid: false,
  1099  		},
  1100  		{
  1101  			d: DockerRegistry{
  1102  				Password: "password",
  1103  			},
  1104  			valid: false,
  1105  		},
  1106  	}
  1107  	for i, test := range tests {
  1108  		ok, _ := test.d.validate()
  1109  		if ok != test.valid {
  1110  			t.Errorf("test %d: expect %t, but got %t", i, test.valid, ok)
  1111  		}
  1112  	}
  1113  }
  1114  
  1115  func TestValidateDockerStorage(t *testing.T) {
  1116  	tests := []struct {
  1117  		storage DockerStorage
  1118  		valid   bool
  1119  	}{
  1120  		{
  1121  			storage: DockerStorage{
  1122  				Driver: "devicemapper",
  1123  			},
  1124  			valid: true,
  1125  		},
  1126  		{
  1127  			storage: DockerStorage{
  1128  				Driver: "overlay2",
  1129  			},
  1130  			valid: true,
  1131  		},
  1132  		{
  1133  			storage: DockerStorage{
  1134  				Driver: "foo",
  1135  			},
  1136  			valid: true,
  1137  		},
  1138  		{
  1139  			storage: DockerStorage{
  1140  				Driver: "devicemapper",
  1141  				DirectLVMBlockDevice: DirectLVMBlockDevice{
  1142  					Path: "",
  1143  				},
  1144  			},
  1145  			valid: true,
  1146  		},
  1147  		{
  1148  			storage: DockerStorage{
  1149  				Driver: "devicemapper",
  1150  				DirectLVMBlockDevice: DirectLVMBlockDevice{
  1151  					Path: "foo",
  1152  				},
  1153  			},
  1154  			valid: false,
  1155  		},
  1156  		{
  1157  			storage: DockerStorage{
  1158  				Driver: "devicemapper",
  1159  				DirectLVMBlockDevice: DirectLVMBlockDevice{
  1160  					Path: "/foo/bar",
  1161  				},
  1162  			},
  1163  			valid: true,
  1164  		},
  1165  		{
  1166  			storage: DockerStorage{
  1167  				Driver: "foo",
  1168  				DirectLVMBlockDevice: DirectLVMBlockDevice{
  1169  					Path: "/foo/bar",
  1170  				},
  1171  			},
  1172  			valid: false,
  1173  		},
  1174  	}
  1175  	for i, test := range tests {
  1176  		ok, _ := test.storage.validate()
  1177  		if ok != test.valid {
  1178  			t.Errorf("test %d: expect valid, but got invalid", i)
  1179  		}
  1180  	}
  1181  }
  1182  
  1183  func TestValidateDockerStorageDirectLVM(t *testing.T) {
  1184  	tests := []struct {
  1185  		config DockerStorageDirectLVMDeprecated
  1186  		valid  bool
  1187  	}{
  1188  		{
  1189  			config: DockerStorageDirectLVMDeprecated{
  1190  				Enabled: false,
  1191  			},
  1192  			valid: true,
  1193  		},
  1194  		{
  1195  			config: DockerStorageDirectLVMDeprecated{
  1196  				Enabled: true,
  1197  			},
  1198  			valid: false,
  1199  		},
  1200  		{
  1201  			config: DockerStorageDirectLVMDeprecated{
  1202  				Enabled:     true,
  1203  				BlockDevice: "foo",
  1204  			},
  1205  			valid: false,
  1206  		},
  1207  		{
  1208  			config: DockerStorageDirectLVMDeprecated{
  1209  				Enabled:     true,
  1210  				BlockDevice: "/dev/sdb",
  1211  			},
  1212  			valid: true,
  1213  		},
  1214  	}
  1215  	for i, test := range tests {
  1216  		ok, _ := test.config.validate()
  1217  		if ok != test.valid {
  1218  			t.Errorf("test %d: expect valid, but got invalid", i)
  1219  		}
  1220  	}
  1221  }
  1222  
  1223  func TestCNIAddOn(t *testing.T) {
  1224  	tests := []struct {
  1225  		n     CNI
  1226  		valid bool
  1227  	}{
  1228  		{
  1229  			n: CNI{
  1230  				Provider: "calico",
  1231  				Options: CNIOptions{
  1232  					Calico: CalicoOptions{
  1233  						Mode: "overlay",
  1234  					},
  1235  				},
  1236  			},
  1237  			valid: true,
  1238  		},
  1239  		{
  1240  			n: CNI{
  1241  				Provider: "calico",
  1242  				Options: CNIOptions{
  1243  					Calico: CalicoOptions{
  1244  						Mode: "routed",
  1245  					},
  1246  				},
  1247  			},
  1248  			valid: true,
  1249  		},
  1250  		{
  1251  			n: CNI{
  1252  				Provider: "weave",
  1253  			},
  1254  			valid: true,
  1255  		},
  1256  		{
  1257  			n: CNI{
  1258  				Provider: "contiv",
  1259  			},
  1260  			valid: true,
  1261  		},
  1262  		{
  1263  			n: CNI{
  1264  				Provider: "foo",
  1265  				Options: CNIOptions{
  1266  					Calico: CalicoOptions{
  1267  						Mode: "overlay",
  1268  					},
  1269  				},
  1270  			},
  1271  			valid: false,
  1272  		},
  1273  		{
  1274  			n: CNI{
  1275  				Provider: "calico",
  1276  				Options: CNIOptions{
  1277  					Calico: CalicoOptions{
  1278  						Mode: "foo",
  1279  					},
  1280  				},
  1281  			},
  1282  			valid: false,
  1283  		},
  1284  		{
  1285  			n: CNI{
  1286  				Provider: "foo",
  1287  				Disable:  true,
  1288  				Options: CNIOptions{
  1289  					Calico: CalicoOptions{
  1290  						Mode: "overlay",
  1291  					},
  1292  				},
  1293  			},
  1294  			valid: true,
  1295  		},
  1296  		{
  1297  			n: CNI{
  1298  				Provider: "calico",
  1299  				Disable:  true,
  1300  				Options: CNIOptions{
  1301  					Calico: CalicoOptions{
  1302  						Mode: "foo",
  1303  					},
  1304  				},
  1305  			},
  1306  			valid: true,
  1307  		},
  1308  		{
  1309  			n: CNI{
  1310  				Provider: "calico",
  1311  				Options: CNIOptions{
  1312  					Calico: CalicoOptions{
  1313  						Mode:     "overlay",
  1314  						LogLevel: "",
  1315  					},
  1316  				},
  1317  			},
  1318  			valid: true,
  1319  		},
  1320  		{
  1321  			n: CNI{
  1322  				Provider: "calico",
  1323  				Options: CNIOptions{
  1324  					Calico: CalicoOptions{
  1325  						Mode:     "overlay",
  1326  						LogLevel: "info",
  1327  					},
  1328  				},
  1329  			},
  1330  			valid: true,
  1331  		},
  1332  		{
  1333  			n: CNI{
  1334  				Provider: "calico",
  1335  				Options: CNIOptions{
  1336  					Calico: CalicoOptions{
  1337  						Mode:     "overlay",
  1338  						LogLevel: "warning",
  1339  					},
  1340  				},
  1341  			},
  1342  			valid: true,
  1343  		},
  1344  		{
  1345  			n: CNI{
  1346  				Provider: "calico",
  1347  				Options: CNIOptions{
  1348  					Calico: CalicoOptions{
  1349  						Mode:     "overlay",
  1350  						LogLevel: "debug",
  1351  					},
  1352  				},
  1353  			},
  1354  			valid: true,
  1355  		},
  1356  		{
  1357  			n: CNI{
  1358  				Provider: "calico",
  1359  				Options: CNIOptions{
  1360  					Calico: CalicoOptions{
  1361  						Mode:     "overlay",
  1362  						LogLevel: "INFO",
  1363  					},
  1364  				},
  1365  			},
  1366  			valid: false,
  1367  		},
  1368  		{
  1369  			n: CNI{
  1370  				Provider: "calico",
  1371  				Options: CNIOptions{
  1372  					Calico: CalicoOptions{
  1373  						Mode:     "overlay",
  1374  						LogLevel: "foo",
  1375  					},
  1376  				},
  1377  			},
  1378  			valid: false,
  1379  		},
  1380  	}
  1381  	for i, test := range tests {
  1382  		ok, _ := test.n.validate()
  1383  		if ok != test.valid {
  1384  			t.Errorf("test %d: expect %t, but got %t", i, test.valid, ok)
  1385  		}
  1386  	}
  1387  }
  1388  
  1389  func TestDNSProvider(t *testing.T) {
  1390  	tests := []struct {
  1391  		d     DNS
  1392  		valid bool
  1393  	}{
  1394  		{
  1395  			d: DNS{
  1396  				Provider: "kubedns",
  1397  			},
  1398  			valid: true,
  1399  		},
  1400  		{
  1401  			d: DNS{
  1402  				Provider: "coredns",
  1403  			},
  1404  			valid: true,
  1405  		},
  1406  		{
  1407  			d: DNS{
  1408  				Disable:  true,
  1409  				Provider: "foo",
  1410  			},
  1411  			valid: true,
  1412  		},
  1413  		{
  1414  			d: DNS{
  1415  				Provider: "foo",
  1416  			},
  1417  			valid: false,
  1418  		},
  1419  	}
  1420  	for i, test := range tests {
  1421  		ok, _ := test.d.validate()
  1422  		if ok != test.valid {
  1423  			t.Errorf("test %d: expect %t, but got %t", i, test.valid, ok)
  1424  		}
  1425  	}
  1426  }
  1427  
  1428  func TestHeapsterAddOn(t *testing.T) {
  1429  	tests := []struct {
  1430  		h     HeapsterMonitoring
  1431  		valid bool
  1432  	}{
  1433  		{
  1434  			h: HeapsterMonitoring{
  1435  				Options: HeapsterOptions{
  1436  					Heapster: Heapster{
  1437  						Replicas:    0,
  1438  						ServiceType: "ClusterIP",
  1439  					},
  1440  				},
  1441  			},
  1442  			valid: false,
  1443  		},
  1444  		{
  1445  			h: HeapsterMonitoring{
  1446  				Options: HeapsterOptions{
  1447  					Heapster: Heapster{
  1448  						Replicas:    1,
  1449  						ServiceType: "Foo",
  1450  					},
  1451  				},
  1452  			},
  1453  			valid: false,
  1454  		},
  1455  		{
  1456  			h: HeapsterMonitoring{
  1457  				Options: HeapsterOptions{
  1458  					Heapster: Heapster{
  1459  						Replicas:    -1,
  1460  						ServiceType: "ClusterIP",
  1461  					},
  1462  				},
  1463  			},
  1464  			valid: false,
  1465  		},
  1466  		{
  1467  			h: HeapsterMonitoring{
  1468  				Options: HeapsterOptions{
  1469  					Heapster: Heapster{
  1470  						Replicas:    1,
  1471  						ServiceType: "ClusterIP",
  1472  					},
  1473  				},
  1474  			},
  1475  			valid: true,
  1476  		},
  1477  	}
  1478  	for i, test := range tests {
  1479  		ok, _ := test.h.validate()
  1480  		if ok != test.valid {
  1481  			t.Errorf("test %d: expect %t, but got %t", i, test.valid, ok)
  1482  		}
  1483  	}
  1484  }
  1485  
  1486  func TestDashbordAddOn(t *testing.T) {
  1487  	tests := []struct {
  1488  		d     Dashboard
  1489  		valid bool
  1490  	}{
  1491  		{
  1492  			d: Dashboard{
  1493  				Options: DashboardOptions{
  1494  					ServiceType: "ClusterIP",
  1495  				},
  1496  			},
  1497  			valid: true,
  1498  		},
  1499  		{
  1500  			d: Dashboard{
  1501  				Options: DashboardOptions{
  1502  					ServiceType: "Foo",
  1503  				},
  1504  			},
  1505  			valid: false,
  1506  		},
  1507  		{
  1508  			d: Dashboard{
  1509  				Options: DashboardOptions{
  1510  					ServiceType: "ClusterIP",
  1511  					NodePort:    "32500",
  1512  				},
  1513  			},
  1514  			valid: false,
  1515  		},
  1516  		{
  1517  			d: Dashboard{
  1518  				Options: DashboardOptions{
  1519  					ServiceType: "NodePort",
  1520  					NodePort:    "32500",
  1521  				},
  1522  			},
  1523  			valid: true,
  1524  		},
  1525  	}
  1526  	for i, test := range tests {
  1527  		ok, _ := test.d.validate()
  1528  		if ok != test.valid {
  1529  			t.Errorf("test %d: expect %t, but got %t", i, test.valid, ok)
  1530  		}
  1531  	}
  1532  }
  1533  
  1534  func TestPackageManagerAddOn(t *testing.T) {
  1535  	tests := []struct {
  1536  		p     PackageManager
  1537  		valid bool
  1538  	}{
  1539  		{
  1540  			p: PackageManager{
  1541  				Disable:  false,
  1542  				Provider: "helm",
  1543  			},
  1544  			valid: true,
  1545  		},
  1546  		{
  1547  			p: PackageManager{
  1548  				Disable:  true,
  1549  				Provider: "",
  1550  			},
  1551  			valid: true,
  1552  		},
  1553  		{
  1554  			p: PackageManager{
  1555  				Disable:  true,
  1556  				Provider: "foo",
  1557  			},
  1558  			valid: true,
  1559  		},
  1560  		{
  1561  			p: PackageManager{
  1562  				Disable:  false,
  1563  				Provider: "",
  1564  			},
  1565  			valid: true,
  1566  		},
  1567  		{
  1568  			p: PackageManager{
  1569  				Disable:  false,
  1570  				Provider: "foo",
  1571  			},
  1572  			valid: false,
  1573  		},
  1574  	}
  1575  	for i, test := range tests {
  1576  		ok, _ := test.p.validate()
  1577  		if ok != test.valid {
  1578  			t.Errorf("test %d: expect %t, but got %t", i, test.valid, ok)
  1579  		}
  1580  	}
  1581  }
  1582  
  1583  func TestCloudProvider(t *testing.T) {
  1584  	tests := []struct {
  1585  		c     CloudProvider
  1586  		valid bool
  1587  	}{
  1588  		{
  1589  			c: CloudProvider{
  1590  				Provider: "",
  1591  			},
  1592  			valid: true,
  1593  		},
  1594  		{
  1595  			c: CloudProvider{
  1596  				Provider: "aws",
  1597  			},
  1598  			valid: true,
  1599  		},
  1600  		{
  1601  			c: CloudProvider{
  1602  				Provider: "awss",
  1603  			},
  1604  			valid: false,
  1605  		},
  1606  		{
  1607  			c: CloudProvider{
  1608  				Provider: "gce",
  1609  				Config:   "/bin/sh",
  1610  			},
  1611  			valid: true,
  1612  		},
  1613  		{
  1614  			c: CloudProvider{
  1615  				Provider: "gce",
  1616  				Config:   "/bin/foo",
  1617  			},
  1618  			valid: false,
  1619  		},
  1620  		{
  1621  			c: CloudProvider{
  1622  				Provider: "gce",
  1623  				Config:   "foo",
  1624  			},
  1625  			valid: false,
  1626  		},
  1627  	}
  1628  	for i, test := range tests {
  1629  		ok, _ := test.c.validate()
  1630  		if ok != test.valid {
  1631  			t.Errorf("test %d: expect %t, but got %t", i, test.valid, ok)
  1632  		}
  1633  	}
  1634  }
  1635  
  1636  func TestNodeLabels(t *testing.T) {
  1637  	tests := []struct {
  1638  		n     Node
  1639  		valid bool
  1640  	}{
  1641  		{
  1642  			n: Node{
  1643  				Host: "foo",
  1644  				IP:   "192.1.1.1",
  1645  			},
  1646  			valid: true,
  1647  		},
  1648  		{
  1649  			n: Node{
  1650  				Host:   "foo",
  1651  				IP:     "192.1.1.1",
  1652  				Labels: map[string]string{},
  1653  			},
  1654  			valid: true,
  1655  		},
  1656  		{
  1657  			n: Node{
  1658  				Host:   "foo",
  1659  				IP:     "192.1.1.1",
  1660  				Labels: map[string]string{"com.foo/bar": ""},
  1661  			},
  1662  			valid: true,
  1663  		},
  1664  		{
  1665  			n: Node{
  1666  				Host:   "foo",
  1667  				IP:     "192.1.1.1",
  1668  				Labels: map[string]string{"com.foo/bar": "foobar"},
  1669  			},
  1670  			valid: true,
  1671  		},
  1672  		{
  1673  			n: Node{
  1674  				Host:   "foo",
  1675  				IP:     "192.1.1.1",
  1676  				Labels: map[string]string{"com.foo/bar": "foobar", "com.foo/xyz": "xyz"},
  1677  			},
  1678  			valid: true,
  1679  		},
  1680  		{
  1681  			n: Node{
  1682  				Host:   "foo",
  1683  				IP:     "192.1.1.1",
  1684  				Labels: map[string]string{"kismatic/foo": "bar"},
  1685  			},
  1686  			valid: false,
  1687  		},
  1688  		{
  1689  			n: Node{
  1690  				Host:   "foo",
  1691  				IP:     "192.1.1.1",
  1692  				Labels: map[string]string{"com.foo/kismatic-version": "v1.0.0"},
  1693  			},
  1694  			valid: true,
  1695  		},
  1696  		{
  1697  			n: Node{
  1698  				Host:   "foo",
  1699  				IP:     "192.1.1.1",
  1700  				Labels: map[string]string{"": "com.foo/worker"},
  1701  			},
  1702  			valid: false,
  1703  		},
  1704  		{
  1705  			n: Node{
  1706  				Host:   "foo",
  1707  				IP:     "192.1.1.1",
  1708  				Labels: map[string]string{"": ""},
  1709  			},
  1710  			valid: false,
  1711  		},
  1712  		{
  1713  			n: Node{
  1714  				Host:   "foo",
  1715  				IP:     "192.1.1.1",
  1716  				Labels: map[string]string{"node-type:test": "test"},
  1717  			},
  1718  			valid: false,
  1719  		},
  1720  		{
  1721  			n: Node{
  1722  				Host:   "foo",
  1723  				IP:     "192.1.1.1",
  1724  				Labels: map[string]string{"com.foo/invalid": ":test"},
  1725  			},
  1726  			valid: false,
  1727  		},
  1728  		{
  1729  			n: Node{
  1730  				Host:   "foo",
  1731  				IP:     "192.1.1.1",
  1732  				Labels: map[string]string{"node-type:test": ":test"},
  1733  			},
  1734  			valid: false,
  1735  		},
  1736  	}
  1737  	for i, test := range tests {
  1738  		ok, _ := test.n.validate()
  1739  		if ok != test.valid {
  1740  			t.Errorf("test %d: expect %t, but got %t", i, test.valid, ok)
  1741  		}
  1742  	}
  1743  }
  1744  
  1745  func TestNodeTaints(t *testing.T) {
  1746  	tests := []struct {
  1747  		n     Node
  1748  		valid bool
  1749  	}{
  1750  		{
  1751  			n: Node{
  1752  				Host: "foo",
  1753  				IP:   "192.1.1.1",
  1754  			},
  1755  			valid: true,
  1756  		},
  1757  		{
  1758  			n: Node{
  1759  				Host:   "foo",
  1760  				IP:     "192.1.1.1",
  1761  				Taints: []Taint{},
  1762  			},
  1763  			valid: true,
  1764  		},
  1765  		{
  1766  			n: Node{
  1767  				Host: "foo",
  1768  				IP:   "192.1.1.1",
  1769  				Taints: []Taint{
  1770  					Taint{
  1771  						Key:    "com.foo/bar",
  1772  						Value:  "",
  1773  						Effect: "NoSchedule",
  1774  					},
  1775  				},
  1776  			},
  1777  			valid: true,
  1778  		},
  1779  		{
  1780  			n: Node{
  1781  				Host: "foo",
  1782  				IP:   "192.1.1.1",
  1783  				Taints: []Taint{
  1784  					Taint{
  1785  						Key:    "com.foo/bar",
  1786  						Value:  "foobar",
  1787  						Effect: "NoSchedule",
  1788  					},
  1789  				},
  1790  			},
  1791  			valid: true,
  1792  		},
  1793  		{
  1794  			n: Node{
  1795  				Host: "foo",
  1796  				IP:   "192.1.1.1",
  1797  				Taints: []Taint{
  1798  					Taint{
  1799  						Key:    "com.foo/bar",
  1800  						Value:  "foobar",
  1801  						Effect: "NoSchedule",
  1802  					},
  1803  					Taint{
  1804  						Key:    "com.foo/xyz",
  1805  						Value:  "xyz",
  1806  						Effect: "NoSchedule",
  1807  					},
  1808  				},
  1809  			},
  1810  			valid: true,
  1811  		},
  1812  		{
  1813  			n: Node{
  1814  				Host: "foo",
  1815  				IP:   "192.1.1.1",
  1816  				Taints: []Taint{
  1817  					Taint{
  1818  						Key:    "kismatic/foo",
  1819  						Value:  "bar",
  1820  						Effect: "NoSchedule",
  1821  					},
  1822  				},
  1823  			},
  1824  			valid: false,
  1825  		},
  1826  		{
  1827  			n: Node{
  1828  				Host: "foo",
  1829  				IP:   "192.1.1.1",
  1830  				Taints: []Taint{
  1831  					Taint{
  1832  						Key:    "com.foo/kismatic-version",
  1833  						Value:  "v1.0.0",
  1834  						Effect: "NoSchedule",
  1835  					},
  1836  				},
  1837  			},
  1838  			valid: true,
  1839  		},
  1840  		{
  1841  			n: Node{
  1842  				Host: "foo",
  1843  				IP:   "192.1.1.1",
  1844  				Taints: []Taint{
  1845  					Taint{
  1846  						Key:    "",
  1847  						Value:  "v1.0.0",
  1848  						Effect: "NoSchedule",
  1849  					},
  1850  				},
  1851  			},
  1852  			valid: false,
  1853  		},
  1854  		{
  1855  			n: Node{
  1856  				Host: "foo",
  1857  				IP:   "192.1.1.1",
  1858  				Taints: []Taint{
  1859  					Taint{
  1860  						Key:    "",
  1861  						Value:  "",
  1862  						Effect: "NoSchedule",
  1863  					},
  1864  				},
  1865  				Labels: map[string]string{"": ""},
  1866  			},
  1867  			valid: false,
  1868  		},
  1869  		{
  1870  			n: Node{
  1871  				Host: "foo",
  1872  				IP:   "192.1.1.1",
  1873  				Taints: []Taint{
  1874  					Taint{
  1875  						Key:    "",
  1876  						Value:  "",
  1877  						Effect: "",
  1878  					},
  1879  				},
  1880  				Labels: map[string]string{"": ""},
  1881  			},
  1882  			valid: false,
  1883  		},
  1884  		{
  1885  			n: Node{
  1886  				Host: "foo",
  1887  				IP:   "192.1.1.1",
  1888  				Taints: []Taint{
  1889  					Taint{
  1890  						Key:    "node-type:test",
  1891  						Value:  "test",
  1892  						Effect: "NoSchedule",
  1893  					},
  1894  				},
  1895  				Labels: map[string]string{"node-type:test": "test"},
  1896  			},
  1897  			valid: false,
  1898  		},
  1899  		{
  1900  			n: Node{
  1901  				Host: "foo",
  1902  				IP:   "192.1.1.1",
  1903  				Taints: []Taint{
  1904  					Taint{
  1905  						Key:    "com.foo/invalid",
  1906  						Value:  ":test",
  1907  						Effect: "NoSchedule",
  1908  					},
  1909  				},
  1910  			},
  1911  			valid: false,
  1912  		},
  1913  		{
  1914  			n: Node{
  1915  				Host: "foo",
  1916  				IP:   "192.1.1.1",
  1917  				Taints: []Taint{
  1918  					Taint{
  1919  						Key:    "node-type:test",
  1920  						Value:  ":test",
  1921  						Effect: "NoSchedule",
  1922  					},
  1923  				},
  1924  			},
  1925  			valid: false,
  1926  		},
  1927  		{
  1928  			n: Node{
  1929  				Host: "foo",
  1930  				IP:   "192.1.1.1",
  1931  				Taints: []Taint{
  1932  					Taint{
  1933  						Key:    "kismatic/foo",
  1934  						Value:  "bar",
  1935  						Effect: "",
  1936  					},
  1937  				},
  1938  			},
  1939  			valid: false,
  1940  		},
  1941  		{
  1942  			n: Node{
  1943  				Host: "foo",
  1944  				IP:   "192.1.1.1",
  1945  				Taints: []Taint{
  1946  					Taint{
  1947  						Key:    "kismatic/foo",
  1948  						Value:  "bar",
  1949  						Effect: "Foo",
  1950  					},
  1951  				},
  1952  			},
  1953  			valid: false,
  1954  		},
  1955  	}
  1956  	for i, test := range tests {
  1957  		ok, _ := test.n.validate()
  1958  		if ok != test.valid {
  1959  			t.Errorf("test %d: expect %t, but got %t", i, test.valid, ok)
  1960  		}
  1961  	}
  1962  }
  1963  
  1964  func TestNodeKubeletOptions(t *testing.T) {
  1965  	tests := []struct {
  1966  		nl    nodeList
  1967  		valid bool
  1968  	}{
  1969  		{
  1970  			nl: nodeList{
  1971  				[]Node{
  1972  					{
  1973  						Host: "host1",
  1974  						IP:   "10.0.0.1",
  1975  						KubeletOptions: KubeletOptions{
  1976  							Overrides: map[string]string{
  1977  								"v": "2",
  1978  							},
  1979  						},
  1980  					},
  1981  					{
  1982  						Host: "host2",
  1983  						IP:   "10.0.0.2",
  1984  						KubeletOptions: KubeletOptions{
  1985  							Overrides: map[string]string{
  1986  								"v": "2",
  1987  							},
  1988  						},
  1989  					},
  1990  				},
  1991  			},
  1992  			valid: true,
  1993  		},
  1994  		{
  1995  			nl: nodeList{
  1996  				[]Node{
  1997  					{
  1998  						Host: "host1",
  1999  						IP:   "10.0.0.1",
  2000  						KubeletOptions: KubeletOptions{
  2001  							Overrides: map[string]string{
  2002  								"v": "2",
  2003  							},
  2004  						},
  2005  					},
  2006  					{
  2007  						Host: "host1",
  2008  						IP:   "10.0.0.1",
  2009  						KubeletOptions: KubeletOptions{
  2010  							Overrides: map[string]string{
  2011  								"v": "2",
  2012  							},
  2013  						},
  2014  					},
  2015  				},
  2016  			},
  2017  			valid: true,
  2018  		},
  2019  		{
  2020  			nl: nodeList{
  2021  				[]Node{
  2022  					{
  2023  						Host: "host1",
  2024  						IP:   "10.0.0.1",
  2025  						KubeletOptions: KubeletOptions{
  2026  							Overrides: map[string]string{
  2027  								"v": "2",
  2028  							},
  2029  						},
  2030  					},
  2031  					{
  2032  						Host: "host1",
  2033  						IP:   "10.0.0.1",
  2034  						KubeletOptions: KubeletOptions{
  2035  							Overrides: map[string]string{
  2036  								"foo": "bar",
  2037  							},
  2038  						},
  2039  					},
  2040  				},
  2041  			},
  2042  			valid: false,
  2043  		},
  2044  		{
  2045  			nl: nodeList{
  2046  				[]Node{
  2047  					{
  2048  						Host: "host1",
  2049  						IP:   "10.0.0.1",
  2050  						KubeletOptions: KubeletOptions{
  2051  							Overrides: map[string]string{
  2052  								"v": "2",
  2053  							},
  2054  						},
  2055  					},
  2056  					{
  2057  						Host: "host1",
  2058  						IP:   "10.0.0.1",
  2059  						KubeletOptions: KubeletOptions{
  2060  							Overrides: map[string]string{
  2061  								"v": "3",
  2062  							},
  2063  						},
  2064  					},
  2065  				},
  2066  			},
  2067  			valid: false,
  2068  		},
  2069  		{
  2070  			nl: nodeList{
  2071  				[]Node{
  2072  					{
  2073  						Host: "host1",
  2074  						IP:   "10.0.0.1",
  2075  						KubeletOptions: KubeletOptions{
  2076  							Overrides: map[string]string{
  2077  								"v": "2",
  2078  							},
  2079  						},
  2080  					},
  2081  					{
  2082  						Host: "host1",
  2083  						IP:   "10.0.0.1",
  2084  						KubeletOptions: KubeletOptions{
  2085  							Overrides: map[string]string{
  2086  								"v":   "2",
  2087  								"foo": "bar",
  2088  							},
  2089  						},
  2090  					},
  2091  				},
  2092  			},
  2093  			valid: false,
  2094  		},
  2095  	}
  2096  	for i, test := range tests {
  2097  		ok, _ := test.nl.validate()
  2098  		if ok != test.valid {
  2099  			t.Errorf("test %d: expect %t, but got %t", i, test.valid, ok)
  2100  		}
  2101  	}
  2102  }
  2103  
  2104  func TestValidateFile(t *testing.T) {
  2105  	tests := []struct {
  2106  		srcPath        string
  2107  		destPath       string
  2108  		hosts          []string
  2109  		skipValidation bool
  2110  		valid          bool
  2111  	}{
  2112  		{
  2113  			srcPath:  "/bin/sh",
  2114  			destPath: "/tmp/copy_xa.yaml",
  2115  			hosts:    []string{"master01"},
  2116  			valid:    true,
  2117  		},
  2118  		{
  2119  			srcPath:  "/bin/sh",
  2120  			destPath: "/tmp/copy_xa.yaml",
  2121  			hosts:    []string{"worker01"},
  2122  			valid:    true,
  2123  		},
  2124  		{
  2125  			srcPath:  "/bin/sh",
  2126  			destPath: "/tmp/copy_xa.yaml",
  2127  			hosts:    []string{"etcd01"},
  2128  			valid:    true,
  2129  		},
  2130  		{
  2131  			srcPath:        "/tmp/xa.yaml",
  2132  			destPath:       "/tmp/copy_xa.yaml",
  2133  			hosts:          []string{"master01"},
  2134  			skipValidation: true,
  2135  			valid:          true,
  2136  		},
  2137  		{
  2138  			srcPath:  "/bin/sh",
  2139  			destPath: "/tmp/copy_xa.yaml",
  2140  			hosts:    []string{"worker"},
  2141  			valid:    true,
  2142  		},
  2143  		{
  2144  			srcPath:  "/bin/sh",
  2145  			destPath: "/tmp/copy_xa.yaml",
  2146  			hosts:    []string{"all"},
  2147  			valid:    true,
  2148  		},
  2149  		{
  2150  			srcPath:  "/bin/sh",
  2151  			destPath: "/tmp/copy_xa.yaml",
  2152  			hosts:    []string{"master", "worker"},
  2153  			valid:    true,
  2154  		},
  2155  		{
  2156  			srcPath:  "/bin/sh",
  2157  			destPath: "/tmp/copy_xa.yaml",
  2158  			hosts:    []string{"master", "worker", "worker100"},
  2159  			valid:    false,
  2160  		},
  2161  		{
  2162  			srcPath:  "",
  2163  			destPath: "",
  2164  			hosts:    []string{"master01"},
  2165  			valid:    false,
  2166  		},
  2167  		{
  2168  			srcPath:  "",
  2169  			destPath: "/tmp/copy_xa.yaml",
  2170  			hosts:    []string{"master01"},
  2171  			valid:    false,
  2172  		},
  2173  		{
  2174  			srcPath:  "/bin/sh",
  2175  			destPath: "",
  2176  			hosts:    []string{"master01"},
  2177  			valid:    false,
  2178  		},
  2179  		{
  2180  			srcPath:  "../someRelativePath",
  2181  			destPath: "../someRelativePath",
  2182  			hosts:    []string{"master01"},
  2183  			valid:    false,
  2184  		},
  2185  		{
  2186  			srcPath:  "/bin/sh",
  2187  			destPath: "../someRelativePath",
  2188  			hosts:    []string{"master01"},
  2189  			valid:    false,
  2190  		},
  2191  		{
  2192  			srcPath:  "/bin/sh",
  2193  			destPath: "/bin/sh",
  2194  			hosts:    []string{},
  2195  			valid:    false,
  2196  		},
  2197  		{
  2198  			srcPath:  "/bin/sh",
  2199  			destPath: "/bin/sh",
  2200  			hosts:    []string{"master02"},
  2201  			valid:    false,
  2202  		},
  2203  		{
  2204  			srcPath:  "/bin/sh",
  2205  			destPath: "/bin/sh",
  2206  			hosts:    []string{"foo"},
  2207  			valid:    false,
  2208  		},
  2209  	}
  2210  	for n, test := range tests {
  2211  		v := []AdditionalFile{
  2212  			AdditionalFile{
  2213  				Source:         test.srcPath,
  2214  				Destination:    test.destPath,
  2215  				Hosts:          test.hosts,
  2216  				SkipValidation: test.skipValidation,
  2217  			},
  2218  		}
  2219  		plan := validPlan()
  2220  		fg := additionalFilesGroup{AdditionalFiles: v, Plan: &plan}
  2221  		if valid, errs := fg.validate(); valid != test.valid {
  2222  			t.Errorf("test %d: expect valid = %t, but got %t", n, test.valid, valid)
  2223  			if !valid {
  2224  				t.Errorf("error %v", errs)
  2225  			}
  2226  		}
  2227  	}
  2228  }