diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_validation.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_validation.go index 4ce96da420..c2c9b5fac3 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_validation.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_validation.go @@ -56,7 +56,7 @@ var ( ) // NetworkDeviceCheck defines the function type for checks. -type NetworkDeviceCheck func(*Device) error +type NetworkDeviceCheck func(*Device, map[string]string) error // Validate implements the config.Provider interface. //nolint:gocyclo,cyclop @@ -125,8 +125,22 @@ func (c *Config) Validate(mode config.RuntimeMode, options ...config.ValidationO } if c.MachineConfig.MachineNetwork != nil { + bondedInterfaces := map[string]string{} + for _, device := range c.MachineConfig.MachineNetwork.NetworkInterfaces { - if err := ValidateNetworkDevices(device, CheckDeviceInterface, CheckDeviceAddressing, CheckDeviceRoutes); err != nil { + if device.Bond() != nil { + for _, iface := range device.Bond().Interfaces() { + if otherIface, exists := bondedInterfaces[iface]; exists && otherIface != device.Interface() { + result = multierror.Append(result, fmt.Errorf("interface %q is declared as part of two bonds: %q and %q", iface, otherIface, device.Interface())) + } + + bondedInterfaces[iface] = device.Interface() + } + } + } + + for _, device := range c.MachineConfig.MachineNetwork.NetworkInterfaces { + if err := ValidateNetworkDevices(device, bondedInterfaces, CheckDeviceInterface, CheckDeviceAddressing, CheckDeviceRoutes); err != nil { result = multierror.Append(result, err) } } @@ -281,7 +295,7 @@ func (manifests ClusterInlineManifests) Validate() error { // ValidateNetworkDevices runs the specified validation checks specific to the // network devices. -func ValidateNetworkDevices(d *Device, checks ...NetworkDeviceCheck) error { +func ValidateNetworkDevices(d *Device, bondedInterfaces map[string]string, checks ...NetworkDeviceCheck) error { var result *multierror.Error if d == nil { @@ -293,14 +307,14 @@ func ValidateNetworkDevices(d *Device, checks ...NetworkDeviceCheck) error { } for _, check := range checks { - result = multierror.Append(result, check(d)) + result = multierror.Append(result, check(d, bondedInterfaces)) } return result.ErrorOrNil() } // CheckDeviceInterface ensures that the interface has been specified. -func CheckDeviceInterface(d *Device) error { +func CheckDeviceInterface(d *Device, bondedInterfaces map[string]string) error { var result *multierror.Error if d == nil { @@ -471,13 +485,21 @@ func checkWireguard(b *DeviceWireguardConfig) error { // CheckDeviceAddressing ensures that an appropriate addressing method. // has been specified. -func CheckDeviceAddressing(d *Device) error { +// +//nolint:gocyclo +func CheckDeviceAddressing(d *Device, bondedInterfaces map[string]string) error { var result *multierror.Error if d == nil { return fmt.Errorf("empty device") } + if _, bonded := bondedInterfaces[d.Interface()]; bonded { + if d.DeviceDHCP || d.DeviceCIDR != "" || d.DeviceVIPConfig != nil { + result = multierror.Append(result, fmt.Errorf("[%s] %q: %s", "networking.os.device", d.DeviceInterface, "bonded interface shouldn't have any addressing methods configured")) + } + } + // Test for both dhcp and cidr specified if d.DeviceDHCP && d.DeviceCIDR != "" { result = multierror.Append(result, fmt.Errorf("[%s] %q: %w", "networking.os.device", d.DeviceInterface, ErrBadAddressing)) @@ -501,7 +523,7 @@ func CheckDeviceAddressing(d *Device) error { } // CheckDeviceRoutes ensures that the specified routes are valid. -func CheckDeviceRoutes(d *Device) error { +func CheckDeviceRoutes(d *Device, bondedInterfaces map[string]string) error { var result *multierror.Error if d == nil { diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_validation_test.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_validation_test.go index f194f8fbbe..b0e93be0cf 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_validation_test.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_validation_test.go @@ -313,6 +313,10 @@ func TestValidate(t *testing.T) { BondUpDelay: 100, BondPacketsPerSlave: 8, BondADActorSysPrio: 48, + BondInterfaces: []string{ + "eth0", + "eth1", + }, }, }, }, @@ -328,6 +332,97 @@ func TestValidate(t *testing.T) { }, expectedError: "3 errors occurred:\n\t* invalid bond type roundrobin\n\t* bond.upDelay can't be set if miiMon is zero\n\t* bond.adActorSysPrio is only available in 802.3ad mode\n\n", }, + { + name: "BondDoubleBond", + config: &v1alpha1.Config{ + ConfigVersion: "v1alpha1", + MachineConfig: &v1alpha1.MachineConfig{ + MachineType: "controlplane", + MachineNetwork: &v1alpha1.NetworkConfig{ + NetworkInterfaces: []*v1alpha1.Device{ + { + DeviceInterface: "bond0", + DeviceBond: &v1alpha1.Bond{ + BondInterfaces: []string{ + "eth0", + "eth1", + }, + }, + }, + { + DeviceInterface: "bond1", + DeviceBond: &v1alpha1.Bond{ + BondInterfaces: []string{ + "eth1", + "eth2", + }, + }, + }, + }, + }, + }, + ClusterConfig: &v1alpha1.ClusterConfig{ + ControlPlane: &v1alpha1.ControlPlaneConfig{ + Endpoint: &v1alpha1.Endpoint{ + endpointURL, + }, + }, + }, + }, + expectedError: "1 error occurred:\n\t* interface \"eth1\" is declared as part of two bonds: \"bond0\" and \"bond1\"\n\n", + }, + { + name: "BondSlaveAddressing", + config: &v1alpha1.Config{ + ConfigVersion: "v1alpha1", + MachineConfig: &v1alpha1.MachineConfig{ + MachineType: "controlplane", + MachineNetwork: &v1alpha1.NetworkConfig{ + NetworkInterfaces: []*v1alpha1.Device{ + { + DeviceInterface: "bond0", + DeviceBond: &v1alpha1.Bond{ + BondInterfaces: []string{ + "eth0", + "eth1", + }, + }, + }, + { + DeviceInterface: "bond0", + DeviceBond: &v1alpha1.Bond{ + BondInterfaces: []string{ + "eth0", + "eth1", + }, + }, + }, + { + DeviceInterface: "eth0", + DeviceDHCP: true, + }, + { + DeviceInterface: "eth1", + DeviceCIDR: "192.168.0.1/24", + }, + { + DeviceInterface: "eth2", + DeviceCIDR: "192.168.1.1/24", + }, + }, + }, + }, + ClusterConfig: &v1alpha1.ClusterConfig{ + ControlPlane: &v1alpha1.ControlPlaneConfig{ + Endpoint: &v1alpha1.Endpoint{ + endpointURL, + }, + }, + }, + }, + expectedError: "2 errors occurred:\n\t* [networking.os.device] \"eth0\": bonded interface shouldn't have any addressing methods configured\n" + + "\t* [networking.os.device] \"eth1\": bonded interface shouldn't have any addressing methods configured\n\n", + }, { name: "Wireguard", config: &v1alpha1.Config{