From d4b604c3970cf5157e3905ecfcb3f305d2603c50 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Wed, 17 Aug 2016 23:25:29 -0700 Subject: [PATCH] validate: Check configuration against JSON Schema runtime-spec publishes a JSON Schema covering the configuration format (and other JSON related to runtime-spec) [1]. 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. [1]: https://github.com/opencontainers/runtime-spec/tree/v1.0.0-rc1/schema Signed-off-by: W. Trevor King --- cmd/ocitools/validate.go | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/cmd/ocitools/validate.go b/cmd/ocitools/validate.go index 0fab50202..994d8543e 100644 --- a/cmd/ocitools/validate.go +++ b/cmd/ocitools/validate.go @@ -18,6 +18,7 @@ import ( "github.com/blang/semver" rspec "github.com/opencontainers/runtime-spec/specs-go" "github.com/urfave/cli" + "github.com/xeipuuv/gojsonschema" ) type configCheck func(rspec.Spec, string, bool) []string @@ -61,6 +62,8 @@ var ( "CAP_KILL", "CAP_AUDIT_WRITE", } + + configSchemaTemplate = "https://raw.githubusercontent.com/opencontainers/runtime-spec/v%s/schema/schema.json" ) var bundleValidateCommand = cli.Command{ @@ -103,6 +106,7 @@ var bundleValidateCommand = cli.Command{ checks := []configCheck{ checkMandatoryFields, checkSemVer, + checkJSONSchema, checkMounts, checkPlatform, checkProcess, @@ -144,6 +148,44 @@ func checkSemVer(spec rspec.Spec, rootfs string, hostCheck bool) (msgs []string) return } +// JSONSchemaURL returns the URL for the JSON Schema specifying the +// configuration format. It consumes configSchemaTemplate, but we +// provide it as a function to isolate consumers from inconsistent +// naming as runtime-spec evolves. +func JSONSchemaURL(version string) (url string, err error) { + if version != rspec.Version { + return "", fmt.Errorf("unrecognized version %s", version) + } + return fmt.Sprintf(configSchemaTemplate, version), nil +} + +// checkJSONSchema validates the configuration against the +// runtime-spec JSON Schema, using the version of the schema that +// matches the configuration's declared version. +func checkJSONSchema(spec rspec.Spec, rootfs string, hostCheck bool) (msgs []string) { + url, err := JSONSchemaURL(spec.Version) + if err != nil { + msgs = append(msgs, err.Error()) + return msgs + } + + schemaLoader := gojsonschema.NewReferenceLoader(url) + documentLoader := gojsonschema.NewGoLoader(spec) + result, err := gojsonschema.Validate(schemaLoader, documentLoader) + if err != nil { + msgs = append(msgs, fmt.Sprintf("internal error: %s", err.Error())) + return msgs + } + + if !result.Valid() { + for _, resultError := range result.Errors() { + msgs = append(msgs, fmt.Sprintf("%s", resultError)) + } + } + + return msgs +} + func checkPlatform(spec rspec.Spec, rootfs string, hostCheck bool) (msgs []string) { logrus.Debugf("check platform")