github.com/openshift/installer@v1.4.17/pkg/asset/agent/installconfig_test.go (about)

     1  package agent
     2  
     3  import (
     4  	"net"
     5  	"testing"
     6  
     7  	"github.com/golang/mock/gomock"
     8  	"github.com/stretchr/testify/assert"
     9  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    10  	"k8s.io/utils/pointer"
    11  
    12  	"github.com/openshift/installer/pkg/asset"
    13  	"github.com/openshift/installer/pkg/asset/mock"
    14  	"github.com/openshift/installer/pkg/ipnet"
    15  	"github.com/openshift/installer/pkg/types"
    16  	"github.com/openshift/installer/pkg/types/baremetal"
    17  	"github.com/openshift/installer/pkg/types/external"
    18  	"github.com/openshift/installer/pkg/types/none"
    19  	"github.com/openshift/installer/pkg/types/vsphere"
    20  )
    21  
    22  func TestInstallConfigLoad(t *testing.T) {
    23  	cases := []struct {
    24  		name           string
    25  		data           string
    26  		fetchError     error
    27  		expectedFound  bool
    28  		expectedError  string
    29  		expectedConfig *types.InstallConfig
    30  	}{
    31  		{
    32  			name: "unsupported platform",
    33  			data: `
    34  apiVersion: v1
    35  metadata:
    36      name: test-cluster
    37  baseDomain: test-domain
    38  platform:
    39    aws:
    40      region: us-east-1
    41  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
    42  `,
    43  			expectedFound: false,
    44  			expectedError: `invalid install-config configuration: Platform: Unsupported value: "aws": supported values: "baremetal", "vsphere", "none", "external"`,
    45  		},
    46  		{
    47  			name: "apiVips not set for baremetal Compact platform",
    48  			data: `
    49  apiVersion: v1
    50  metadata:
    51    name: test-cluster
    52  baseDomain: test-domain
    53  networking:
    54    clusterNetwork:
    55    - cidr: 10.128.0.0/14
    56      hostPrefix: 23
    57    networkType: OVNKubernetes
    58    machineNetwork:
    59    - cidr: 192.168.122.0/23
    60    serviceNetwork:
    61    - 172.30.0.0/16
    62  compute:
    63    - architecture: amd64
    64      hyperthreading: Enabled
    65      name: worker
    66      platform: {}
    67      replicas: 0
    68  controlPlane:
    69    architecture: amd64
    70    hyperthreading: Enabled
    71    name: master
    72    platform: {}
    73    replicas: 3
    74  platform:
    75    baremetal:
    76      externalMACAddress: "52:54:00:f6:b4:02"
    77      provisioningMACAddress: "52:54:00:6e:3b:02"
    78      ingressVIPs:
    79        - 192.168.122.11
    80      hosts:
    81        - name: host1
    82          bootMACAddress: 52:54:01:aa:aa:a1
    83        - name: host2
    84          bootMACAddress: 52:54:01:bb:bb:b1
    85        - name: host3
    86          bootMACAddress: 52:54:01:cc:cc:c1
    87  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
    88  `,
    89  			expectedFound: false,
    90  			expectedError: "invalid install-config configuration: [platform.baremetal.apiVIPs: Required value: must specify at least one VIP for the API, platform.baremetal.apiVIPs: Required value: must specify VIP for API, when VIP for ingress is set]",
    91  		},
    92  		{
    93  			name: "ingressVIP missing and deprecated vSphere credentials are present",
    94  			data: `
    95  apiVersion: v1
    96  metadata:
    97    name: test-cluster
    98  baseDomain: test-domain
    99  platform:
   100    vsphere:
   101      apiVips:
   102        - 192.168.122.10
   103      vCenter: test.vcenter.com
   104      username: testuser
   105      password: testpassword
   106      datacenter: testDatacenter
   107      defaultDatastore: testDatastore
   108  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   109  `,
   110  			expectedFound: false,
   111  			expectedError: `invalid install-config configuration: [platform.vsphere.ingressVIPs: Required value: must specify VIP for ingress, when VIP for API is set, Platform.VSphere.failureDomains.topology.folder: Required value: must specify a folder for agent-based installs]`,
   112  		},
   113  		{
   114  			name: "ingressVIP missing and vcenter vSphere credentials are present",
   115  			data: `
   116  apiVersion: v1
   117  metadata:
   118    name: test-cluster
   119  baseDomain: test-domain
   120  platform:
   121    vsphere:
   122      apiVips:
   123        - 192.168.122.10
   124      vcenters:
   125      - server: test.vcenter.com
   126        user: testuser
   127        password: testpassword
   128        datacenters:
   129        - testDatacenter
   130      failureDomains:
   131      - name: testFailuredomain
   132        server: test.vcenter.com
   133        zone: testZone
   134        region: testRegion
   135        topology:
   136          computeCluster: "/testDatacenter/host/testcluster"
   137          datacenter: testDatacenter
   138          datastore: "/testDatacenter/datastore/testDatastore"
   139          folder: "/testDatacenter/vm/testFolder"
   140          networks:
   141          - testNetwork
   142  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   143  `,
   144  			expectedFound: false,
   145  			expectedError: `invalid install-config configuration: platform.vsphere.ingressVIPs: Required value: must specify VIP for ingress, when VIP for API is set`,
   146  		},
   147  		{
   148  			name: "vcenter vSphere credentials are present but failureDomain server does not match",
   149  			data: `
   150  apiVersion: v1
   151  metadata:
   152    name: test-cluster
   153  baseDomain: test-domain
   154  platform:
   155    vsphere:
   156      apiVips:
   157        - 192.168.122.10
   158      ingressVips:
   159        - 192.168.122.11
   160      vcenters:
   161      - server: test.vcenter.com
   162        user: testuser
   163        password: testpassword
   164        datacenters:
   165        - testDatacenter
   166      failureDomains:
   167      - name: testFailuredomain
   168        server: diff1.vcenter.com
   169        zone: testZone
   170        region: testRegion
   171        topology:
   172          computeCluster: "/testDatacenter/host/testcluster"
   173          datacenter: testDatacenter
   174          datastore: "/testDatacenter/datastore/testDatastore"
   175          folder: "/testDatacenter/vm/testFolder"
   176          networks:
   177          - testNetwork
   178      - name: testFailuredomain2
   179        server: diff2.vcenter.com
   180        zone: testZone2
   181        region: testRegion2
   182        topology:
   183          computeCluster: "/testDatacenter2/host/testcluster2"
   184          datacenter: testDatacenter2
   185          datastore: "/testDatacenter2/datastore/testDatastore2"
   186          folder: "/testDatacenter2/vm/testFolder"
   187          networks:
   188          - testNetwork2
   189  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   190  `,
   191  			expectedFound: false,
   192  			expectedError: `invalid install-config configuration: [platform.vsphere.failureDomains.server: Invalid value: "diff1.vcenter.com": server does not exist in vcenters, platform.vsphere.failureDomains.server: Invalid value: "diff2.vcenter.com": server does not exist in vcenters]`,
   193  		},
   194  		{
   195  			name: "All required vSphere fields must be entered if some of them are entered - deprecated fields",
   196  			data: `
   197  apiVersion: v1
   198  metadata:
   199    name: test-cluster
   200  baseDomain: test-domain
   201  platform:
   202    vsphere:
   203      apiVips:
   204        - 192.168.122.10
   205      ingressVips:
   206        - 192.168.122.11
   207      vCenter: test.vcenter.com
   208  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   209  `,
   210  			expectedFound: false,
   211  			expectedError: `invalid install-config configuration: [Platform.VSphere.username: Required value: All credential fields are required if any one is specified, Platform.VSphere.password: Required value: All credential fields are required if any one is specified, Platform.VSphere.datacenter: Required value: All credential fields are required if any one is specified, Platform.VSphere.failureDomains.topology.folder: Required value: must specify a folder for agent-based installs]`,
   212  		},
   213  		{
   214  			name: "All required vSphere fields must be entered if some of them are entered - vcenter fields",
   215  			data: `
   216  apiVersion: v1
   217  metadata:
   218    name: test-cluster
   219  baseDomain: test-domain
   220  platform:
   221    vsphere:
   222      apiVips:
   223        - 192.168.122.10
   224      ingressVips:
   225        - 192.168.122.11
   226      vcenters:
   227      - server: test.vcenter.com
   228        user: testuser
   229  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   230  `,
   231  			expectedFound: false,
   232  			expectedError: `invalid install-config configuration: [Platform.VSphere.password: Required value: All credential fields are required if any one is specified, Platform.VSphere.datacenter: Required value: All credential fields are required if any one is specified, Platform.VSphere.failureDomains.topology.folder: Required value: must specify a folder for agent-based installs]`,
   233  		},
   234  		{
   235  			name: "ingressVIP missing for vSphere, credentials not provided and should not flag error",
   236  			data: `
   237  apiVersion: v1
   238  metadata:
   239    name: test-cluster
   240  baseDomain: test-domain
   241  platform:
   242    vsphere:
   243      apiVips:
   244        - 192.168.122.10
   245  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   246  `,
   247  			expectedFound: false,
   248  			expectedError: `invalid install-config configuration: platform.vsphere.ingressVIPs: Required value: must specify VIP for ingress, when VIP for API is set`,
   249  		},
   250  		{
   251  			name: "no compute.replicas set for SNO",
   252  			data: `
   253  apiVersion: v1
   254  metadata:
   255    name: test-cluster
   256  baseDomain: test-domain
   257  networking:
   258    networkType: OVNKubernetes
   259  controlPlane:
   260    architecture: amd64
   261    hyperthreading: Enabled
   262    name: master
   263    platform: {}
   264    replicas: 1
   265  platform:
   266    none : {}
   267  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   268  `,
   269  			expectedFound: false,
   270  			expectedError: "invalid install-config configuration: Compute.Replicas: Required value: Total number of Compute.Replicas must be 0 when ControlPlane.Replicas is 1 for platform none or external. Found 3",
   271  		},
   272  		{
   273  			name: "incorrect compute.replicas set",
   274  			data: `
   275  apiVersion: v1
   276  metadata:
   277    name: test-cluster
   278  baseDomain: test-domain
   279  networking:
   280    networkType: OVNKubernetes
   281  controlPlane:
   282    architecture: amd64
   283    hyperthreading: Enabled
   284    name: master
   285    platform: {}
   286    replicas: 2
   287  platform:
   288    external:
   289      platformName: oci
   290      cloudControllerManager: External
   291  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"authorization value\"}}}"
   292  `,
   293  			expectedFound: false,
   294  			expectedError: "invalid install-config configuration: ControlPlane.Replicas: Invalid value: 2: ControlPlane.Replicas can only be set to 3 or 1. Found 2",
   295  		},
   296  		{
   297  			name: "invalid platform for SNO cluster",
   298  			data: `
   299  apiVersion: v1
   300  metadata:
   301    name: test-cluster
   302  baseDomain: test-domain
   303  networking:
   304    networkType: OVNKubernetes
   305  compute:
   306    - architecture: amd64
   307      hyperthreading: Enabled
   308      name: worker
   309      platform: {}
   310      replicas: 0
   311  controlPlane:
   312    architecture: amd64
   313    hyperthreading: Enabled
   314    name: master
   315    platform: {}
   316    replicas: 1
   317  platform:
   318    aws:
   319      region: us-east-1
   320  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   321  `,
   322  			expectedFound: false,
   323  			expectedError: "invalid install-config configuration: [Platform: Unsupported value: \"aws\": supported values: \"baremetal\", \"vsphere\", \"none\", \"external\", Platform: Invalid value: \"aws\": Only platform none and external supports 1 ControlPlane and 0 Compute nodes]",
   324  		},
   325  		{
   326  			name: "invalid platform.baremetal for architecture ppc64le",
   327  			data: `
   328  apiVersion: v1
   329  metadata:
   330    name: test-cluster
   331  baseDomain: test-domain
   332  networking:
   333    networkType: OVNKubernetes
   334    machineNetwork:
   335    - cidr: 192.168.122.0/23
   336  compute:
   337    - architecture: ppc64le
   338      hyperthreading: Enabled
   339      name: worker
   340      platform: {}
   341      replicas: 0
   342  controlPlane:
   343    architecture: ppc64le
   344    hyperthreading: Enabled
   345    name: master
   346    platform: {}
   347    replicas: 3
   348  platform:
   349    baremetal:
   350      apiVIP: 192.168.122.10
   351      ingressVIP: 192.168.122.11
   352      hosts:
   353      - name: host1
   354        bootMACAddress: 52:54:01:aa:aa:a1
   355      - name: host2
   356        bootMACAddress: 52:54:01:bb:bb:b1
   357      - name: host3
   358        bootMACAddress: 52:54:01:cc:cc:c1
   359  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   360  `,
   361  			expectedFound: false,
   362  			expectedError: "invalid install-config configuration: Platform: Invalid value: \"baremetal\": CPU architecture \"ppc64le\" only supports platform \"none\".",
   363  		},
   364  		{
   365  			name: "invalid platform.baremetal for architecture s390x",
   366  			data: `
   367  apiVersion: v1
   368  metadata:
   369    name: test-cluster
   370  baseDomain: test-domain
   371  networking:
   372    networkType: OVNKubernetes
   373    machineNetwork:
   374    - cidr: 192.168.122.0/23
   375  compute:
   376    - architecture: s390x
   377      hyperthreading: Enabled
   378      name: worker
   379      platform: {}
   380      replicas: 0
   381  controlPlane:
   382    architecture: s390x
   383    hyperthreading: Enabled
   384    name: master
   385    platform: {}
   386    replicas: 3
   387  platform:
   388    baremetal:
   389      apiVIP: 192.168.122.10
   390      ingressVIP: 192.168.122.11
   391      hosts:
   392      - name: host1
   393        bootMACAddress: 52:54:01:aa:aa:a1
   394      - name: host2
   395        bootMACAddress: 52:54:01:bb:bb:b1
   396      - name: host3
   397        bootMACAddress: 52:54:01:cc:cc:c1
   398  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   399  `,
   400  			expectedFound: false,
   401  			expectedError: "invalid install-config configuration: Platform: Invalid value: \"baremetal\": CPU architecture \"s390x\" only supports platform \"none\".",
   402  		},
   403  		{
   404  			name: "generic platformName for external platform",
   405  			data: `
   406  apiVersion: v1
   407  metadata:
   408    name: test-cluster
   409  baseDomain: test-domain
   410  networking:
   411    networkType: OVNKubernetes
   412  compute:
   413    - architecture: amd64
   414      hyperthreading: Enabled
   415      name: worker
   416      platform: {}
   417      replicas: 0
   418  controlPlane:
   419    architecture: amd64
   420    hyperthreading: Enabled
   421    name: master
   422    platform: {}
   423    replicas: 1
   424  platform:
   425    external:
   426     platformName: some-cloud-provider
   427  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   428  `,
   429  			expectedFound: true,
   430  			expectedConfig: &types.InstallConfig{
   431  				TypeMeta: metav1.TypeMeta{
   432  					APIVersion: types.InstallConfigVersion,
   433  				},
   434  				ObjectMeta: metav1.ObjectMeta{
   435  					Name: "test-cluster",
   436  				},
   437  				AdditionalTrustBundlePolicy: types.PolicyProxyOnly,
   438  				BaseDomain:                  "test-domain",
   439  				Networking: &types.Networking{
   440  					MachineNetwork: []types.MachineNetworkEntry{
   441  						{CIDR: *ipnet.MustParseCIDR("10.0.0.0/16")},
   442  					},
   443  					NetworkType:    "OVNKubernetes",
   444  					ServiceNetwork: []ipnet.IPNet{*ipnet.MustParseCIDR("172.30.0.0/16")},
   445  					ClusterNetwork: []types.ClusterNetworkEntry{
   446  						{
   447  							CIDR:       *ipnet.MustParseCIDR("10.128.0.0/14"),
   448  							HostPrefix: 23,
   449  						},
   450  					},
   451  				},
   452  				ControlPlane: &types.MachinePool{
   453  					Name:           "master",
   454  					Replicas:       pointer.Int64(1),
   455  					Hyperthreading: types.HyperthreadingEnabled,
   456  					Architecture:   types.ArchitectureAMD64,
   457  				},
   458  				Compute: []types.MachinePool{
   459  					{
   460  						Name:           "worker",
   461  						Replicas:       pointer.Int64(0),
   462  						Hyperthreading: types.HyperthreadingEnabled,
   463  						Architecture:   types.ArchitectureAMD64,
   464  					},
   465  				},
   466  				Platform: types.Platform{
   467  					External: &external.Platform{
   468  						PlatformName:           "some-cloud-provider",
   469  						CloudControllerManager: "",
   470  					},
   471  				},
   472  				PullSecret: `{"auths":{"example.com":{"auth":"c3VwZXItc2VjcmV0Cg=="}}}`,
   473  				Publish:    types.ExternalPublishingStrategy,
   474  			},
   475  		},
   476  		{
   477  			name: "unsupported CloudControllerManager for external platform",
   478  			data: `
   479  apiVersion: v1
   480  metadata:
   481      name: test-cluster
   482  baseDomain: test-domain
   483  platform:
   484    external:
   485      platformName: oci
   486  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   487  `,
   488  			expectedFound: false,
   489  			expectedError: `invalid install-config configuration: Platform.External.CloudControllerManager: Invalid value: "": When using external oci platform, Platform.External.CloudControllerManager must be set to External`,
   490  		},
   491  		{
   492  			name: "valid configuration for none platform for sno",
   493  			data: `
   494  apiVersion: v1
   495  metadata:
   496    name: test-cluster
   497  baseDomain: test-domain
   498  networking:
   499    networkType: OVNKubernetes
   500  compute:
   501    - architecture: amd64
   502      hyperthreading: Enabled
   503      name: worker
   504      platform: {}
   505      replicas: 0
   506  controlPlane:
   507    architecture: amd64
   508    hyperthreading: Enabled
   509    name: master
   510    platform: {}
   511    replicas: 1
   512  platform:
   513    none : {}
   514  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   515  `,
   516  			expectedFound: true,
   517  			expectedConfig: &types.InstallConfig{
   518  				TypeMeta: metav1.TypeMeta{
   519  					APIVersion: types.InstallConfigVersion,
   520  				},
   521  				ObjectMeta: metav1.ObjectMeta{
   522  					Name: "test-cluster",
   523  				},
   524  				AdditionalTrustBundlePolicy: types.PolicyProxyOnly,
   525  				BaseDomain:                  "test-domain",
   526  				Networking: &types.Networking{
   527  					MachineNetwork: []types.MachineNetworkEntry{
   528  						{CIDR: *ipnet.MustParseCIDR("10.0.0.0/16")},
   529  					},
   530  					NetworkType:    "OVNKubernetes",
   531  					ServiceNetwork: []ipnet.IPNet{*ipnet.MustParseCIDR("172.30.0.0/16")},
   532  					ClusterNetwork: []types.ClusterNetworkEntry{
   533  						{
   534  							CIDR:       *ipnet.MustParseCIDR("10.128.0.0/14"),
   535  							HostPrefix: 23,
   536  						},
   537  					},
   538  				},
   539  				ControlPlane: &types.MachinePool{
   540  					Name:           "master",
   541  					Replicas:       pointer.Int64(1),
   542  					Hyperthreading: types.HyperthreadingEnabled,
   543  					Architecture:   types.ArchitectureAMD64,
   544  				},
   545  				Compute: []types.MachinePool{
   546  					{
   547  						Name:           "worker",
   548  						Replicas:       pointer.Int64(0),
   549  						Hyperthreading: types.HyperthreadingEnabled,
   550  						Architecture:   types.ArchitectureAMD64,
   551  					},
   552  				},
   553  				Platform:   types.Platform{None: &none.Platform{}},
   554  				PullSecret: `{"auths":{"example.com":{"auth":"c3VwZXItc2VjcmV0Cg=="}}}`,
   555  				Publish:    types.ExternalPublishingStrategy,
   556  			},
   557  		},
   558  		{
   559  			name: "valid configuration for none platform for HA cluster",
   560  			data: `
   561  apiVersion: v1
   562  metadata:
   563    name: test-cluster
   564  baseDomain: test-domain
   565  networking:
   566    networkType: OVNKubernetes
   567  compute:
   568    - architecture: amd64
   569      hyperthreading: Enabled
   570      name: worker
   571      platform: {}
   572      replicas: 2
   573  controlPlane:
   574    architecture: amd64
   575    hyperthreading: Enabled
   576    name: master
   577    platform: {}
   578    replicas: 3
   579  platform:
   580    none : {}
   581  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   582  `,
   583  			expectedFound: true,
   584  			expectedConfig: &types.InstallConfig{
   585  				TypeMeta: metav1.TypeMeta{
   586  					APIVersion: types.InstallConfigVersion,
   587  				},
   588  				ObjectMeta: metav1.ObjectMeta{
   589  					Name: "test-cluster",
   590  				},
   591  				AdditionalTrustBundlePolicy: types.PolicyProxyOnly,
   592  				BaseDomain:                  "test-domain",
   593  				Networking: &types.Networking{
   594  					MachineNetwork: []types.MachineNetworkEntry{
   595  						{CIDR: *ipnet.MustParseCIDR("10.0.0.0/16")},
   596  					},
   597  					NetworkType:    "OVNKubernetes",
   598  					ServiceNetwork: []ipnet.IPNet{*ipnet.MustParseCIDR("172.30.0.0/16")},
   599  					ClusterNetwork: []types.ClusterNetworkEntry{
   600  						{
   601  							CIDR:       *ipnet.MustParseCIDR("10.128.0.0/14"),
   602  							HostPrefix: 23,
   603  						},
   604  					},
   605  				},
   606  				ControlPlane: &types.MachinePool{
   607  					Name:           "master",
   608  					Replicas:       pointer.Int64(3),
   609  					Hyperthreading: types.HyperthreadingEnabled,
   610  					Architecture:   types.ArchitectureAMD64,
   611  				},
   612  				Compute: []types.MachinePool{
   613  					{
   614  						Name:           "worker",
   615  						Replicas:       pointer.Int64(2),
   616  						Hyperthreading: types.HyperthreadingEnabled,
   617  						Architecture:   types.ArchitectureAMD64,
   618  					},
   619  				},
   620  				Platform:   types.Platform{None: &none.Platform{}},
   621  				PullSecret: `{"auths":{"example.com":{"auth":"c3VwZXItc2VjcmV0Cg=="}}}`,
   622  				Publish:    types.ExternalPublishingStrategy,
   623  			},
   624  		},
   625  		{
   626  			name: "valid configuration for baremetal platform for HA cluster - deprecated and unused fields",
   627  			data: `
   628  apiVersion: v1
   629  metadata:
   630    name: test-cluster
   631  baseDomain: test-domain
   632  networking:
   633    clusterNetwork:
   634    - cidr: 10.128.0.0/14
   635      hostPrefix: 23
   636    networkType: OVNKubernetes
   637    machineNetwork:
   638    - cidr: 192.168.122.0/23
   639    serviceNetwork:
   640    - 172.30.0.0/16
   641  compute:
   642    - architecture: amd64
   643      hyperthreading: Disabled
   644      name: worker
   645      platform: {}
   646      replicas: 2
   647  controlPlane:
   648    architecture: amd64
   649    hyperthreading: Disabled
   650    name: master
   651    platform: {}
   652    replicas: 3
   653  platform:
   654    baremetal:
   655      libvirtURI: qemu+ssh://root@52.116.73.24/system
   656      clusterProvisioningIP: "192.168.122.90"
   657      bootstrapProvisioningIP: "192.168.122.91"
   658      externalBridge: "somevalue"
   659      externalMACAddress: "52:54:00:f6:b4:02"
   660      provisioningNetwork: "Disabled"
   661      provisioningBridge: br0
   662      provisioningMACAddress: "52:54:00:6e:3b:02"
   663      provisioningNetworkInterface: "eth11"
   664      provisioningDHCPExternal: true
   665      provisioningDHCPRange: 172.22.0.10,172.22.0.254
   666      apiVIP: 192.168.122.10
   667      ingressVIP: 192.168.122.11
   668      bootstrapOSImage: https://mirror.example.com/images/qemu.qcow2.gz?sha256=a07bd
   669      clusterOSImage: https://mirror.example.com/images/metal.qcow2.gz?sha256=3b5a8
   670      bootstrapExternalStaticIP: 192.1168.122.50
   671      bootstrapExternalStaticGateway: gateway
   672      hosts:
   673        - name: host1
   674          bootMACAddress: 52:54:01:aa:aa:a1
   675        - name: host2
   676          bootMACAddress: 52:54:01:bb:bb:b1
   677        - name: host3
   678          bootMACAddress: 52:54:01:cc:cc:c1
   679        - name: host4
   680          bootMACAddress: 52:54:01:dd:dd:d1
   681        - name: host5
   682          bootMACAddress: 52:54:01:ee:ee:e1
   683  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   684  `,
   685  			expectedFound: true,
   686  			expectedConfig: &types.InstallConfig{
   687  				TypeMeta: metav1.TypeMeta{
   688  					APIVersion: types.InstallConfigVersion,
   689  				},
   690  				ObjectMeta: metav1.ObjectMeta{
   691  					Name: "test-cluster",
   692  				},
   693  				AdditionalTrustBundlePolicy: types.PolicyProxyOnly,
   694  				BaseDomain:                  "test-domain",
   695  				Networking: &types.Networking{
   696  					MachineNetwork: []types.MachineNetworkEntry{
   697  						{CIDR: *ipnet.MustParseCIDR("192.168.122.0/23")},
   698  					},
   699  					NetworkType:    "OVNKubernetes",
   700  					ServiceNetwork: []ipnet.IPNet{*ipnet.MustParseCIDR("172.30.0.0/16")},
   701  					ClusterNetwork: []types.ClusterNetworkEntry{
   702  						{
   703  							CIDR:       *ipnet.MustParseCIDR("10.128.0.0/14"),
   704  							HostPrefix: 23,
   705  						},
   706  					},
   707  				},
   708  				ControlPlane: &types.MachinePool{
   709  					Name:           "master",
   710  					Replicas:       pointer.Int64(3),
   711  					Hyperthreading: types.HyperthreadingDisabled,
   712  					Architecture:   types.ArchitectureAMD64,
   713  				},
   714  				Compute: []types.MachinePool{
   715  					{
   716  						Name:           "worker",
   717  						Replicas:       pointer.Int64(2),
   718  						Hyperthreading: types.HyperthreadingDisabled,
   719  						Architecture:   types.ArchitectureAMD64,
   720  					},
   721  				},
   722  				Platform: types.Platform{
   723  					BareMetal: &baremetal.Platform{
   724  						LibvirtURI:                         "qemu+ssh://root@52.116.73.24/system",
   725  						ClusterProvisioningIP:              "192.168.122.90",
   726  						BootstrapProvisioningIP:            "192.168.122.91",
   727  						ExternalBridge:                     "somevalue",
   728  						ExternalMACAddress:                 "52:54:00:f6:b4:02",
   729  						ProvisioningNetwork:                "Disabled",
   730  						ProvisioningBridge:                 "br0",
   731  						ProvisioningMACAddress:             "52:54:00:6e:3b:02",
   732  						ProvisioningDHCPRange:              "172.22.0.10,172.22.0.254",
   733  						DeprecatedProvisioningDHCPExternal: true,
   734  						ProvisioningNetworkCIDR: &ipnet.IPNet{
   735  							IPNet: net.IPNet{
   736  								IP:   []byte("\xc0\xa8\x7a\x00"),
   737  								Mask: []byte("\xff\xff\xfe\x00"),
   738  							},
   739  						},
   740  						ProvisioningNetworkInterface: "eth11",
   741  						Hosts: []*baremetal.Host{
   742  							{
   743  								Name:            "host1",
   744  								BootMACAddress:  "52:54:01:aa:aa:a1",
   745  								BootMode:        "UEFI",
   746  								HardwareProfile: "default",
   747  							},
   748  							{
   749  								Name:            "host2",
   750  								BootMACAddress:  "52:54:01:bb:bb:b1",
   751  								BootMode:        "UEFI",
   752  								HardwareProfile: "default",
   753  							},
   754  							{
   755  								Name:            "host3",
   756  								BootMACAddress:  "52:54:01:cc:cc:c1",
   757  								BootMode:        "UEFI",
   758  								HardwareProfile: "default",
   759  							},
   760  							{
   761  								Name:            "host4",
   762  								BootMACAddress:  "52:54:01:dd:dd:d1",
   763  								BootMode:        "UEFI",
   764  								HardwareProfile: "default",
   765  							},
   766  							{
   767  								Name:            "host5",
   768  								BootMACAddress:  "52:54:01:ee:ee:e1",
   769  								BootMode:        "UEFI",
   770  								HardwareProfile: "default",
   771  							}},
   772  						DeprecatedAPIVIP:               "192.168.122.10",
   773  						APIVIPs:                        []string{"192.168.122.10"},
   774  						DeprecatedIngressVIP:           "192.168.122.11",
   775  						IngressVIPs:                    []string{"192.168.122.11"},
   776  						BootstrapOSImage:               "https://mirror.example.com/images/qemu.qcow2.gz?sha256=a07bd",
   777  						ClusterOSImage:                 "https://mirror.example.com/images/metal.qcow2.gz?sha256=3b5a8",
   778  						BootstrapExternalStaticIP:      "192.1168.122.50",
   779  						BootstrapExternalStaticGateway: "gateway",
   780  					},
   781  				},
   782  				PullSecret: `{"auths":{"example.com":{"auth":"c3VwZXItc2VjcmV0Cg=="}}}`,
   783  				Publish:    types.ExternalPublishingStrategy,
   784  			},
   785  		},
   786  		{
   787  			name: "valid configuration for vsphere platform for compact cluster - deprecated field apiVip",
   788  			data: `
   789  apiVersion: v1
   790  metadata:
   791    name: test-cluster
   792  baseDomain: test-domain
   793  networking:
   794    clusterNetwork:
   795    - cidr: 10.128.0.0/14
   796      hostPrefix: 23
   797    networkType: OVNKubernetes
   798    machineNetwork:
   799    - cidr: 192.168.122.0/23
   800    serviceNetwork: 
   801    - 172.30.0.0/16
   802  compute:
   803    - architecture: amd64
   804      hyperthreading: Enabled
   805      name: worker
   806      platform: {}
   807      replicas: 0
   808  controlPlane:
   809    architecture: amd64
   810    hyperthreading: Enabled
   811    name: master
   812    platform: {}
   813    replicas: 3
   814  platform:
   815    vsphere :
   816      vcenter: 192.168.122.30
   817      username: testUsername
   818      password: testPassword
   819      datacenter: testDataCenter
   820      defaultDataStore: testDefaultDataStore
   821      folder: testFolder
   822      cluster: testCluster
   823      apiVIP: 192.168.122.10
   824      ingressVIPs: 
   825        - 192.168.122.11
   826  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   827  `,
   828  			expectedFound: true,
   829  			expectedConfig: &types.InstallConfig{
   830  				TypeMeta: metav1.TypeMeta{
   831  					APIVersion: types.InstallConfigVersion,
   832  				},
   833  				ObjectMeta: metav1.ObjectMeta{
   834  					Name: "test-cluster",
   835  				},
   836  				AdditionalTrustBundlePolicy: types.PolicyProxyOnly,
   837  				BaseDomain:                  "test-domain",
   838  				Networking: &types.Networking{
   839  					MachineNetwork: []types.MachineNetworkEntry{
   840  						{CIDR: *ipnet.MustParseCIDR("192.168.122.0/23")},
   841  					},
   842  					NetworkType:    "OVNKubernetes",
   843  					ServiceNetwork: []ipnet.IPNet{*ipnet.MustParseCIDR("172.30.0.0/16")},
   844  					ClusterNetwork: []types.ClusterNetworkEntry{
   845  						{
   846  							CIDR:       *ipnet.MustParseCIDR("10.128.0.0/14"),
   847  							HostPrefix: 23,
   848  						},
   849  					},
   850  				},
   851  				ControlPlane: &types.MachinePool{
   852  					Name:           "master",
   853  					Replicas:       pointer.Int64(3),
   854  					Hyperthreading: types.HyperthreadingEnabled,
   855  					Architecture:   types.ArchitectureAMD64,
   856  				},
   857  				Compute: []types.MachinePool{
   858  					{
   859  						Name:           "worker",
   860  						Replicas:       pointer.Int64(0),
   861  						Hyperthreading: types.HyperthreadingEnabled,
   862  						Architecture:   types.ArchitectureAMD64,
   863  					},
   864  				},
   865  				Platform: types.Platform{
   866  					VSphere: &vsphere.Platform{
   867  						DeprecatedVCenter:          "192.168.122.30",
   868  						DeprecatedUsername:         "testUsername",
   869  						DeprecatedPassword:         "testPassword",
   870  						DeprecatedDatacenter:       "testDataCenter",
   871  						DeprecatedCluster:          "testCluster",
   872  						DeprecatedDefaultDatastore: "testDefaultDataStore",
   873  						DeprecatedFolder:           "testFolder",
   874  						DeprecatedAPIVIP:           "192.168.122.10",
   875  						APIVIPs:                    []string{"192.168.122.10"},
   876  						IngressVIPs:                []string{"192.168.122.11"},
   877  						VCenters: []vsphere.VCenter{{
   878  							Server:      "192.168.122.30",
   879  							Port:        443,
   880  							Username:    "testUsername",
   881  							Password:    "testPassword",
   882  							Datacenters: []string{"testDataCenter"},
   883  						}},
   884  						FailureDomains: []vsphere.FailureDomain{{
   885  							Name:   "generated-failure-domain",
   886  							Region: "generated-region",
   887  							Zone:   "generated-zone",
   888  							Server: "192.168.122.30",
   889  							Topology: vsphere.Topology{
   890  								Datacenter:     "testDataCenter",
   891  								ComputeCluster: "/testDataCenter/host/testCluster",
   892  								Networks:       []string{""},
   893  								Datastore:      "/testDataCenter/datastore/testDefaultDataStore",
   894  								ResourcePool:   "/testDataCenter/host/testCluster/Resources",
   895  								Folder:         "/testDataCenter/vm/testFolder",
   896  							},
   897  						}},
   898  					},
   899  				},
   900  				PullSecret: `{"auths":{"example.com":{"auth":"c3VwZXItc2VjcmV0Cg=="}}}`,
   901  				Publish:    types.ExternalPublishingStrategy,
   902  			},
   903  		},
   904  		{
   905  			name: "provisioningNetwork invalid for baremetal cluster",
   906  			data: `
   907  apiVersion: v1
   908  metadata:
   909    name: test-cluster
   910  baseDomain: test-domain
   911  networking:
   912    clusterNetwork:
   913    - cidr: 10.128.0.0/14
   914      hostPrefix: 23
   915    networkType: OVNKubernetes
   916    machineNetwork:
   917    - cidr: 192.168.122.0/23
   918    serviceNetwork:
   919    - 172.30.0.0/16
   920  compute:
   921    - architecture: amd64
   922      name: worker
   923      replicas: 0
   924  controlPlane:
   925    architecture: amd64
   926    name: master
   927    replicas: 3
   928  platform:
   929    baremetal:
   930      provisioningNetwork: "UNMANAGED"
   931      ingressVIPs:
   932        - 192.168.122.11
   933      apiVIPs:
   934        - 192.168.122.10
   935  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   936  `,
   937  			expectedFound: false,
   938  			expectedError: "invalid install-config configuration: platform.baremetal.provisioningNetwork: Unsupported value: \"UNMANAGED\": supported values: \"Disabled\", \"Managed\", \"Unmanaged\"",
   939  		},
   940  		{
   941  			name: "Provisioning validation failures for baremetal cluster",
   942  			data: `
   943  apiVersion: v1
   944  metadata:
   945    name: test-cluster
   946  baseDomain: test-domain
   947  networking:
   948    clusterNetwork:
   949    - cidr: 10.128.0.0/14
   950      hostPrefix: 23
   951    networkType: OVNKubernetes
   952    machineNetwork:
   953    - cidr: 192.168.122.0/23
   954    serviceNetwork:
   955    - 172.30.0.0/16
   956  compute:
   957    - architecture: amd64
   958      name: worker
   959      replicas: 0
   960  controlPlane:
   961    architecture: amd64
   962    name: master
   963    replicas: 3
   964  platform:
   965    baremetal:
   966      ingressVIPs:
   967        - 192.168.122.11
   968      apiVIPs:
   969        - 192.168.122.10
   970      clusterProvisioningIP: "172.22.0.11"
   971      provisioningNetwork: "Managed"
   972      provisioningMACAddress: "52:54:00:6e:3b:02"
   973      provisioningNetworkInterface: "eth11"
   974      provisioningDHCPExternal: true
   975      provisioningDHCPRange: 172.22.0.10,172.22.0.254
   976      hosts:
   977        - name: host1
   978          bootMACAddress: 52:54:01:aa:aa:a1
   979          bmc:
   980            username: "admin"
   981            password: "password"
   982            address: "redfish+http://10.10.10.1:8000/redfish/v1/Systems/1234"
   983        - name: host2
   984          bootMACAddress: 52:54:01:bb:bb:b1
   985          bmc:
   986            username: "admin"
   987            password: "password"
   988            address: "redfish+http://10.10.10.2:8000/redfish/v1/Systems/1234"
   989        - name: host3
   990          bootMACAddress: 52:54:01:cc:cc:c1
   991          bmc:
   992            username: "admin"
   993            password: "password"
   994            address: "redfish+http://10.10.10.2:8000/redfish/v1/Systems/1234"
   995  pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"c3VwZXItc2VjcmV0Cg==\"}}}"
   996  `,
   997  			expectedFound: false,
   998  			expectedError: `invalid install-config configuration: [Platform.BareMetal.clusterProvisioningIP: Invalid value: "172.22.0.11": "172.22.0.11" overlaps with the allocated DHCP range, Platform.BareMetal.hosts[2].BMC.Address: Duplicate value: "redfish+http://10.10.10.2:8000/redfish/v1/Systems/1234"]`,
   999  		},
  1000  	}
  1001  	for _, tc := range cases {
  1002  		t.Run(tc.name, func(t *testing.T) {
  1003  			mockCtrl := gomock.NewController(t)
  1004  			defer mockCtrl.Finish()
  1005  
  1006  			fileFetcher := mock.NewMockFileFetcher(mockCtrl)
  1007  			fileFetcher.EXPECT().FetchByName(InstallConfigFilename).
  1008  				Return(
  1009  					&asset.File{
  1010  						Filename: InstallConfigFilename,
  1011  						Data:     []byte(tc.data)},
  1012  					tc.fetchError,
  1013  				).MaxTimes(2)
  1014  
  1015  			asset := &OptionalInstallConfig{}
  1016  			found, err := asset.Load(fileFetcher)
  1017  			assert.Equal(t, tc.expectedFound, found, "unexpected found value returned from Load")
  1018  			if tc.expectedError != "" {
  1019  				assert.Equal(t, tc.expectedError, err.Error())
  1020  			} else {
  1021  				assert.NoError(t, err)
  1022  			}
  1023  			if tc.expectedFound {
  1024  				assert.Equal(t, tc.expectedConfig, asset.Config, "unexpected Config in InstallConfig")
  1025  			}
  1026  		})
  1027  	}
  1028  }