Skip to content

Commit 684188c

Browse files
committed
Add validations and unit tests
1 parent 37a4cc8 commit 684188c

12 files changed

Lines changed: 498 additions & 32 deletions

File tree

community/modules/compute/gke-nodeset/metadata.yaml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2025 Google LLC
1+
# Copyright 2026 Google LLC
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -25,3 +25,17 @@ ghpc:
2525
vars: [slurm_cluster_name]
2626
pattern: "^[a-z](?:[a-z0-9]{0,9})$"
2727
error_message: "'slurm_cluster_name' must contain only lowercase letters and numbers, start with a letter, and be 1-10 characters long."
28+
- validator: range
29+
inputs:
30+
vars: [network_storage]
31+
min: 1
32+
max: 1
33+
length_check: true
34+
error_message: "The 'network_storage' list must contain exactly one element."
35+
- validator: range
36+
inputs:
37+
vars: [filestore_id]
38+
min: 1
39+
max: 1
40+
length_check: true
41+
error_message: "The 'filestore_id' list must contain exactly one element."

community/modules/compute/mig/metadata.yaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2023 "Google LLC"
1+
# Copyright 2026 "Google LLC"
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -19,3 +19,10 @@ spec:
1919
- compute.googleapis.com
2020
ghpc:
2121
inject_module_id: ghpc_module_id
22+
validators:
23+
- validator: range
24+
inputs:
25+
vars: [versions]
26+
min: 1
27+
length_check: true
28+
error_message: "The 'versions' list must contain at least one version."

community/modules/compute/notebook/metadata.yaml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2023 "Google LLC"
1+
# Copyright 2026 "Google LLC"
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -18,3 +18,13 @@ spec:
1818
services:
1919
- notebooks.googleapis.com
2020
- storage.googleapis.com
21+
ghpc:
22+
validators:
23+
- validator: range
24+
inputs:
25+
vars: [mount_runner.args]
26+
min: 5
27+
max: 5
28+
length_check: true
29+
delimiter: " "
30+
error_message: "The 'mount_runner.args' field must contain exactly 5 elements."

community/modules/database/slurm-cloudsql-federation/metadata.yaml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2023 "Google LLC"
1+
# Copyright 2026 "Google LLC"
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -19,3 +19,10 @@ spec:
1919
- bigqueryconnection.googleapis.com
2020
- sqladmin.googleapis.com
2121
- servicenetworking.googleapis.com
22+
ghpc:
23+
validators:
24+
- validator: regex
25+
inputs:
26+
vars: [network_id]
27+
pattern: ^projects/[^/]+/global/networks/[^/]+$
28+
error_message: "The network id must be provided in the following format: projects/<project_id>/global/networks/<network_name>."
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright 2026 "Google LLC"
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
---
15+
16+
ghpc:
17+
validators:
18+
- validator: range
19+
inputs:
20+
vars: [node_count_static]
21+
min: 0
22+
length_check: false
23+
error_message: "The 'node_count_static' value must be at least 0."
24+
- validator: range
25+
inputs:
26+
vars: [node_count_dynamic_max]
27+
min: 0
28+
length_check: false
29+
error_message: "The 'node_count_dynamic_max' value must be at least 0."

community/modules/scheduler/htcondor-access-point/metadata.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2023 "Google LLC"
1+
# Copyright 2026 "Google LLC"
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -18,3 +18,11 @@ spec:
1818
services:
1919
- compute.googleapis.com
2020
- storage.googleapis.com
21+
ghpc:
22+
validators:
23+
- validator: range
24+
inputs:
25+
vars: [zones]
26+
max: 2
27+
length_check: true
28+
error_message: "The 'zones' list must contain at most 2 zones."

docs/blueprint-validation.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,24 @@ ghpc:
121121
error_message: "partition_name must be lowercase alphanumeric and max 10 characters."
122122
```
123123

124+
### Range Validator
125+
The `range` validator ensures that input variables (or their computed lengths/segment counts) fall within a specified numerical minimum and/or maximum. It supports validating individual numeric values, the number of elements in a list, or the count of segments in a delimited string (when an optional delimiter is specified). The optional length_check field (defaulting to false) determines whether to validate the value itself or the count of its elements/segments.
126+
127+
**Example definition in `metadata.yaml`:**
128+
129+
```yaml
130+
ghpc:
131+
validators:
132+
- validator: range
133+
inputs:
134+
vars: [mount_runner.args]
135+
min: 5
136+
max: 5
137+
length_check: true
138+
delimiter: " "
139+
error_message: "The 'mount_runner.args' field must contain exactly 5 elements."
140+
```
141+
124142
Unlike blueprint-level validators, these are intrinsic to the module and ensure that the module receives data in the exact format required for its internal logic to function.
125143

126144
## Skipping or Disabling Validators

modules/file-system/filestore/metadata.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2023 "Google LLC"
1+
# Copyright 2026 "Google LLC"
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -17,3 +17,12 @@ spec:
1717
requirements:
1818
services:
1919
- file.googleapis.com
20+
21+
ghpc:
22+
validators:
23+
- validator: range
24+
inputs:
25+
vars: [size_gb]
26+
min: 1024
27+
length_check: false
28+
error_message: "The 'size_gb' value must be at least 1024 GiB. See https://cloud.google.com/filestore/docs/service-tiers for details."

modules/network/multivpc/metadata.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2024 "Google LLC"
1+
# Copyright 2026 "Google LLC"
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -17,3 +17,12 @@ spec:
1717
requirements:
1818
services:
1919
- compute.googleapis.com
20+
ghpc:
21+
validators:
22+
- validator: range
23+
inputs:
24+
vars: [network_count]
25+
min: 2
26+
max: 8
27+
length_check: false
28+
error_message: "The 'network_count' must be between 2 and 8. Use the standard Toolkit module at 'modules/network/vpc' for count = 1."

pkg/validators/metadata_validator_helpers.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2025, 2026 "Google LLC"
1+
// Copyright 2026 "Google LLC"
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -64,7 +64,7 @@ func evaluateAndFlatten(val cty.Value) []cty.Value {
6464

6565
// getModuleSettingValues retrieves a cty.Value from module settings using a dot-separated path,
6666
// evaluates expressions via bp.Eval and returns flattened slice + path for errors.
67-
func getModuleSettingValues(bp config.Blueprint, group config.Group, modIdx int, mod config.Module, settingName string) (cty.Value, config.Path, error) {
67+
func getModuleSettingValues(bp config.Blueprint, group config.Group, modIdx int, mod config.Module, settingName string) ([]cty.Value, config.Path, error) {
6868
var nilPath config.Path
6969

7070
// Determine canonical path for this module.setting in the blueprint.
@@ -76,7 +76,7 @@ func getModuleSettingValues(bp config.Blueprint, group config.Group, modIdx int,
7676

7777
if bp.YamlCtx != nil {
7878
if _, ok := bp.YamlCtx.Pos(path); !ok {
79-
return cty.NilVal, nilPath, fmt.Errorf("setting %q not present in blueprint YAML for module %q", settingName, mod.ID)
79+
return nil, nilPath, fmt.Errorf("setting %q not present in blueprint YAML for module %q", settingName, mod.ID)
8080
}
8181
}
8282

@@ -85,7 +85,9 @@ func getModuleSettingValues(bp config.Blueprint, group config.Group, modIdx int,
8585
if evaledVal, err := bp.Eval(val); err == nil {
8686
val = evaledVal
8787
}
88-
return val, path, nil
88+
values := evaluateAndFlatten(val)
89+
90+
return values, path, nil
8991
}
9092

9193
// parseStringList normalizes an input that may be a single string, []interface{} or nil into []string.
@@ -116,7 +118,7 @@ func parseStringList(v interface{}) ([]string, bool) {
116118
// Target represents a resolved target (module-setting or blueprint var) for validation.
117119
type Target struct {
118120
Name string
119-
Value cty.Value
121+
Values []cty.Value
120122
Path config.Path
121123
IsBlueprint bool // true if came from blueprint vars, false if module.settings
122124
}
@@ -144,7 +146,7 @@ func parseIntInput(inputs map[string]interface{}, key string) (*int, error) {
144146
// processModuleSettings processes a list of names interpreted as module settings.
145147
func processModuleSettings(bp config.Blueprint, mod config.Module, group config.Group, modIdx int, list []string, optional bool, handler func(Target) error) error {
146148
for _, s := range list {
147-
value, path, err := getModuleSettingValues(bp, group, modIdx, mod, s)
149+
values, path, err := getModuleSettingValues(bp, group, modIdx, mod, s)
148150
if err != nil {
149151
if optional {
150152
continue
@@ -155,7 +157,7 @@ func processModuleSettings(bp config.Blueprint, mod config.Module, group config.
155157
Path: missingPath,
156158
}
157159
}
158-
if err := handler(Target{Name: s, Value: value, Path: path, IsBlueprint: false}); err != nil {
160+
if err := handler(Target{Name: s, Values: values, Path: path, IsBlueprint: false}); err != nil {
159161
return err
160162
}
161163
}

0 commit comments

Comments
 (0)