From cdcabdeb6b1555f76b57dd5b8ce51da2121ea76f Mon Sep 17 00:00:00 2001 From: Vincent Batts Date: Tue, 19 Jan 2016 11:27:47 -0500 Subject: [PATCH] schema: JSON Schema and validator for `config.json` Conforming to https://tools.ietf.org/html/draft-zyp-json-schema-03 and http://json-schema.org/latest/json-schema-core.html * Utilizes a number of JSON schema features, including 'pattern' * Defined primitives, like integers, that we'll use * Split out definitions for primitives and platform-specific * Provide a Makefile for: - "fmt" target for *.json - "validate" target for building the validation tool Signed-off-by: Vincent Batts --- schema/Makefile | 15 ++ schema/defs-linux.json | 239 +++++++++++++++++++++++++++ schema/defs.json | 160 ++++++++++++++++++ schema/schema-linux.json | 343 +++++++++++++++++++++++++++++++++++++++ schema/schema.json | 158 ++++++++++++++++++ schema/validate.go | 45 +++++ 6 files changed, 960 insertions(+) create mode 100644 schema/Makefile create mode 100644 schema/defs-linux.json create mode 100644 schema/defs.json create mode 100644 schema/schema-linux.json create mode 100644 schema/schema.json create mode 100644 schema/validate.go diff --git a/schema/Makefile b/schema/Makefile new file mode 100644 index 000000000..5f0a4f683 --- /dev/null +++ b/schema/Makefile @@ -0,0 +1,15 @@ + +default: help + +help: + @echo "Usage: make " + @echo + @echo " * 'fmt' - format the json with indentation" + @echo " * 'validate' - build the validation tool" + +fmt: + for i in *.json ; do jq --indent 4 -M . "$${i}" > xx && cat xx > "$${i}" && rm xx ; done + +validate: validate.go + go build ./validate.go + diff --git a/schema/defs-linux.json b/schema/defs-linux.json new file mode 100644 index 000000000..1f143f020 --- /dev/null +++ b/schema/defs-linux.json @@ -0,0 +1,239 @@ +{ + "definitions": { + "SeccompArch": { + "type": "string", + "enum": [ + "SCMP_ARCH_X86", + "SCMP_ARCH_X86_64", + "SCMP_ARCH_X32", + "SCMP_ARCH_ARM", + "SCMP_ARCH_AARCH64", + "SCMP_ARCH_MIPS", + "SCMP_ARCH_MIPS64", + "SCMP_ARCH_MIPS64N32", + "SCMP_ARCH_MIPSEL", + "SCMP_ARCH_MIPSEL64", + "SCMP_ARCH_MIPSEL64N32" + ] + }, + "SeccompAction": { + "type": "string", + "enum": [ + "SCMP_ACT_KILL", + "SCMP_ACT_TRAP", + "SCMP_ACT_ERRNO", + "SCMP_ACT_TRACE", + "SCMP_ACT_ALLOW" + ] + }, + "SeccompOperators": { + "type": "string", + "enum": [ + "SCMP_CMP_NE", + "SCMP_CMP_LT", + "SCMP_CMP_LE", + "SCMP_CMP_EQ", + "SCMP_CMP_GE", + "SCMP_CMP_GT", + "SCMP_CMP_MASKED_EQ" + ] + }, + "SyscallArg": { + "properties": { + "index": { + "$ref": "defs.json#/definitions/uint32" + }, + "value": { + "$ref": "defs.json#/definitions/uint64" + }, + "valueTwo": { + "$ref": "defs.json#/definitions/uint64" + }, + "op": { + "$ref": "#/definitions/SeccompOperators" + } + } + }, + "Syscall": { + "properties": { + "name": { + "type": "string" + }, + "action": { + "$ref": "#/definitions/SeccompAction" + }, + "args": { + "type": "array", + "items": { + "$ref": "#/definitions/SyscallArg" + } + } + } + }, + "Capability": { + "description": "Linux process permissions", + "type": "string", + "pattern": "^CAP_([A-Z]|_)+$" + }, + "Major": { + "description": "major device number", + "$ref": "defs.json#/definitions/uint16" + }, + "Minor": { + "description": "minor device number", + "$ref": "defs.json#/definitions/uint16" + }, + "FileMode": { + "description": "File permissions mode (typically an octal value)", + "type": "integer", + "minimum": 0, + "maximum": 512 + }, + "FilePermissions": { + "type": "string" + }, + "FileType": { + "type": "integer" + }, + "Device": { + "properties": { + "type": { + "$ref": "#/definitions/FileType" + }, + "permissions": { + "$ref": "#/definitions/FilePermissions" + }, + "path": { + "$ref": "defs.json#/definitions/FilePath" + }, + "fileMode": { + "$ref": "#/definitions/FileMode" + }, + "major": { + "$ref": "#/definitions/Major" + }, + "minor": { + "$ref": "#/definitions/Minor" + }, + "uid": { + "$ref": "defs.json#/definitions/UID" + }, + "gid": { + "$ref": "defs.json#/definitions/GID" + } + } + }, + "blkioWeight": { + "type": "integer", + "minimum": 10, + "maximum": 1000 + }, + "blkioWeightPointer": { + "oneOf": [ + { + "$ref": "#/definitions/blkioWeight" + }, + { + "type": "null" + } + ] + }, + "blockIODevice": { + "properties": { + "major": { + "$ref": "#/definitions/Major" + }, + "minor": { + "$ref": "#/definitions/Minor" + } + }, + "required": [ + "major", + "minor" + ] + }, + "blockIODeviceWeight": { + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/blockIODevice" + }, + { + "properties": { + "weight": { + "$ref": "#/definitions/blkioWeightPointer" + }, + "leafWeight": { + "$ref": "#/definitions/blkioWeightPointer" + } + } + } + ] + }, + "blockIODeviceWeightPointer": { + "oneOf": [ + { + "$ref": "#/definitions/blockIODeviceWeight" + }, + { + "type": "null" + } + ] + }, + "blockIODeviceThrottle": { + "allOf": [ + { + "$ref": "#/definitions/blockIODevice" + }, + { + "properties": { + "rate": { + "$ref": "defs.json#/definitions/uint64Pointer" + } + } + } + ] + }, + "blockIODeviceThrottlePointer": { + "oneOf": [ + { + "$ref": "#/definitions/blockIODeviceThrottle" + }, + { + "type": "null" + } + ] + }, + "NetworkInterfacePriority": { + "properties": { + "name": { + "type": "string" + }, + "priority": { + "$ref": "defs.json#/definitions/uint32" + } + } + }, + "NamespaceType": { + "type": "string", + "enum": [ + "mount", + "pid", + "network", + "uts", + "ipc", + "user" + ] + }, + "NamespaceReference": { + "properties": { + "type": { + "$ref": "#/definitions/NamespaceType" + }, + "path": { + "$ref": "defs.json#/definitions/FilePath" + } + } + } + } +} diff --git a/schema/defs.json b/schema/defs.json new file mode 100644 index 000000000..a1a9e8777 --- /dev/null +++ b/schema/defs.json @@ -0,0 +1,160 @@ +{ + "description": "Definitions used throughout the OpenContainer Specification", + "definitions": { + "int8": { + "type": "integer", + "minimum": -128, + "maximum": 127 + }, + "int16": { + "type": "integer", + "minimum": -32768, + "maximum": 32767 + }, + "int32": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + "int64": { + "type": "integer", + "minimum": -9223372036854776000, + "maximum": 9223372036854776000 + }, + "uint8": { + "type": "integer", + "minimum": 0, + "maximum": 255 + }, + "uint16": { + "type": "integer", + "minimum": 0, + "maximum": 65535 + }, + "uint32": { + "type": "integer", + "minimum": 0, + "maximum": 4294967295 + }, + "uint64": { + "type": "integer", + "minimum": 0, + "maximum": 18446744073709552000 + }, + "uint16Pointer": { + "oneOf": [ + { + "$ref": "#/definitions/uint16" + }, + { + "type": "null" + } + ] + }, + "uint64Pointer": { + "oneOf": [ + { + "$ref": "#/definitions/uint64" + }, + { + "type": "null" + } + ] + }, + "stringPointer": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "mapStringString": { + "type": "object", + "patternProperties": { + ".{1,}": { + "type": "string" + } + } + }, + "UID": { + "$ref": "#/definitions/uint32" + }, + "GID": { + "$ref": "#/definitions/uint32" + }, + "ArrayOfGIDs": { + "type": "array", + "items": { + "$ref": "#/definitions/GID" + } + }, + "ArrayOfStrings": { + "type": "array", + "items": { + "type": "string" + } + }, + "FilePath": { + "type": "string" + }, + "Env": { + "$ref": "#/definitions/ArrayOfStrings" + }, + "Hook": { + "properties": { + "path": { + "$ref": "#/definitions/FilePath" + }, + "args": { + "$ref": "#/definitions/ArrayOfStrings" + }, + "env": { + "$ref": "#/definitions/Env" + } + } + }, + "ArrayOfHooks": { + "type": "array", + "items": { + "$ref": "#/definitions/Hook" + } + }, + "IDMapping": { + "properties": { + "hostID": { + "$ref": "#/definitions/uint32" + }, + "containerID": { + "$ref": "#/definitions/uint32" + }, + "size": { + "$ref": "#/definitions/uint32" + } + } + }, + "Mount": { + "properties": { + "source": { + "$ref": "#/definitions/FilePath" + }, + "destination": { + "$ref": "#/definitions/FilePath" + }, + "options": { + "$ref": "#/definitions/ArrayOfStrings" + }, + "type": { + "type": "string" + } + }, + "required": [ + "destination", + "source", + "type" + ] + } + } +} diff --git a/schema/schema-linux.json b/schema/schema-linux.json new file mode 100644 index 000000000..8ffb1ff05 --- /dev/null +++ b/schema/schema-linux.json @@ -0,0 +1,343 @@ +{ + "linux": { + "description": "Linux platform-specific configurations", + "id": "https://opencontainers.org/schema/bundle/linux", + "type": "object", + "properties": { + "devices": { + "id": "https://opencontainers.org/schema/bundle/linux/devices", + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "defs-linux.json#/definitions/Device" + } + }, + { + "type": "null" + } + ] + }, + "uidMappings": { + "id": "https://opencontainers.org/schema/bundle/linux/uidMappings", + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "defs.json#/definitions/IDMapping" + } + }, + { + "type": "null" + } + ] + }, + "gidMappings": { + "id": "https://opencontainers.org/schema/bundle/linux/gidMappings", + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "defs.json#/definitions/IDMapping" + } + }, + { + "type": "null" + } + ] + }, + "namespaces": { + "id": "https://opencontainers.org/schema/bundle/linux/namespaces", + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "defs-linux.json#/definitions/NamespaceReference" + } + ] + } + }, + "resources": { + "id": "https://opencontainers.org/schema/bundle/linux/resources", + "type": "object", + "properties": { + "blockIO": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO", + "type": "object", + "properties": { + "blkioWeight": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO/blkioWeight", + "$ref": "defs-linux.json#/definitions/blkioWeightPointer" + }, + "blkioLeafWeight": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO/blkioLeafWeight", + "$ref": "defs-linux.json#/definitions/blkioWeightPointer" + }, + "blkioThrottleReadBpsDevice": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO/blkioThrottleReadBpsDevice", + "oneOf": [ + { + "type": "array", + "items": [ + { + "$ref": "defs-linux.json#/definitions/blockIODeviceThrottlePointer" + } + ] + }, + { + "type": "null" + } + ] + }, + "blkioThrottleWriteBpsDevice": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO/blkioThrottleWriteBpsDevice", + "oneOf": [ + { + "type": "array", + "items": [ + { + "$ref": "defs-linux.json#/definitions/blockIODeviceThrottlePointer" + } + ] + }, + { + "type": "null" + } + ] + }, + "blkioThrottleReadIopsDevice": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO/blkioThrottleReadIopsDevice", + "oneOf": [ + { + "type": "array", + "items": [ + { + "$ref": "defs-linux.json#/definitions/blockIODeviceThrottlePointer" + } + ] + }, + { + "type": "null" + } + ] + }, + "blkioThrottleWriteIopsDevice": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO/blkioThrottleWriteIopsDevice", + "oneOf": [ + { + "type": "array", + "items": [ + { + "$ref": "defs-linux.json#/definitions/blockIODeviceThrottlePointer" + } + ] + }, + { + "type": "null" + } + ] + }, + "blkioWeightDevice": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/blockIO/blkioWeightDevice", + "type": "array", + "items": [ + { + "$ref": "defs-linux.json#/definitions/blockIODeviceWeightPointer" + } + ] + } + } + }, + "cpu": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/cpu", + "properties": { + "cpus": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/cpu/cpus", + "$ref": "defs.json#/definitions/stringPointer" + }, + "mems": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/cpu/mems", + "$ref": "defs.json#/definitions/stringPointer" + }, + "period": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/cpu/period", + "$ref": "defs.json#/definitions/uint64Pointer" + }, + "quota": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/cpu/quota", + "$ref": "defs.json#/definitions/uint64Pointer" + }, + "realtimePeriod": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/cpu/realtimePeriod", + "$ref": "defs.json#/definitions/uint64Pointer" + }, + "realtimeRuntime": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/cpu/realtimeRuntime", + "$ref": "defs.json#/definitions/uint64Pointer" + }, + "shares": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/cpu/shares", + "$ref": "defs.json#/definitions/uint64Pointer" + } + }, + "type": "object" + }, + "disableOOMKiller": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/disableOOMKiller", + "type": "boolean" + }, + "hugepageLimits": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/hugepageLimits", + "oneOf": [ + { + "type": "object", + "properties": { + "pageSize": { + "type": "string" + }, + "limit": { + "$ref": "defs.json#/definitions/uint64" + } + } + }, + { + "type": "null" + } + ] + }, + "memory": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/memory", + "type": "object", + "properties": { + "kernel": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/memory/kernel", + "$ref": "defs.json#/definitions/uint64Pointer" + }, + "limit": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/memory/limit", + "$ref": "defs.json#/definitions/uint64Pointer" + }, + "reservation": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/memory/reservation", + "$ref": "defs.json#/definitions/uint64Pointer" + }, + "swap": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/memory/swap", + "$ref": "defs.json#/definitions/uint64Pointer" + }, + "swappiness": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/memory/swappiness", + "$ref": "defs.json#/definitions/uint64Pointer" + } + } + }, + "network": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/network", + "type": "object", + "properties": { + "classId": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/network/classId", + "type": "string" + }, + "priorities": { + "id": "https://opencontainers.org/schema/bundle/linux/resources/network/priorities", + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "defs-linux.json#/definitions/NetworkInterfacePriority" + } + }, + { + "type": "null" + } + ] + } + } + } + } + }, + "rlimits": { + "id": "https://opencontainers.org/schema/bundle/linux/rlimits", + "items": [ + { + "id": "https://opencontainers.org/schema/bundle/linux/rlimits/0", + "properties": { + "hard": { + "id": "https://opencontainers.org/schema/bundle/linux/rlimits/0/hard", + "type": "integer" + }, + "soft": { + "id": "https://opencontainers.org/schema/bundle/linux/rlimits/0/soft", + "type": "integer" + }, + "type": { + "id": "https://opencontainers.org/schema/bundle/linux/rlimits/0/type", + "type": "string", + "pattern": "^RLIMIT_[A-Z]+$" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "cgroupsPath": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "string" + } + ] + }, + "rootfsPropagation": { + "id": "https://opencontainers.org/schema/bundle/linux/rootfsPropagation", + "type": "string" + }, + "seccomp": { + "id": "https://opencontainers.org/schema/bundle/linux/seccomp", + "properties": { + "defaultAction": { + "id": "https://opencontainers.org/schema/bundle/linux/seccomp/defaultAction", + "type": "string" + }, + "architectures": { + "id": "https://opencontainers.org/schema/bundle/linux/seccomp/architectures", + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "defs-linux.json#/definitions/SeccompArch" + } + }, + { + "type": "null" + } + ] + }, + "syscalls": { + "id": "https://opencontainers.org/schema/bundle/linux/seccomp/syscalls", + "type": "array", + "items": { + "$ref": "defs-linux.json#/definitions/Syscall" + } + } + }, + "type": "object" + }, + "sysctl": { + "id": "https://opencontainers.org/schema/bundle/linux/sysctl", + "oneOf": [ + { + "$ref": "defs.json#/definitions/mapStringString" + }, + { + "type": "null" + } + ] + } + } + } +} diff --git a/schema/schema.json b/schema/schema.json new file mode 100644 index 000000000..16be027c0 --- /dev/null +++ b/schema/schema.json @@ -0,0 +1,158 @@ +{ + "description": "Schema for OpenContainer bundle configuration file", + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "https://opencontainers.org/schema/bundle", + "type": "object", + "properties": { + "ociVersion": { + "description": "The version of OpenContainer specification configuration complies with", + "id": "https://opencontainers.org/schema/bundle/ociVersion", + "type": "string" + }, + "hooks": { + "id": "https://opencontainers.org/schema/bundle/hooks", + "type": "object", + "properties": { + "prestart": { + "$ref": "defs.json#/definitions/ArrayOfHooks" + }, + "poststart": { + "$ref": "defs.json#/definitions/ArrayOfHooks" + }, + "poststop": { + "$ref": "defs.json#/definitions/ArrayOfHooks" + } + } + }, + "annotations": { + "id": "https://opencontainers.org/schema/bundle/linux/sysctl", + "oneOf": [ + { + "$ref": "defs.json#/definitions/mapStringString" + }, + { + "type": "null" + } + ] + }, + "hostname": { + "id": "https://opencontainers.org/schema/bundle/hostname", + "type": "string" + }, + "mounts": { + "id": "https://opencontainers.org/schema/bundle/mounts", + "type": "array", + "items": { + "$ref": "defs.json#/definitions/Mount" + } + }, + "platform": { + "id": "https://opencontainers.org/schema/bundle/platform", + "type": "object", + "required": [ + "arch", + "os" + ], + "properties": { + "arch": { + "id": "https://opencontainers.org/schema/bundle/platform/arch", + "type": "string" + }, + "os": { + "id": "https://opencontainers.org/schema/bundle/platform/os", + "type": "string" + } + } + }, + "root": { + "description": "the root filesystem the container's bundle", + "id": "https://opencontainers.org/schema/bundle/root", + "type": "object", + "properties": { + "path": { + "id": "https://opencontainers.org/schema/bundle/root/path", + "$ref": "defs.json#/definitions/FilePath" + }, + "readonly": { + "id": "https://opencontainers.org/schema/bundle/root/readonly", + "type": "boolean" + } + } + }, + "process": { + "id": "https://opencontainers.org/schema/bundle/process", + "type": "object", + "required": [ + "cwd", + "args" + ], + "properties": { + "args": { + "id": "https://opencontainers.org/schema/bundle/process/args", + "$ref": "defs.json#/definitions/ArrayOfStrings" + }, + "cwd": { + "id": "https://opencontainers.org/schema/bundle/process/cwd", + "type": "string" + }, + "env": { + "id": "https://opencontainers.org/schema/bundle/process/env", + "$ref": "defs.json#/definitions/Env" + }, + "terminal": { + "id": "https://opencontainers.org/schema/bundle/process/terminal", + "type": "boolean" + }, + "user": { + "id": "https://opencontainers.org/schema/bundle/process/user", + "type": "object", + "properties": { + "uid": { + "id": "https://opencontainers.org/schema/bundle/process/user/uid", + "$ref": "defs.json#/definitions/UID" + }, + "gid": { + "id": "https://opencontainers.org/schema/bundle/process/user/gid", + "$ref": "defs.json#/definitions/GID" + }, + "additionalGids": { + "id": "https://opencontainers.org/schema/bundle/process/user/additionalGids", + "$ref": "defs.json#/definitions/ArrayOfGIDs" + } + } + }, + "capabilities": { + "id": "https://opencontainers.org/schema/bundle/process/linux/capabilities", + "type": "array", + "items": { + "$ref": "defs-linux.json#/definitions/Capability" + } + }, + "apparmorProfile": { + "id": "https://opencontainers.org/schema/bundle/process/linux/apparmorProfile", + "type": "string" + }, + "selinuxProcessLabel": { + "id": "https://opencontainers.org/schema/bundle/process/linux/selinuxProcessLabel", + "type": "string" + }, + "noNewPrivileges": { + "id": "https://opencontainers.org/schema/bundle/process/linux/noNewPrivileges", + "type": "boolean" + } + } + }, + "linux": { + "$ref": "schema-linux.json#/linux" + } + }, + "required": [ + "ociVersion", + "platform", + "process", + "root", + "hostname", + "mounts", + "hooks" + ] +} diff --git a/schema/validate.go b/schema/validate.go new file mode 100644 index 000000000..a4d60ec9d --- /dev/null +++ b/schema/validate.go @@ -0,0 +1,45 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/xeipuuv/gojsonschema" +) + +func main() { + if len(os.Args[1:]) != 2 { + fmt.Printf("ERROR: usage is: %s ", os.Args[0]) + os.Exit(1) + } + + schemaPath, err := filepath.Abs(os.Args[1]) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + documentPath, err := filepath.Abs(os.Args[2]) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + schemaLoader := gojsonschema.NewReferenceLoader("file://" + schemaPath) + documentLoader := gojsonschema.NewReferenceLoader("file://" + documentPath) + + result, err := gojsonschema.Validate(schemaLoader, documentLoader) + if err != nil { + panic(err.Error()) + } + + if result.Valid() { + fmt.Printf("The document is valid\n") + } else { + fmt.Printf("The document is not valid. see errors :\n") + for _, desc := range result.Errors() { + fmt.Printf("- %s\n", desc) + } + os.Exit(1) + } +}