diff --git a/go.mod b/go.mod index ee5bd2c9c18..f25b44ba95c 100644 --- a/go.mod +++ b/go.mod @@ -48,6 +48,7 @@ require ( github.com/go-openapi/runtime v0.28.0 github.com/go-openapi/strfmt v0.23.0 github.com/go-openapi/swag v0.23.0 + github.com/go-openapi/validate v0.24.0 github.com/go-playground/validator/v10 v10.19.0 github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang/mock v1.7.0-rc.1 @@ -185,7 +186,6 @@ require ( github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/loads v0.22.0 // indirect github.com/go-openapi/spec v0.21.0 // indirect - github.com/go-openapi/validate v0.24.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/gobuffalo/flect v1.0.2 // indirect diff --git a/pkg/asset/agent/manifests/nmstateconfig_test.go b/pkg/asset/agent/manifests/nmstateconfig_test.go index 252351f19bf..8ad10c7330d 100644 --- a/pkg/asset/agent/manifests/nmstateconfig_test.go +++ b/pkg/asset/agent/manifests/nmstateconfig_test.go @@ -284,6 +284,30 @@ func TestNMStateConfig_Generate(t *testing.T) { expectedConfig: nil, expectedError: "failed to validate network yaml", }, + { + name: "invalid networkConfig, no interfaces in interface table", + dependencies: []asset.Asset{ + &workflow.AgentWorkflow{Workflow: workflow.AgentWorkflowTypeInstall}, + &joiner.ClusterInfo{}, + getAgentHostsConfigNoInterfaces(), + getValidOptionalInstallConfig(), + }, + requiresNmstatectl: true, + expectedConfig: nil, + expectedError: "at least one interface for host 0 must be provided", + }, + { + name: "invalid networkConfig, invalid mac in interface table", + dependencies: []asset.Asset{ + &workflow.AgentWorkflow{Workflow: workflow.AgentWorkflowTypeInstall}, + &joiner.ClusterInfo{}, + getAgentHostsConfigInvalidMac(), + getValidOptionalInstallConfig(), + }, + requiresNmstatectl: true, + expectedConfig: nil, + expectedError: "MAC address 98-af-65-a5-8d-02 for host 0 is incorrectly formatted, use XX:XX:XX:XX:XX:XX format", + }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { diff --git a/pkg/asset/agent/manifests/staticnetworkconfig/generator.go b/pkg/asset/agent/manifests/staticnetworkconfig/generator.go index 4aa69f956a3..e3cc6a71b62 100644 --- a/pkg/asset/agent/manifests/staticnetworkconfig/generator.go +++ b/pkg/asset/agent/manifests/staticnetworkconfig/generator.go @@ -10,6 +10,7 @@ import ( "sort" "strings" + "github.com/go-openapi/validate" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -61,7 +62,7 @@ func (s *staticNetworkConfigGenerator) GenerateStaticNetworkConfigData(ctx conte s.log.WithError(err).Errorf("Failed to decode static network config") return nil, err } - s.log.Infof("Start configuring static network for %d hosts", len(staticNetworkConfig)) + s.log.Warnf("Start configuring static network for %d hosts", len(staticNetworkConfig)) filesList := []StaticNetworkConfigData{} for i, hostConfig := range staticNetworkConfig { hostFileList, err := s.generateHostStaticNetworkConfigData(ctx, hostConfig, fmt.Sprintf("host%d", i)) @@ -218,8 +219,9 @@ func (s *staticNetworkConfigGenerator) formatNMConnection(nmConnection string) ( // ValidateStaticConfigParams validates the NMState data in a HostStaticNetworkConfig. func (s *staticNetworkConfigGenerator) ValidateStaticConfigParams(ctx context.Context, staticNetworkConfig []*models.HostStaticNetworkConfig) error { var err *multierror.Error + s.log.Warnf("Validating StaticConfigParams") for i, hostConfig := range staticNetworkConfig { - err = multierror.Append(err, s.validateMacInterfaceName(i, hostConfig.MacInterfaceMap)) + err = multierror.Append(err, s.validateMacInterfaceTable(i, hostConfig.MacInterfaceMap)) if validateErr := s.ValidateNMStateYaml(ctx, hostConfig.NetworkYaml); validateErr != nil { err = multierror.Append(err, fmt.Errorf("failed to validate network yaml for host %d, %w", i, validateErr)) } @@ -227,12 +229,18 @@ func (s *staticNetworkConfigGenerator) ValidateStaticConfigParams(ctx context.Co return err.ErrorOrNil() } -func (s *staticNetworkConfigGenerator) validateMacInterfaceName(hostIdx int, macInterfaceMap models.MacInterfaceMap) error { +func (s *staticNetworkConfigGenerator) validateMacInterfaceTable(hostIdx int, macInterfaceMap models.MacInterfaceMap) error { + if len(macInterfaceMap) == 0 { + return fmt.Errorf("at least one interface for host %d must be provided", hostIdx) + } interfaceCheck := make(map[string]struct{}, len(macInterfaceMap)) macCheck := make(map[string]struct{}, len(macInterfaceMap)) for _, macInterface := range macInterfaceMap { interfaceCheck[macInterface.LogicalNicName] = struct{}{} macCheck[macInterface.MacAddress] = struct{}{} + if err := validate.Pattern("mac_address", "body", macInterface.MacAddress, `^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$`); err != nil { + return fmt.Errorf("MAC address %s for host %d is incorrectly formatted, use XX:XX:XX:XX:XX:XX format", macInterface.MacAddress, hostIdx) + } } if len(interfaceCheck) < len(macInterfaceMap) || len(macCheck) < len(macInterfaceMap) { return fmt.Errorf("MACs and Interfaces for host %d must be unique", hostIdx) diff --git a/pkg/asset/agent/manifests/util_test.go b/pkg/asset/agent/manifests/util_test.go index bed168a9faf..1fadafc8bc7 100644 --- a/pkg/asset/agent/manifests/util_test.go +++ b/pkg/asset/agent/manifests/util_test.go @@ -450,6 +450,39 @@ func getAgentHostsWithBMCConfig() *agentconfig.AgentHosts { } } +func getAgentHostsConfigNoInterfaces() *agentconfig.AgentHosts { + return &agentconfig.AgentHosts{ + Hosts: []agenttypes.Host{ + { + Hostname: "control-0.example.org", + Interfaces: []*v1beta1.Interface{}, + NetworkConfig: v1beta1.NetConfig{ + Raw: unmarshalJSON([]byte(rawNMStateConfig)), + }, + }, + }, + } +} + +func getAgentHostsConfigInvalidMac() *agentconfig.AgentHosts { + return &agentconfig.AgentHosts{ + Hosts: []agenttypes.Host{ + { + Hostname: "control-0.example.org", + Interfaces: []*v1beta1.Interface{ + { + Name: "enp2s0", + MacAddress: "98-af-65-a5-8d-02", + }, + }, + NetworkConfig: v1beta1.NetConfig{ + Raw: unmarshalJSON([]byte(rawNMStateConfig)), + }, + }, + }, + } +} + func getGoodACI() *hiveext.AgentClusterInstall { goodACI := &hiveext.AgentClusterInstall{ TypeMeta: metav1.TypeMeta{