Skip to content

Commit d81eb6f

Browse files
committed
validate: Check configuration against JSON Schema
runtime-spec publishes a JSON Schema covering the configuration format (and other JSON related to runtime-spec). Reduce duplication of effort by validating configurations against that schema. For example this gives us lots of range checking: $ ocitools validate ... 1. linux.resources.oomScoreAdj: Must be greater than or equal to -1000 ... without us having to duplicate all the range-input work that the runtime-spec folks have already done for us. Only validating the JSON Schema is not sufficient, because --host-specific (e.g. you're running on a Linux box) and cross-property constraits (e.g. must create a new UTS namespace if you set hostname) are difficult/impossible to express in JSON Schema. Signed-off-by: W. Trevor King <[email protected]>
1 parent ea1d5dd commit d81eb6f

File tree

1 file changed

+42
-0
lines changed

1 file changed

+42
-0
lines changed

cmd/ocitools/validate.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/blang/semver"
1919
rspec "github.com/opencontainers/runtime-spec/specs-go"
2020
"github.com/urfave/cli"
21+
"github.com/xeipuuv/gojsonschema"
2122
)
2223

2324
type configCheck func(rspec.Spec, string, bool) []string
@@ -61,6 +62,8 @@ var (
6162
"CAP_KILL",
6263
"CAP_AUDIT_WRITE",
6364
}
65+
66+
configSchemaTemplate = "https://raw.githubusercontent.com/opencontainers/runtime-spec/v%s/schema/schema.json"
6467
)
6568

6669
var bundleValidateCommand = cli.Command{
@@ -103,6 +106,7 @@ var bundleValidateCommand = cli.Command{
103106
checks := []configCheck{
104107
checkMandatoryFields,
105108
checkSemVer,
109+
checkJSONSchema,
106110
checkMounts,
107111
checkPlatform,
108112
checkProcess,
@@ -144,6 +148,44 @@ func checkSemVer(spec rspec.Spec, rootfs string, hostCheck bool) (msgs []string)
144148
return
145149
}
146150

151+
// JSONSchemaURL returns the URL for the JSON Schema specifying the
152+
// configuration format. It consumes configSchemaTemplate, but we
153+
// provide it as a function to isolate consumers from inconsistent
154+
// naming as runtime-spec evolves.
155+
func JSONSchemaURL(version string) (url string, err error) {
156+
if version != rspec.Version {
157+
return "", fmt.Errorf("unrecognized version %s", version)
158+
}
159+
return fmt.Sprintf(configSchemaTemplate, version), nil
160+
}
161+
162+
// checkJSONSchema validates the configuration against the
163+
// runtime-spec JSON Schema, using the version of the schema that
164+
// matches the configuration's declared version.
165+
func checkJSONSchema(spec rspec.Spec, rootfs string, hostCheck bool) (msgs []string) {
166+
url, err := JSONSchemaURL(spec.Version)
167+
if err != nil {
168+
msgs = append(msgs, err.Error())
169+
return msgs
170+
}
171+
172+
schemaLoader := gojsonschema.NewReferenceLoader(url)
173+
documentLoader := gojsonschema.NewGoLoader(spec)
174+
result, err := gojsonschema.Validate(schemaLoader, documentLoader)
175+
if err != nil {
176+
msgs = append(msgs, fmt.Sprintf("internal error: %s", err.Error()))
177+
return msgs
178+
}
179+
180+
if !result.Valid() {
181+
for _, resultError := range result.Errors() {
182+
msgs = append(msgs, fmt.Sprintf("%s", resultError))
183+
}
184+
}
185+
186+
return msgs
187+
}
188+
147189
func checkPlatform(spec rspec.Spec, rootfs string, hostCheck bool) (msgs []string) {
148190
logrus.Debugf("check platform")
149191

0 commit comments

Comments
 (0)