config: update validation to work with nodeGroups

This commit is contained in:
Malte Poll 2023-08-02 10:24:25 +02:00 committed by Malte Poll
parent 2246c31b7b
commit b61deb6a03

View File

@ -25,8 +25,10 @@ import (
"github.com/edgelesssys/constellation/v2/internal/attestation/variant" "github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/compatibility" "github.com/edgelesssys/constellation/v2/internal/compatibility"
"github.com/edgelesssys/constellation/v2/internal/config/disktypes"
"github.com/edgelesssys/constellation/v2/internal/config/instancetypes" "github.com/edgelesssys/constellation/v2/internal/config/instancetypes"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/role"
consemver "github.com/edgelesssys/constellation/v2/internal/semver" consemver "github.com/edgelesssys/constellation/v2/internal/semver"
"github.com/edgelesssys/constellation/v2/internal/versions" "github.com/edgelesssys/constellation/v2/internal/versions"
) )
@ -101,20 +103,6 @@ func translateInvalidK8sVersionError(ut ut.Translator, fe validator.FieldError)
return t return t
} }
func (c *Config) validateAWSInstanceType(fl validator.FieldLevel) bool {
acceptNonCVM := c.GetAttestationConfig().GetVariant().Equal(variant.AWSNitroTPM{})
return validInstanceTypeForProvider(fl.Field().String(), acceptNonCVM, cloudprovider.AWS)
}
func (c *Config) validateAzureInstanceType(fl validator.FieldLevel) bool {
acceptNonCVM := c.GetAttestationConfig().GetVariant().Equal(variant.AzureTrustedLaunch{})
return validInstanceTypeForProvider(fl.Field().String(), acceptNonCVM, cloudprovider.Azure)
}
func validateGCPInstanceType(fl validator.FieldLevel) bool {
return validInstanceTypeForProvider(fl.Field().String(), false, cloudprovider.GCP)
}
func validateAWSRegionField(fl validator.FieldLevel) bool { func validateAWSRegionField(fl validator.FieldLevel) bool {
return ValidateAWSRegion(fl.Field().String()) return ValidateAWSRegion(fl.Field().String())
} }
@ -123,12 +111,41 @@ func validateAWSZoneField(fl validator.FieldLevel) bool {
return ValidateAWSZone(fl.Field().String()) return ValidateAWSZone(fl.Field().String())
} }
func validateAzureZoneField(fl validator.FieldLevel) bool {
return ValidateAzureZone(fl.Field().String())
}
func validateGCPZoneField(fl validator.FieldLevel) bool {
return ValidateGCPZone(fl.Field().String())
}
func validateOpenStackRegionField(fl validator.FieldLevel) bool {
return ValidateOpenStackRegion(fl.Field().String())
}
// ValidateAWSZone validates that the zone is in the correct format. // ValidateAWSZone validates that the zone is in the correct format.
func ValidateAWSZone(zone string) bool { func ValidateAWSZone(zone string) bool {
awsZoneRegex := regexp.MustCompile(`^\w+-\w+-[1-9][abc]$`) awsZoneRegex := regexp.MustCompile(`^\w+-\w+-[1-9][abc]$`)
return awsZoneRegex.MatchString(zone) return awsZoneRegex.MatchString(zone)
} }
// ValidateAzureZone validates that the zone is in the correct format.
func ValidateAzureZone(zone string) bool {
azureZoneRegex := regexp.MustCompile(`^$|^\d+(?:,\d+)*$`)
return azureZoneRegex.MatchString(zone)
}
// ValidateGCPZone validates that the zone is in the correct format.
func ValidateGCPZone(zone string) bool {
gcpZoneRegex := regexp.MustCompile(`^[\w-]+-[a-z]$`)
return gcpZoneRegex.MatchString(zone)
}
// ValidateOpenStackRegion validates that the region is in the correct format.
func ValidateOpenStackRegion(region string) bool {
return len(region) > 0
}
// ValidateAWSRegion validates that the region is in the correct format. // ValidateAWSRegion validates that the region is in the correct format.
func ValidateAWSRegion(region string) bool { func ValidateAWSRegion(region string) bool {
awsRegionRegex := regexp.MustCompile(`^\w+-\w+-[1-9]$`) awsRegionRegex := regexp.MustCompile(`^\w+-\w+-[1-9]$`)
@ -193,6 +210,33 @@ func validateAttestation(sl validator.StructLevel) {
} }
} }
func validateNodeGroups(sl validator.StructLevel) {
nodeGroups := sl.Current().Interface().(Config).NodeGroups
defaultControlPlaneGroup, hasDefaultControlPlaneGroup := nodeGroups[constants.DefaultControlPlaneGroupName]
defaultWorkerGroup, hasDefaultWorkerGroup := nodeGroups[constants.DefaultWorkerGroupName]
if !hasDefaultControlPlaneGroup {
sl.ReportError(nodeGroups, "NodeGroups", "NodeGroups", "no_default_control_plane_group", "")
}
if !hasDefaultWorkerGroup {
sl.ReportError(nodeGroups, "NodeGroups", "NodeGroups", "no_default_worker_group", "")
}
if hasDefaultControlPlaneGroup {
if defaultControlPlaneGroup.Role != role.ControlPlane.TFString() {
sl.ReportError(nodeGroups, "NodeGroups", "NodeGroups", "control_plane_group_role_mismatch", "")
}
if defaultControlPlaneGroup.InitialCount < 1 {
sl.ReportError(nodeGroups, "NodeGroups", "NodeGroups", "control_plane_group_initial_count", "")
}
}
if hasDefaultWorkerGroup {
if defaultWorkerGroup.Role != role.Worker.TFString() {
sl.ReportError(nodeGroups, "NodeGroups", "NodeGroups", "worker_group_role_mismatch", "")
}
}
}
func translateNoAttestationError(ut ut.Translator, fe validator.FieldError) string { func translateNoAttestationError(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("no_attestation", fe.Field()) t, _ := ut.T("no_attestation", fe.Field())
@ -203,6 +247,75 @@ func registerNoAttestationError(ut ut.Translator) error {
return ut.Add("no_attestation", "{0}: No attestation has been defined (requires either awsSEVSNP, awsNitroTPM, azureSEVSNP, azureTrustedLaunch, gcpSEVES, or qemuVTPM)", true) return ut.Add("no_attestation", "{0}: No attestation has been defined (requires either awsSEVSNP, awsNitroTPM, azureSEVSNP, azureTrustedLaunch, gcpSEVES, or qemuVTPM)", true)
} }
func translateNoDefaultControlPlaneGroupError(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("no_default_control_plane_group", fe.Field())
return t
}
func registerNoDefaultControlPlaneGroupError(ut ut.Translator) error {
return ut.Add("no_default_control_plane_group", "{0}: No default control plane group (control_plane_default) has been defined", true)
}
func translateNoDefaultWorkerGroupError(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("no_default_worker_group", fe.Field())
return t
}
func registerNoDefaultWorkerGroupError(ut ut.Translator) error {
return ut.Add("no_default_worker_group", "{0}: No default worker group (worker_default) has been defined", true)
}
func translateControlPlaneGroupInitialCountError(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("control_plane_group_initial_count", fe.Field())
return t
}
func registerControlPlaneGroupInitialCountError(ut ut.Translator) error {
return ut.Add("control_plane_group_initial_count", "{0}: The default control plane group (control_plane_default) must have at least one node", true)
}
func translateControlPlaneGroupRoleMismatchError(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("control_plane_group_role_mismatch", fe.Field())
return t
}
func registerControlPlaneGroupRoleMismatchError(ut ut.Translator) error {
return ut.Add("control_plane_group_role_mismatch", "{0}: The default control plane group (control_plane_default) must have the \"control-plane\" role", true)
}
func translateWorkerGroupRoleMismatchError(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("worker_group_role_mismatch", fe.Field())
return t
}
func registerWorkerGroupRoleMismatchError(ut ut.Translator) error {
return ut.Add("worker_group_role_mismatch", "{0}: The default worker group (worker_default) must have the \"worker\" role", true)
}
func registerValidZoneError(ut ut.Translator) error {
return ut.Add("valid_zone", "{0}: has invalid format: {1}", true)
}
func (c *Config) translateValidZoneError(ut ut.Translator, fe validator.FieldError) string {
var t string
switch c.GetProvider() {
case cloudprovider.AWS:
t, _ = ut.T("valid_zone", fe.Field(), "field must be of format eu-central-1a")
case cloudprovider.Azure:
t, _ = ut.T("valid_zone", fe.Field(), "field must be a comma separated list of zones: 1,2,3 (or empty for all zones)")
case cloudprovider.GCP:
t, _ = ut.T("valid_zone", fe.Field(), "field must be of format europe-west3-b")
default:
t, _ = ut.T("valid_zone", fe.Field(), "field must not be empty")
}
return t
}
func registerAWSRegionError(ut ut.Translator) error { func registerAWSRegionError(ut ut.Translator) error {
return ut.Add("aws_region", "{0}: has invalid format: {1}", true) return ut.Add("aws_region", "{0}: has invalid format: {1}", true)
} }
@ -254,8 +367,53 @@ func (c *Config) translateMoreThanOneAttestationError(ut ut.Translator, fe valid
return t return t
} }
func registerTranslateDiskTypeError(ut ut.Translator) error {
return ut.Add("disk_type", "{0} must be one of {1}", true)
}
func (c *Config) translateDiskTypeError(ut ut.Translator, fe validator.FieldError) string {
var supported []string
switch c.GetProvider() {
case cloudprovider.AWS:
supported = disktypes.AWSDiskTypes
case cloudprovider.Azure:
supported = disktypes.AzureDiskTypes
case cloudprovider.GCP:
supported = disktypes.GCPDiskTypes
}
t, _ := ut.T("disk_type", fe.Field(), fmt.Sprintf("%v", supported))
return t
}
func (c *Config) registerTranslateInstanceTypeError(ut ut.Translator) error {
switch c.GetProvider() {
case cloudprovider.AWS:
return registerTranslateAWSInstanceTypeError(ut)
case cloudprovider.Azure:
return registerTranslateAzureInstanceTypeError(ut)
case cloudprovider.GCP:
return registerTranslateGCPInstanceTypeError(ut)
}
return ut.Add("instance_type", "{0} is an invalid instance type", true)
}
func (c *Config) translateInstanceTypeError(ut ut.Translator, fe validator.FieldError) string {
switch c.GetProvider() {
case cloudprovider.AWS:
return c.translateAWSInstanceTypeError(ut, fe)
case cloudprovider.Azure:
return c.translateAzureInstanceTypeError(ut, fe)
case cloudprovider.GCP:
return translateGCPInstanceTypeError(ut, fe)
}
t, _ := ut.T("instance_type", fe.Field())
return t
}
func registerTranslateAWSInstanceTypeError(ut ut.Translator) error { func registerTranslateAWSInstanceTypeError(ut ut.Translator) error {
return ut.Add("aws_instance_type", "{0} must be an instance from one of the following families types with size xlarge or higher: {1}", true) return ut.Add("instance_type", "{0} must be an instance from one of the following families types with size xlarge or higher: {1}", true)
} }
func (c *Config) translateAWSInstanceTypeError(ut ut.Translator, fe validator.FieldError) string { func (c *Config) translateAWSInstanceTypeError(ut ut.Translator, fe validator.FieldError) string {
@ -268,24 +426,24 @@ func (c *Config) translateAWSInstanceTypeError(ut ut.Translator, fe validator.Fi
instances = instancetypes.AWSSupportedInstanceFamilies instances = instancetypes.AWSSupportedInstanceFamilies
} }
t, _ = ut.T("aws_instance_type", fe.Field(), fmt.Sprintf("%v", instances)) t, _ = ut.T("instance_type", fe.Field(), fmt.Sprintf("%v", instances))
return t return t
} }
func registerTranslateGCPInstanceTypeError(ut ut.Translator) error { func registerTranslateGCPInstanceTypeError(ut ut.Translator) error {
return ut.Add("gcp_instance_type", fmt.Sprintf("{0} must be one of %v", instancetypes.GCPInstanceTypes), true) return ut.Add("instance_type", fmt.Sprintf("{0} must be one of %v", instancetypes.GCPInstanceTypes), true)
} }
func translateGCPInstanceTypeError(ut ut.Translator, fe validator.FieldError) string { func translateGCPInstanceTypeError(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T("gcp_instance_type", fe.Field()) t, _ := ut.T("instance_type", fe.Field())
return t return t
} }
// Validation translation functions for Azure & GCP instance type errors. // Validation translation functions for Azure & GCP instance type errors.
func registerTranslateAzureInstanceTypeError(ut ut.Translator) error { func registerTranslateAzureInstanceTypeError(ut ut.Translator) error {
return ut.Add("azure_instance_type", "{0} must be one of {1}", true) return ut.Add("instance_type", "{0} must be one of {1}", true)
} }
func (c *Config) translateAzureInstanceTypeError(ut ut.Translator, fe validator.FieldError) string { func (c *Config) translateAzureInstanceTypeError(ut ut.Translator, fe validator.FieldError) string {
@ -299,7 +457,7 @@ func (c *Config) translateAzureInstanceTypeError(ut ut.Translator, fe validator.
instances = instancetypes.AzureTrustedLaunchInstanceTypes instances = instancetypes.AzureTrustedLaunchInstanceTypes
} }
t, _ = ut.T("azure_instance_type", fe.Field(), fmt.Sprintf("%v", instances)) t, _ = ut.T("instance_type", fe.Field(), fmt.Sprintf("%v", instances))
return t return t
} }
@ -371,6 +529,8 @@ func validInstanceTypeForProvider(insType string, acceptNonCVM bool, provider cl
} }
} }
return false return false
case cloudprovider.OpenStack, cloudprovider.QEMU:
return true
default: default:
return false return false
} }
@ -600,3 +760,86 @@ func warnDeprecated(fl validator.FieldLevel) bool {
) )
return true return true
} }
func (c *Config) validateNodeGroupZoneField(fl validator.FieldLevel) bool {
switch c.GetProvider() {
case cloudprovider.AWS:
return validateAWSZoneField(fl)
case cloudprovider.Azure:
return validateAzureZoneField(fl)
case cloudprovider.GCP:
return validateGCPZoneField(fl)
case cloudprovider.OpenStack:
return validateOpenStackRegionField(fl)
case cloudprovider.QEMU:
// QEMU does not use zones
return true
case cloudprovider.Unknown:
return true
}
// this indicates we are missing a case for a new provider
return false
}
func (c *Config) validateInstanceType(fl validator.FieldLevel) bool {
acceptNonCVM := c.GetAttestationConfig().GetVariant().Equal(variant.AzureTrustedLaunch{})
return validInstanceTypeForProvider(fl.Field().String(), acceptNonCVM, c.GetProvider())
}
func (c *Config) validateStateDiskTypeField(fl validator.FieldLevel) bool {
switch c.GetProvider() {
case cloudprovider.AWS:
return validateAWSStateDiskField(fl)
case cloudprovider.Azure:
return validateAzureStateDiskField(fl)
case cloudprovider.GCP:
return validateGCPStateDiskField(fl)
case cloudprovider.OpenStack:
return validateOpenStackStateDiskField(fl)
case cloudprovider.QEMU:
return validateQEMUStateDiskField(fl)
case cloudprovider.Unknown:
return true
}
// this indicates we are missing a case for a new provider
return false
}
func validateAWSStateDiskField(fl validator.FieldLevel) bool {
gotDiskField := fl.Field().String()
for _, diskType := range disktypes.AWSDiskTypes {
if gotDiskField == diskType {
return true
}
}
return false
}
func validateAzureStateDiskField(fl validator.FieldLevel) bool {
gotDiskField := fl.Field().String()
for _, diskType := range disktypes.AzureDiskTypes {
if gotDiskField == diskType {
return true
}
}
return false
}
func validateGCPStateDiskField(fl validator.FieldLevel) bool {
gotDiskField := fl.Field().String()
for _, diskType := range disktypes.GCPDiskTypes {
if gotDiskField == diskType {
return true
}
}
return false
}
func validateOpenStackStateDiskField(fl validator.FieldLevel) bool {
return len(fl.Field().String()) > 0
}
func validateQEMUStateDiskField(_ validator.FieldLevel) bool {
return true
}