Skip to content

Commit f4f5890

Browse files
Add Blueprint level validation for slurm_cluster_name variable (#5010)
1 parent f58062d commit f4f5890

4 files changed

Lines changed: 121 additions & 4 deletions

File tree

pkg/config/config.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,7 @@ func (err InputValueError) Error() string {
617617

618618
var matchLabelNameExp *regexp.Regexp = regexp.MustCompile(`^[\p{Ll}\p{Lo}][\p{Ll}\p{Lo}\p{N}_-]{0,62}$`)
619619
var matchLabelValueExp *regexp.Regexp = regexp.MustCompile(`^[\p{Ll}\p{Lo}\p{N}_-]{0,63}$`)
620+
var matchSlurmClusterNameExp *regexp.Regexp = regexp.MustCompile(`^[a-z](?:[a-z0-9]{0,9})$`)
620621

621622
// isValidLabelName checks if a string is a valid name for a GCP label.
622623
// For more information on valid label names, see the docs at:
@@ -696,6 +697,42 @@ func (bp *Blueprint) checkBlueprintName() error {
696697
return nil
697698
}
698699

700+
func validateSlurmClusterName(bp Blueprint) error {
701+
path := Root.Vars.Dot("slurm_cluster_name")
702+
703+
if !bp.Vars.Has("slurm_cluster_name") {
704+
return nil // Optional: slurm_cluster_name is not always required
705+
}
706+
707+
v, err := bp.Eval(GlobalRef("slurm_cluster_name").AsValue())
708+
if err != nil {
709+
return BpError{path, err}
710+
}
711+
if v.Type() != cty.String || v.IsNull() || !v.IsKnown() {
712+
return BpError{path, InputValueError{
713+
inputKey: "slurm_cluster_name",
714+
cause: errMsgValueNotString,
715+
}}
716+
}
717+
718+
s := v.AsString()
719+
if len(s) == 0 {
720+
return BpError{path, InputValueError{
721+
inputKey: "slurm_cluster_name",
722+
cause: errMsgValueEmptyString,
723+
}}
724+
}
725+
726+
// Check that slurm_cluster_name matches the required regex
727+
if !matchSlurmClusterNameExp.MatchString(s) {
728+
return BpError{path, InputValueError{
729+
inputKey: "slurm_cluster_name",
730+
cause: errMsgSlurmClusterNameReqs,
731+
}}
732+
}
733+
return nil
734+
}
735+
699736
// checkToolkitModulesUrlAndVersion returns an error if either
700737
// toolkit_modules_url or toolkit_modules_version is
701738
// exclsuively supplied (i.e., one is present, but the other is missing).

pkg/config/config_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,3 +845,81 @@ func (s *zeroSuite) TestEvalVars(c *C) {
845845
}
846846
}
847847
}
848+
849+
func (s *zeroSuite) TestValidateSlurmClusterName(c *C) {
850+
var e InputValueError
851+
852+
h := func(val cty.Value) error {
853+
vars := NewDict(map[string]cty.Value{"slurm_cluster_name": val})
854+
return validateSlurmClusterName(Blueprint{Vars: vars})
855+
}
856+
857+
// Valid slurm_cluster_name examples
858+
c.Check(h(cty.StringVal("a")), IsNil) // single lowercase letter
859+
c.Check(h(cty.StringVal("abc123")), IsNil) // letters and numbers
860+
c.Check(h(cty.StringVal("slurm1")), IsNil) // typical name
861+
c.Check(h(cty.StringVal("a123456789")), IsNil) // max length (10 chars)
862+
863+
{ // Is slurm_cluster_name an empty string?
864+
err := h(cty.StringVal(""))
865+
c.Check(errors.As(err, &e), Equals, true)
866+
c.Check(err, ErrorMatches, ".*empty string.*")
867+
}
868+
869+
{ // Is slurm_cluster_name not a string?
870+
err := h(cty.NumberIntVal(100))
871+
c.Check(errors.As(err, &e), Equals, true)
872+
c.Check(err, ErrorMatches, ".*not.*string.*")
873+
}
874+
875+
{ // Is slurm_cluster_name longer than 10 characters?
876+
err := h(cty.StringVal("slurm12345678"))
877+
c.Check(errors.As(err, &e), Equals, true)
878+
c.Check(err, ErrorMatches, ".*between 1 and 10 characters.*")
879+
}
880+
881+
{ // Does slurm_cluster_name contain uppercase letters?
882+
err := h(cty.StringVal("Slurm"))
883+
c.Check(errors.As(err, &e), Equals, true)
884+
c.Check(err, ErrorMatches, ".*lowercase letter.*")
885+
}
886+
887+
{ // Does slurm_cluster_name start with a number?
888+
err := h(cty.StringVal("1slurm"))
889+
c.Check(errors.As(err, &e), Equals, true)
890+
c.Check(err, ErrorMatches, ".*start with a lowercase letter.*")
891+
}
892+
893+
{ // Does slurm_cluster_name contain special characters (dash)?
894+
err := h(cty.StringVal("slurm-gke"))
895+
c.Check(errors.As(err, &e), Equals, true)
896+
c.Check(err, ErrorMatches, ".*lowercase letters and numbers.*")
897+
}
898+
899+
{ // Does slurm_cluster_name contain special characters (underscore)?
900+
err := h(cty.StringVal("slurm_gke"))
901+
c.Check(errors.As(err, &e), Equals, true)
902+
c.Check(err, ErrorMatches, ".*lowercase letters and numbers.*")
903+
}
904+
905+
{ // Does slurm_cluster_name contain special characters (period)?
906+
err := h(cty.StringVal("slurm.gke"))
907+
c.Check(errors.As(err, &e), Equals, true)
908+
c.Check(err, ErrorMatches, ".*lowercase letters and numbers.*")
909+
}
910+
911+
{ // Is slurm_cluster_name not set? (should pass - it's optional)
912+
err := validateSlurmClusterName(Blueprint{})
913+
c.Check(err, IsNil)
914+
}
915+
916+
{ // Expression (should pass if it evaluates correctly)
917+
c.Check(h(MustParseExpression(`"slurm${1}"`).AsValue()), IsNil)
918+
}
919+
920+
{ // Expression that results in invalid value
921+
err := h(MustParseExpression(`"Slurm${1}"`).AsValue())
922+
c.Check(errors.As(err, &e), Equals, true)
923+
c.Check(err, ErrorMatches, ".*lowercase letter.*")
924+
}
925+
}

pkg/config/errors.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ var UnexpectedRefFormat = errors.New("Expected reference formats: $(vars.var_nam
163163
const (
164164
errMsgIntergroupOrder = string("references to outputs from other groups must be to earlier groups")
165165

166-
errMsgValueNotString = string("value was not of type string")
167-
errMsgValueEmptyString = string("value is an empty string")
168-
errMsgLabelValueReqs = string("value can only contain lowercase letters, numeric characters, underscores and dashes, and must be between 0 and 63 characters long")
166+
errMsgValueNotString = string("value was not of type string")
167+
errMsgValueEmptyString = string("value is an empty string")
168+
errMsgLabelValueReqs = string("value can only contain lowercase letters, numeric characters, underscores and dashes, and must be between 0 and 63 characters long")
169+
errMsgSlurmClusterNameReqs = string("must start with a lowercase letter, contain only lowercase letters and numbers, and be between 1 and 10 characters long")
169170
)

pkg/config/validate.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ func validateVars(bp Blueprint) error {
8989

9090
errs := (&Errors{}).
9191
Add(validateDeploymentName(bp)).
92-
Add(validateGlobalLabels(bp))
92+
Add(validateGlobalLabels(bp)).
93+
Add(validateSlurmClusterName(bp))
9394
// Check for any nil values
9495
// Iterator over non evaluated variables, it's Ok if evaluated value is null
9596
for key, val := range bp.Vars.Items() {

0 commit comments

Comments
 (0)