diff --git a/pkg/asset/installconfig/baremetal/OWNERS b/pkg/asset/installconfig/baremetal/OWNERS new file mode 100644 index 00000000000..51e36956992 --- /dev/null +++ b/pkg/asset/installconfig/baremetal/OWNERS @@ -0,0 +1,7 @@ +# See the OWNERS docs: https://git.k8s.io/community/contributors/guide/owners.md +# This file just uses aliases defined in OWNERS_ALIASES. + +approvers: + - baremetal-approvers +reviewers: + - baremetal-reviewers diff --git a/pkg/asset/installconfig/baremetal/validation.go b/pkg/asset/installconfig/baremetal/validation.go new file mode 100644 index 00000000000..2dbf0708008 --- /dev/null +++ b/pkg/asset/installconfig/baremetal/validation.go @@ -0,0 +1,22 @@ +package baremetal + +import ( + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/openshift/installer/pkg/types" + "github.com/openshift/installer/pkg/types/baremetal/validation" +) + +// ValidateProvisioning performs platform validation specifically for any optional requirement +// to be called when the cluster creation takes place +func ValidateProvisioning(ic *types.InstallConfig) error { + allErrs := field.ErrorList{} + if ic.Platform.BareMetal == nil { + return errors.New(field.Required(field.NewPath("platform", "baremetal"), "Baremetal validation requires a baremetal platform configuration").Error()) + } + + allErrs = append(allErrs, validation.ValidateProvisioning(ic.Platform.BareMetal, ic.Networking, field.NewPath("platform").Child("baremetal"))...) + + return allErrs.ToAggregate() +} diff --git a/pkg/asset/installconfig/platformprovisioncheck.go b/pkg/asset/installconfig/platformprovisioncheck.go index 47653cb7cf1..1a311a87b77 100644 --- a/pkg/asset/installconfig/platformprovisioncheck.go +++ b/pkg/asset/installconfig/platformprovisioncheck.go @@ -6,6 +6,7 @@ import ( "github.com/openshift/installer/pkg/asset" azconfig "github.com/openshift/installer/pkg/asset/installconfig/azure" + bmconfig "github.com/openshift/installer/pkg/asset/installconfig/baremetal" gcpconfig "github.com/openshift/installer/pkg/asset/installconfig/gcp" vsconfig "github.com/openshift/installer/pkg/asset/installconfig/vsphere" "github.com/openshift/installer/pkg/types/aws" @@ -41,11 +42,6 @@ func (a *PlatformProvisionCheck) Generate(dependencies asset.Parents) error { var err error platform := ic.Config.Platform.Name() switch platform { - case vsphere.Name: - err = vsconfig.ValidateForProvisioning(ic.Config) - if err != nil { - return err - } case azure.Name: dnsConfig, err := ic.Azure.DNSConfig() if err != nil { @@ -55,6 +51,11 @@ func (a *PlatformProvisionCheck) Generate(dependencies asset.Parents) error { if err != nil { return err } + case baremetal.Name: + err = bmconfig.ValidateProvisioning(ic.Config) + if err != nil { + return err + } case gcp.Name: client, err := gcpconfig.NewClient(context.TODO()) if err != nil { @@ -64,7 +65,12 @@ func (a *PlatformProvisionCheck) Generate(dependencies asset.Parents) error { if err != nil { return err } - case aws.Name, baremetal.Name, libvirt.Name, none.Name, openstack.Name, ovirt.Name: + case vsphere.Name: + err = vsconfig.ValidateForProvisioning(ic.Config) + if err != nil { + return err + } + case aws.Name, libvirt.Name, none.Name, openstack.Name, ovirt.Name: // no special provisioning requirements to check default: err = fmt.Errorf("unknown platform type %q", platform) diff --git a/pkg/types/baremetal/validation/libvirt.go b/pkg/types/baremetal/validation/libvirt.go index f7ec84eb581..4e0824951a9 100644 --- a/pkg/types/baremetal/validation/libvirt.go +++ b/pkg/types/baremetal/validation/libvirt.go @@ -12,7 +12,7 @@ import ( ) func init() { - dynamicValidators = append(dynamicValidators, validateInterfaces) + dynamicProvisioningValidators = append(dynamicProvisioningValidators, validateInterfaces) } // validateInterfaces ensures that any interfaces required by the platform exist on the libvirt host. diff --git a/pkg/types/baremetal/validation/platform.go b/pkg/types/baremetal/validation/platform.go index 1240551e94c..e5657780234 100644 --- a/pkg/types/baremetal/validation/platform.go +++ b/pkg/types/baremetal/validation/platform.go @@ -1,6 +1,7 @@ package validation import ( + "bytes" "fmt" "net" "net/http" @@ -19,13 +20,13 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" ) -// dynamicValidator is a function that validates certain fields in the platform. -type dynamicValidator func(*baremetal.Platform, *field.Path) field.ErrorList +// dynamicProvisioningValidator is a function that validates certain fields in the platform. +type dynamicProvisioningValidator func(*baremetal.Platform, *field.Path) field.ErrorList -// dynamicValidators is an array of dynamicValidator functions. This array can be added to by an init function, and +// dynamicProvisioningValidators is an array of dynamicProvisioningValidator functions. This array can be added to by an init function, and // is intended to be used for validations that require dependencies not built with the default tags, e.g. libvirt // libraries. -var dynamicValidators []dynamicValidator +var dynamicProvisioningValidators []dynamicProvisioningValidator func validateIPinMachineCIDR(vip string, n *types.Networking) error { for _, network := range n.MachineNetwork { @@ -106,8 +107,8 @@ func validateOSImageURI(uri string) error { return nil } -// validateHosts checks that hosts have all required fields set with appropriate values -func validateHosts(hosts []*baremetal.Host, fldPath *field.Path) field.ErrorList { +// validateHostsBase validates the hosts based on a filtering function +func validateHostsBase(hosts []*baremetal.Host, fldPath *field.Path, filter validator.FilterFunc) field.ErrorList { hostErrs := field.ErrorList{} values := make(map[string]map[interface{}]struct{}) @@ -139,7 +140,7 @@ func validateHosts(hosts []*baremetal.Host, fldPath *field.Path) field.ErrorList fldPath = fldPath.Child("hosts") for idx, host := range hosts { - err := validate.Struct(host) + err := validate.StructFiltered(host, filter) if err != nil { hostType := reflect.TypeOf(hosts).Elem().Elem().Name() for _, err := range err.(validator.ValidationErrors) { @@ -157,6 +158,23 @@ func validateHosts(hosts []*baremetal.Host, fldPath *field.Path) field.ErrorList return hostErrs } +// filterHostsBMC is a function to control whether to filter BMC details of Hosts +func filterHostsBMC(ns []byte) bool { + return bytes.Contains(ns, []byte(".BMC")) +} + +// validateHostsWithoutBMC utilizes the filter function to disable BMC checking while validating hosts +func validateHostsWithoutBMC(hosts []*baremetal.Host, fldPath *field.Path) field.ErrorList { + return validateHostsBase(hosts, fldPath, filterHostsBMC) +} + +// validateHostsBMCOnly utilizes the filter function to only perform validation on BMC part of the hosts +func validateHostsBMCOnly(hosts []*baremetal.Host, fldPath *field.Path) field.ErrorList { + return validateHostsBase(hosts, fldPath, func(ns []byte) bool { + return !filterHostsBMC(ns) + }) +} + func validateOSImages(p *baremetal.Platform, fldPath *field.Path) field.ErrorList { platformErrs := field.ErrorList{} @@ -217,17 +235,53 @@ func validateHostsCount(hosts []*baremetal.Host, installConfig *types.InstallCon // ValidatePlatform checks that the specified platform is valid. func ValidatePlatform(p *baremetal.Platform, n *types.Networking, fldPath *field.Path, c *types.InstallConfig) field.ErrorList { allErrs := field.ErrorList{} - if err := validate.URI(p.LibvirtURI); err != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("libvirtURI"), p.LibvirtURI, err.Error())) - } - if err := validate.IP(p.ClusterProvisioningIP); err != nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("provisioningHostIP"), p.ClusterProvisioningIP, err.Error())) } + if p.Hosts == nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("hosts"), p.Hosts, "bare metal hosts are missing")) + } + + if p.DefaultMachinePlatform != nil { + allErrs = append(allErrs, ValidateMachinePool(p.DefaultMachinePlatform, fldPath.Child("defaultMachinePlatform"))...) + } + + if err := validate.IP(p.APIVIP); err != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("apiVIP"), p.APIVIP, err.Error())) + } + + if err := validateIPinMachineCIDR(p.APIVIP, n); err != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("apiVIP"), p.APIVIP, err.Error())) + } + + if err := validate.IP(p.IngressVIP); err != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("ingressVIP"), p.IngressVIP, err.Error())) + } + + if err := validateIPinMachineCIDR(p.IngressVIP, n); err != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("ingressVIP"), p.IngressVIP, err.Error())) + } + + if err := validateHostsCount(p.Hosts, c); err != nil { + allErrs = append(allErrs, field.Required(fldPath.Child("Hosts"), err.Error())) + } + + allErrs = append(allErrs, validateHostsWithoutBMC(p.Hosts, fldPath)...) + + return allErrs +} + +// ValidateProvisioning checks that provisioning network requirements specified is valid. +func ValidateProvisioning(p *baremetal.Platform, n *types.Networking, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + if err := validate.IP(p.BootstrapProvisioningIP); err != nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("bootstrapProvisioningIP"), p.BootstrapProvisioningIP, err.Error())) } + if err := validateIPNotinMachineCIDR(p.ClusterProvisioningIP, n); err != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("provisioningHostIP"), p.ClusterProvisioningIP, err.Error())) + } if p.ProvisioningNetworkCIDR != nil { // Ensure provisioningNetworkCIDR doesn't overlap with any machine network @@ -265,51 +319,24 @@ func ValidatePlatform(p *baremetal.Platform, n *types.Networking, fldPath *field } } + if err := validateIPNotinMachineCIDR(p.BootstrapProvisioningIP, n); err != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("bootstrapHostIP"), p.BootstrapProvisioningIP, err.Error())) + } + // Make sure the provisioning interface is set. Very little we can do to validate this as it's not on this machine. if p.ProvisioningNetworkInterface == "" { allErrs = append(allErrs, field.Invalid(fldPath.Child("provisioningNetworkInterface"), p.ProvisioningNetworkInterface, "no provisioning network interface is configured, please set this value to be the interface on the provisioning network on your cluster's baremetal hosts")) } - if p.Hosts == nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("hosts"), p.Hosts, "bare metal hosts are missing")) - } - - if p.DefaultMachinePlatform != nil { - allErrs = append(allErrs, ValidateMachinePool(p.DefaultMachinePlatform, fldPath.Child("defaultMachinePlatform"))...) - } - - if err := validate.IP(p.APIVIP); err != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("apiVIP"), p.APIVIP, err.Error())) - } - - if err := validateIPinMachineCIDR(p.APIVIP, n); err != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("apiVIP"), p.APIVIP, err.Error())) - } - - if err := validate.IP(p.IngressVIP); err != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("ingressVIP"), p.IngressVIP, err.Error())) - } - - if err := validateIPinMachineCIDR(p.IngressVIP, n); err != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("ingressVIP"), p.IngressVIP, err.Error())) - } - - if err := validateIPNotinMachineCIDR(p.ClusterProvisioningIP, n); err != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("provisioningHostIP"), p.ClusterProvisioningIP, err.Error())) - } - if err := validateIPNotinMachineCIDR(p.BootstrapProvisioningIP, n); err != nil { - allErrs = append(allErrs, field.Invalid(fldPath.Child("bootstrapHostIP"), p.BootstrapProvisioningIP, err.Error())) - } - - if err := validateHostsCount(p.Hosts, c); err != nil { - allErrs = append(allErrs, field.Required(fldPath.Child("Hosts"), err.Error())) + if err := validate.URI(p.LibvirtURI); err != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("libvirtURI"), p.LibvirtURI, err.Error())) } allErrs = append(allErrs, validateOSImages(p, fldPath)...) - allErrs = append(allErrs, validateHosts(p.Hosts, fldPath)...) + allErrs = append(allErrs, validateHostsBMCOnly(p.Hosts, fldPath)...) - for _, validator := range dynamicValidators { + for _, validator := range dynamicProvisioningValidators { allErrs = append(allErrs, validator(p, fldPath)...) } diff --git a/pkg/types/baremetal/validation/platform_test.go b/pkg/types/baremetal/validation/platform_test.go index 7c4bdbe2c5e..ad27e19391b 100644 --- a/pkg/types/baremetal/validation/platform_test.go +++ b/pkg/types/baremetal/validation/platform_test.go @@ -23,32 +23,6 @@ using the fluent interface by chaining the exposed functions accordingly. */ func TestValidatePlatform(t *testing.T) { - interfaceValidator := func(p *baremetal.Platform, fldPath *field.Path) field.ErrorList { - errorList := field.ErrorList{} - - if p.ExternalBridge != "br0" { - errorList = append(errorList, field.Invalid(fldPath.Child("externalBridge"), p.ExternalBridge, - "invalid external bridge")) - } - - if p.ProvisioningBridge != "br1" { - errorList = append(errorList, field.Invalid(fldPath.Child("provisioningBridge"), p.ProvisioningBridge, - "invalid provisioning bridge")) - } - - return errorList - } - dynamicValidators = append(dynamicValidators, interfaceValidator) - - //Used for url validations - imagesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/gzip") - if strings.Contains(r.RequestURI, "notexistent") { - http.NotFound(w, r) - } - })) - defer imagesServer.Close() - cases := []struct { name string config *types.InstallConfig @@ -66,35 +40,6 @@ func TestValidatePlatform(t *testing.T) { ClusterProvisioningIP("fd2e:6f44:5dd8:b856::3"). BootstrapProvisioningIP("fd2e:6f44:5dd8:b856::2").build(), }, - { - name: "valid_with_os_image_overrides", - platform: platform(). - BootstrapOSImage(imagesServer.URL + "/images/qemu.x86_64.qcow2.gz?sha256=3b5a882c2af3e19d515b961855d144f293cab30190c2bdedd661af31a1fc4e2f"). - ClusterOSImage(imagesServer.URL + "/images/metal.x86_64.qcow2.gz?sha256=340dfa4d92450f2eee852ed1e2d02e3138cc68d824827ef9cf0a40a7ea2f93da").build(), - }, - { - name: "valid_provisioningDHCPRange", - platform: platform(). - ProvisioningDHCPRange("172.22.0.10,172.22.0.50").build(), - }, - { - name: "invalid_provisioningDHCPRange_missing_pair", - platform: platform(). - ProvisioningDHCPRange("172.22.0.10,").build(), - expected: "provisioningDHCPRange: Invalid value: \"172.22.0.10,\": : \"\" is not a valid IP", - }, - { - name: "invalid_provisioningDHCPRange_not_a_range", - platform: platform(). - ProvisioningDHCPRange("172.22.0.19").build(), - expected: "Invalid value: \"172.22.0.19\": provisioning dhcp range should be in format: start_ip,end_ip", - }, - { - name: "invalid_provisioningDHCPRange_wrong_CIDR", - platform: platform(). - ProvisioningDHCPRange("192.168.128.1,172.22.0.100").build(), - expected: "Invalid value: \"192.168.128.1,172.22.0.100\": \"192.168.128.1\" is not in the provisioning network", - }, { name: "invalid_apivip", platform: platform(). @@ -114,53 +59,140 @@ func TestValidatePlatform(t *testing.T) { expected: "bare metal hosts are missing", }, { - name: "invalid_libvirturi", + name: "toofew_hosts", + config: installConfig(). + BareMetalPlatform( + platform().Hosts( + host1())). + ControlPlane( + machinePool().Replicas(3)). + Compute( + machinePool().Replicas(2), + machinePool().Replicas(3)).build(), + expected: "baremetal.Hosts: Required value: not enough hosts found \\(1\\) to support all the configured ControlPlane and Compute replicas \\(8\\)", + }, + { + name: "enough_hosts", + config: installConfig(). + BareMetalPlatform( + platform().Hosts( + host1(), + host2())). + ControlPlane( + machinePool().Replicas(2)).build(), + }, + { + name: "missing_name", platform: platform(). - LibvirtURI("").build(), - expected: "invalid URI \"\"", + Hosts(host1().Name("")).build(), + expected: "baremetal.hosts\\[0\\].Name: Required value: missing Name", }, { - name: "invalid_extbridge", + name: "missing_mac", platform: platform(). - ExternalBridge("noexist").build(), - expected: "Invalid value: \"noexist\": invalid external bridge", + Hosts(host1().BootMACAddress("")).build(), + expected: "baremetal.hosts\\[0\\].BootMACAddress: Required value: missing BootMACAddress", }, { - name: "invalid_provbridge", + name: "duplicate_host_name", platform: platform(). - ProvisioningBridge("noexist").build(), - expected: "Invalid value: \"noexist\": invalid provisioning bridge", + Hosts( + host1().Name("host1"), + host2().Name("host1")).build(), + expected: "baremetal.hosts\\[1\\].Name: Duplicate value: \"host1\"", }, { - name: "invalid_provisioning_interface", + name: "duplicate_host_mac", platform: platform(). - ProvisioningNetworkInterface("").build(), - expected: "Invalid value: \"\": no provisioning network interface is configured, please set this value to be the interface on the provisioning network on your cluster's baremetal hosts", + Hosts( + host1().BootMACAddress("CA:FE:CA:FE:CA:FE"), + host2().BootMACAddress("CA:FE:CA:FE:CA:FE")).build(), + expected: "baremetal.hosts\\[1\\].BootMACAddress: Duplicate value: \"CA:FE:CA:FE:CA:FE\"", }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + //Build default wrapping installConfig + if tc.config == nil { + tc.config = installConfig().build() + tc.config.BareMetal = tc.platform + } + + err := ValidatePlatform(tc.config.BareMetal, network(), field.NewPath("baremetal"), tc.config).ToAggregate() + + if tc.expected == "" { + assert.NoError(t, err) + } else { + assert.Regexp(t, tc.expected, err) + } + }) + } +} + +func TestValidateProvisioning(t *testing.T) { + //Used for url validations + imagesServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/gzip") + if strings.Contains(r.RequestURI, "notexistent") { + http.NotFound(w, r) + } + })) + defer imagesServer.Close() + + interfaceValidator := func(p *baremetal.Platform, fldPath *field.Path) field.ErrorList { + errorList := field.ErrorList{} + + if p.ExternalBridge != "br0" { + errorList = append(errorList, field.Invalid(fldPath.Child("externalBridge"), p.ExternalBridge, + "invalid external bridge")) + } + if p.ProvisioningBridge != "br1" { + errorList = append(errorList, field.Invalid(fldPath.Child("provisioningBridge"), p.ProvisioningBridge, + "invalid provisioning bridge")) + } + + return errorList + } + dynamicProvisioningValidators = append(dynamicProvisioningValidators, interfaceValidator) + + cases := []struct { + name string + config *types.InstallConfig + platform *baremetal.Platform + expected string + }{ { - name: "invalid_provisioning_network_overlapping_CIDR", - platform: platform().ProvisioningNetworkCIDR("192.168.111.192/23").build(), - expected: "Invalid value: \"192.168.111.192/23\": cannot overlap with machine network: 192.168.111.0/24 overlaps with 192.168.111.192/23", + name: "duplicate_bmc_address", + platform: platform(). + Hosts( + host1().BMCAddress("ipmi://192.168.111.1"), + host2().BMCAddress("ipmi://192.168.111.1")).build(), + expected: "baremetal.hosts\\[1\\].BMC.Address: Duplicate value: \"ipmi://192.168.111.1\"", }, - { - name: "invalid_clusterprovip_machineCIDR", + name: "bmc_address_required", platform: platform(). - ClusterProvisioningIP("192.168.111.5").build(), - expected: "Invalid value: \"192.168.111.5\": the IP must not be in one of the machine networks", + Hosts(host1().BMCAddress("")).build(), + expected: "baremetal.hosts\\[0\\].BMC.Address: Required value: missing Address", }, { - name: "invalid_clusterprovip_wrongCIDR", + name: "bmc_username_required", platform: platform(). - ClusterProvisioningIP("192.168.128.1").build(), - expected: "Invalid value: \"192.168.128.1\": \"192.168.128.1\" is not in the provisioning network", + Hosts(host1().BMCUsername("")).build(), + expected: "baremetal.hosts\\[0\\].BMC.Username: Required value: missing Username", }, { - name: "invalid_bootstrapprovip_machineCIDR", + name: "bmc_password_required", platform: platform(). - BootstrapProvisioningIP("192.168.111.5").build(), - expected: "Invalid value: \"192.168.111.5\": the IP must not be in one of the machine networks", + Hosts(host1().BMCPassword("")).build(), + expected: "baremetal.hosts\\[0\\].BMC.Password: Required value: missing Password", + }, + { + name: "valid_with_os_image_overrides", + platform: platform(). + BootstrapOSImage(imagesServer.URL + "/images/qemu.x86_64.qcow2.gz?sha256=3b5a882c2af3e19d515b961855d144f293cab30190c2bdedd661af31a1fc4e2f"). + ClusterOSImage(imagesServer.URL + "/images/metal.x86_64.qcow2.gz?sha256=340dfa4d92450f2eee852ed1e2d02e3138cc68d824827ef9cf0a40a7ea2f93da").build(), }, { name: "invalid_bootstraposimage", @@ -210,6 +242,18 @@ func TestValidatePlatform(t *testing.T) { ClusterOSImage(imagesServer.URL + "/images/notexistent.x86_64.qcow2.gz?sha256=3b5a882c2af3e19d515b961855d144f293cab30190c2bdedd661af31a1fc4e2f").build(), expected: "baremetal.ClusterOSImage: Not found:.*", }, + { + name: "invalid_extbridge", + platform: platform(). + ExternalBridge("noexist").build(), + expected: "Invalid value: \"noexist\": invalid external bridge", + }, + { + name: "invalid_provbridge", + platform: platform(). + ProvisioningBridge("noexist").build(), + expected: "Invalid value: \"noexist\": invalid provisioning bridge", + }, { name: "invalid_bootstrapprovip_wrongCIDR", platform: platform(). @@ -217,81 +261,62 @@ func TestValidatePlatform(t *testing.T) { expected: "Invalid value: \"192.168.128.1\": \"192.168.128.1\" is not in the provisioning network", }, { - name: "duplicate_bmc_address", + name: "invalid_bootstrapprovip_machineCIDR", platform: platform(). - Hosts( - host1().BMCAddress("ipmi://192.168.111.1"), - host2().BMCAddress("ipmi://192.168.111.1")).build(), - expected: "baremetal.hosts\\[1\\].BMC.Address: Duplicate value: \"ipmi://192.168.111.1\"", + BootstrapProvisioningIP("192.168.111.5").build(), + expected: "Invalid value: \"192.168.111.5\": the IP must not be in one of the machine networks", }, { - name: "bmc_address_required", + name: "invalid_clusterprovip_machineCIDR", platform: platform(). - Hosts(host1().BMCAddress("")).build(), - expected: "baremetal.hosts\\[0\\].BMC.Address: Required value: missing Address", + ClusterProvisioningIP("192.168.111.5").build(), + expected: "Invalid value: \"192.168.111.5\": the IP must not be in one of the machine networks", }, { - name: "bmc_username_required", + name: "invalid_clusterprovip_wrongCIDR", platform: platform(). - Hosts(host1().BMCUsername("")).build(), - expected: "baremetal.hosts\\[0\\].BMC.Username: Required value: missing Username", + ClusterProvisioningIP("192.168.128.1").build(), + expected: "Invalid value: \"192.168.128.1\": \"192.168.128.1\" is not in the provisioning network", }, { - name: "bmc_password_required", - platform: platform(). - Hosts(host1().BMCPassword("")).build(), - expected: "baremetal.hosts\\[0\\].BMC.Password: Required value: missing Password", + name: "invalid_provisioning_network_overlapping_CIDR", + platform: platform().ProvisioningNetworkCIDR("192.168.111.192/23").build(), + expected: "Invalid value: \"192.168.111.192/23\": cannot overlap with machine network: 192.168.111.0/24 overlaps with 192.168.111.192/23", }, { - name: "duplicate_host_name", + name: "invalid_provisioning_interface", platform: platform(). - Hosts( - host1().Name("host1"), - host2().Name("host1")).build(), - expected: "baremetal.hosts\\[1\\].Name: Duplicate value: \"host1\"", + ProvisioningNetworkInterface("").build(), + expected: "Invalid value: \"\": no provisioning network interface is configured, please set this value to be the interface on the provisioning network on your cluster's baremetal hosts", }, { - name: "duplicate_host_mac", + name: "valid_provisioningDHCPRange", platform: platform(). - Hosts( - host1().BootMACAddress("CA:FE:CA:FE:CA:FE"), - host2().BootMACAddress("CA:FE:CA:FE:CA:FE")).build(), - expected: "baremetal.hosts\\[1\\].BootMACAddress: Duplicate value: \"CA:FE:CA:FE:CA:FE\"", + ProvisioningDHCPRange("172.22.0.10,172.22.0.50").build(), }, { - name: "missing_name", + name: "invalid_provisioningDHCPRange_missing_pair", platform: platform(). - Hosts(host1().Name("")).build(), - expected: "baremetal.hosts\\[0\\].Name: Required value: missing Name", + ProvisioningDHCPRange("172.22.0.10,").build(), + expected: "provisioningDHCPRange: Invalid value: \"172.22.0.10,\": : \"\" is not a valid IP", }, { - name: "missing_mac", + name: "invalid_provisioningDHCPRange_not_a_range", platform: platform(). - Hosts(host1().BootMACAddress("")).build(), - expected: "baremetal.hosts\\[0\\].BootMACAddress: Required value: missing BootMACAddress", + ProvisioningDHCPRange("172.22.0.19").build(), + expected: "Invalid value: \"172.22.0.19\": provisioning dhcp range should be in format: start_ip,end_ip", }, { - name: "toofew_hosts", - config: installConfig(). - BareMetalPlatform( - platform().Hosts( - host1())). - ControlPlane( - machinePool().Replicas(3)). - Compute( - machinePool().Replicas(2), - machinePool().Replicas(3)).build(), - expected: "baremetal.Hosts: Required value: not enough hosts found \\(1\\) to support all the configured ControlPlane and Compute replicas \\(8\\)", + name: "invalid_provisioningDHCPRange_wrong_CIDR", + platform: platform(). + ProvisioningDHCPRange("192.168.128.1,172.22.0.100").build(), + expected: "Invalid value: \"192.168.128.1,172.22.0.100\": \"192.168.128.1\" is not in the provisioning network", }, { - name: "enough_hosts", - config: installConfig(). - BareMetalPlatform( - platform().Hosts( - host1(), - host2())). - ControlPlane( - machinePool().Replicas(2)).build(), + name: "invalid_libvirturi", + platform: platform(). + LibvirtURI("").build(), + expected: "invalid URI \"\"", }, } @@ -303,7 +328,7 @@ func TestValidatePlatform(t *testing.T) { tc.config.BareMetal = tc.platform } - err := ValidatePlatform(tc.config.BareMetal, network(), field.NewPath("baremetal"), tc.config).ToAggregate() + err := ValidateProvisioning(tc.config.BareMetal, network(), field.NewPath("baremetal")).ToAggregate() if tc.expected == "" { assert.NoError(t, err)