From 65374488f92512cf34667cb71ea6d62985310f65 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Tue, 10 Jan 2017 17:10:53 -0500 Subject: [PATCH 1/7] Add v3.1 schema and support validating multiple version. Signed-off-by: Daniel Nephin --- cli/compose/loader/loader.go | 7 +- cli/compose/schema/bindata.go | 27 +- .../schema/data/config_schema_v3.1.json | 426 ++++++++++++++++++ cli/compose/schema/schema.go | 30 +- cli/compose/schema/schema_test.go | 47 +- 5 files changed, 511 insertions(+), 26 deletions(-) create mode 100644 cli/compose/schema/data/config_schema_v3.1.json diff --git a/cli/compose/loader/loader.go b/cli/compose/loader/loader.go index 9e46b97594..c9554a4b44 100644 --- a/cli/compose/loader/loader.go +++ b/cli/compose/loader/loader.go @@ -62,16 +62,11 @@ func Load(configDetails types.ConfigDetails) (*types.Config, error) { } } - if err := schema.Validate(configDict); err != nil { + if err := schema.Validate(configDict, schema.Version(configDict)); err != nil { return nil, err } cfg := types.Config{} - version := configDict["version"].(string) - if version != "3" && version != "3.0" { - return nil, fmt.Errorf(`Unsupported Compose file version: %#v. The only version supported is "3" (or "3.0")`, version) - } - if services, ok := configDict["services"]; ok { servicesConfig, err := interpolation.Interpolate(services.(types.Dict), "service", os.LookupEnv) if err != nil { diff --git a/cli/compose/schema/bindata.go b/cli/compose/schema/bindata.go index c976509355..6d900e0a9a 100644 --- a/cli/compose/schema/bindata.go +++ b/cli/compose/schema/bindata.go @@ -1,6 +1,7 @@ // Code generated by go-bindata. // sources: // data/config_schema_v3.0.json +// data/config_schema_v3.1.json // DO NOT EDIT! package schema @@ -88,6 +89,26 @@ func dataConfig_schema_v30Json() (*asset, error) { return a, nil } +var _dataConfig_schema_v31Json = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xec\x1a\x4d\x93\xdb\xa8\xf2\xee\x5f\xa1\x52\x72\x8b\x67\x26\xaf\x5e\xea\x55\xbd\xdc\xde\xf1\x9d\x76\xcf\x3b\xe5\xa8\xb0\xd4\x96\xc9\x20\x20\x80\x9c\x71\x52\xfe\xef\x5b\xe8\xcb\x80\x41\x60\x5b\xd9\xa4\x6a\xf7\x34\x63\xd1\xdd\xf4\x77\x37\x0d\xdf\x57\x59\x96\xbf\x95\xe5\x1e\x1a\x94\x7f\xcc\xf2\xbd\x52\xfc\xe3\xd3\xd3\x67\xc9\xe8\x43\xff\xf5\x91\x89\xfa\xa9\x12\x68\xa7\x1e\xde\x7f\x78\xea\xbf\xbd\xc9\xd7\x1a\x0f\x57\x1a\xa5\x64\x74\x87\xeb\xa2\x5f\x29\x0e\xff\x7e\xfc\xd7\xa3\x46\xef\x41\xd4\x91\x83\x06\x62\xdb\xcf\x50\xaa\xfe\x9b\x80\x2f\x2d\x16\xa0\x91\x9f\xf3\x03\x08\x89\x19\xcd\x37\xeb\x95\x5e\xe3\x82\x71\x10\x0a\x83\xcc\x3f\x66\x9a\xb9\x2c\x9b\x40\xc6\x0f\x06\x59\xa9\x04\xa6\x75\xde\x7d\x3e\x75\x14\xb2\x2c\x97\x20\x0e\xb8\x34\x28\x4c\xac\xbe\x79\x3a\xd3\x7f\x9a\xc0\xd6\x2e\x55\x83\xd9\xee\x3b\x47\x4a\x81\xa0\xbf\x5f\xf2\xd6\x2d\x7f\x7a\x46\x0f\xdf\xfe\xf7\xf0\xc7\xfb\x87\xff\x3e\x16\x0f\x9b\x77\x6f\xad\x65\xad\x5f\x01\xbb\x7e\xfb\x0a\x76\x98\x62\x85\x19\x9d\xf6\xcf\x27\xc8\xd3\xf0\xdf\x69\xda\x18\x55\x55\x07\x8c\x88\xb5\xf7\x0e\x11\x09\xb6\xcc\x14\xd4\x57\x26\x5e\x62\x32\x4f\x60\x3f\x49\xe6\x61\x7f\x8f\xcc\xb6\x38\x07\x46\xda\x26\x6a\xc1\x11\xea\x27\x09\xd3\x6f\xbf\x8c\xfd\x24\x94\x02\x54\xdc\x65\x7b\xa8\x9f\xe6\xb1\x7a\xfb\xfb\x04\x5e\x8d\x42\xcf\xc2\xf6\x10\xc6\xde\x1d\x83\x56\x78\xfb\x54\xe5\x0b\xaf\xb0\xae\x26\x65\x05\xb4\x54\x01\x27\xec\xa8\xbf\x05\xf4\xd1\x03\x34\x40\x55\x3e\xa9\x20\xcb\xf2\x6d\x8b\x49\xe5\x6a\x94\x51\xf8\x4d\x93\x78\x36\x3e\x66\xd9\x77\x37\x93\x19\x74\xba\x75\xeb\x57\xd8\xe0\xd3\x7a\x40\x96\x69\xbd\x64\x54\xc1\xab\xea\x84\x9a\xdf\xba\x57\x01\x2b\x5f\x40\xec\x30\x81\x54\x0c\x24\x6a\x39\xa3\x32\x82\xa5\x2a\x98\x28\x2a\x5c\xaa\xfc\xe4\xa0\x5f\xd0\x8b\xfb\xd3\x84\x6a\xfc\xda\xac\x3c\x04\xf3\x12\xf1\x02\x55\x95\x25\x07\x12\x02\x1d\xf3\x75\x96\x63\x05\x8d\xf4\x8b\x98\xe5\x2d\xc5\x5f\x5a\xf8\xff\x00\xa2\x44\x0b\x2e\xdd\x4a\x30\xbe\x3c\xe1\x5a\xb0\x96\x17\x1c\x09\xed\x60\xf3\xea\xcf\x4b\xd6\x34\x88\x2e\xe5\x75\xd7\xc8\x91\xa0\x79\x46\x15\xc2\x14\x44\x41\x51\x13\x73\x24\x1d\x75\x40\x2b\x59\xf4\x05\x7f\xd6\x8d\x76\x45\x8f\x2f\x1d\x02\x53\xf5\x5f\xd4\x1e\x15\x9d\x73\xec\x9e\x8c\x76\x6d\xcd\x5b\xee\x20\x16\x12\x90\x28\xf7\x37\xe2\xb3\x06\x61\x9a\xa2\x3b\xa0\x4a\x1c\x39\xc3\xbd\xbf\xfc\x72\x8e\x00\xf4\x50\x4c\xb9\xe4\x6a\x35\x00\x3d\x60\xc1\x68\x33\x46\x43\x4a\x82\x99\x92\xbc\xc6\x7f\xe5\x4c\x82\xab\x18\x47\x40\x73\x69\x12\xd5\xd2\xc9\x88\xf1\x3c\x0a\xbe\xce\x72\xda\x36\x5b\x10\xba\x87\xb5\x20\x77\x4c\x34\x48\x33\x3b\xee\x6d\x2c\x5b\x9a\xf6\x78\x9e\xa9\x40\x53\x06\x5d\xd6\x11\x29\x08\xa6\x2f\xcb\xbb\x38\xbc\x2a\x81\x8a\x3d\x93\x2a\x3d\x87\x1b\xe8\x7b\x40\x44\xed\xcb\x3d\x94\x2f\x33\xe8\x26\x94\x85\xcd\xa4\x4a\x71\x72\xdc\xa0\x3a\x0e\xc4\xcb\x18\x08\x41\x5b\x20\x37\xc9\xb9\xa8\xf2\x0d\xb2\xac\xae\x35\x68\xc8\xe3\x2e\x3a\x97\x61\x39\x56\xf3\x2b\x81\x0f\x20\x52\x0b\x38\xe3\xe7\x86\xcb\x5d\x8c\x37\x20\x59\xbc\xfb\xb4\x40\x3f\x3d\xf6\xcd\xe7\x4c\x54\x75\xff\x11\x92\x6f\xdc\x76\x21\x73\xea\xbe\xef\x8b\x23\x61\x5a\x43\x61\x59\xa5\x41\xa5\xee\x1b\x04\xc8\x80\x5d\xcf\xa0\xc3\xe9\xa6\x68\x58\x15\x72\xd0\x0b\x60\x57\x37\xc1\x4c\x7d\x75\x21\xcc\x6e\xea\x1f\x93\x4c\x17\x3d\x40\x44\xa4\x09\xb1\x97\xca\xe6\x99\xdd\xb8\x8b\x75\x70\x88\x60\x24\x21\x1e\xec\x41\x45\x5a\xd4\x30\x3f\x7c\x48\xf4\x09\x1f\xee\x7f\x66\x71\x03\xa8\x41\x9a\xe9\x3d\x72\x84\xd4\x99\x95\x2e\xdc\x7c\x8c\x6c\x22\xd1\xf6\x83\x5b\x78\x8e\xab\x70\xae\xe8\x32\x84\x19\x60\x9c\x09\x75\x11\x5d\x7f\x4d\xb9\xef\xb7\xbe\xbb\xda\x73\x81\x0f\x98\x40\x0d\xf6\xa9\x65\xcb\x18\x01\x44\xad\xd4\x23\x00\x55\x05\xa3\xe4\x98\x00\x29\x15\x12\xd1\x03\x85\x84\xb2\x15\x58\x1d\x0b\xc6\xd5\xe2\x7d\x86\xdc\x37\x85\xc4\xdf\xc0\xb6\xe6\x39\xdf\x0f\x84\x36\x0e\x43\xce\x84\xe4\x46\x83\x86\x52\x52\x3c\x8c\x3d\x89\x30\x9a\xa8\xe2\x29\x2a\x97\xac\x15\x65\xea\x01\x5b\xef\x89\x44\x0d\xa9\x47\x78\xed\x6e\x76\xd8\xcc\x03\xd7\xd7\x00\x5f\x14\xba\xc1\x84\xb1\xaa\xec\xfe\x36\xf3\xca\xc9\x1b\xfa\xf2\x28\x4b\x75\x5b\xb7\x26\x55\x85\x69\xc1\x38\xd0\x68\x6c\x48\xc5\x78\x21\x71\x4d\x11\x89\xc6\x87\x06\xad\x05\x2a\xa1\xe0\x20\x30\xf3\x6a\x6d\x6d\x26\x85\xaa\x15\x48\xb3\x6a\x91\x51\x0d\xdf\xdd\x78\xac\x54\x2a\x1e\xec\x2d\xc1\x0d\x0e\x07\x8d\xc7\x6b\x13\x3a\x80\xbe\xfa\xfb\x8b\xfe\x4c\xc1\x3f\x73\x8a\xa9\x82\x5a\xbb\xc9\xa5\x53\xcd\xf4\x9c\xf3\x2d\x67\x42\xaf\xb9\x47\xc2\xb6\xd2\x0c\x1f\x59\x1f\x98\x3b\xe5\x47\xf0\x75\xa2\x5e\xbe\xac\xbb\x8e\x8e\xde\x7a\x60\x64\xe3\x85\xbf\xaa\x98\xbb\x6c\x6c\x82\xf5\xd4\x1f\x54\xad\x8c\x1e\x0b\x3a\x18\x2a\xe7\x5a\xda\x09\xd4\x18\xda\x2f\x5a\x2d\x74\x9b\xac\x83\xa0\xc2\x7e\x6e\x57\x8e\x64\x57\x8c\xdd\x9d\x13\xeb\x48\xc0\x37\x4f\x36\x41\xdd\x99\xf2\xf3\xe4\x9b\x63\x27\x72\x9e\xc4\x07\x86\xcb\xda\x95\xc4\xc1\xca\x33\x3e\x9d\x2a\xdc\x00\x6b\x55\x04\x4a\x80\x12\xd8\xd1\xfc\x98\x8a\x4d\x62\x20\x7f\xcd\xc1\x50\x85\x25\xda\x3a\x33\xe6\x29\x9d\xdd\x64\xde\xec\x3c\xc0\x1f\x07\x46\x73\xc6\x35\x20\x17\xb0\x6d\x4a\xb0\x08\xe0\x04\x97\x48\xc6\x12\xd2\x1d\x63\x8a\x96\x57\x48\x41\xd1\xdf\xcf\x5e\x55\x02\x66\x72\x3f\x47\x02\x11\x02\x04\xcb\x26\x25\x97\xe6\x15\x10\x74\xbc\xa9\x36\x76\xe8\x3b\x84\x49\x2b\xa0\x40\xa5\x1a\xae\x80\x23\x9e\x99\x37\x8c\x62\xc5\xbc\x99\x22\x6d\xcb\x06\xbd\x16\xe3\xb6\x1d\x48\xac\xc3\xb1\x9b\xfb\xd4\x09\x83\xe1\x09\x7d\x03\x78\x5d\x95\x9e\x31\xd1\xb9\xe6\x07\x3c\x66\xdc\xf1\x42\x74\x01\x52\x27\xa5\x69\x00\x14\xc5\x8f\x96\x98\xe1\xb4\x51\x70\x46\x70\x79\x5c\x4a\xc2\x92\xd1\x5e\xc9\x29\x0e\x71\xa7\x07\x6a\x77\xd0\x2d\x51\xc3\x55\x34\x58\x3b\x84\xaf\x98\x56\xec\xeb\x15\x1b\x2e\xe7\x4a\x9c\xa0\x12\x9c\x7c\x77\xaf\xa2\xa5\x12\x08\x53\x75\x75\x59\xbf\x57\xac\x3b\xaa\xfa\xe4\x9f\x91\xac\x3f\xc1\xc5\xef\xd3\x03\x99\xbe\xe4\x6d\x74\x2a\xd8\x40\xc3\x84\xd7\x01\x17\x78\xf0\x11\x13\x71\x04\x5b\xa0\xaa\x25\x8d\x91\x07\xa8\x82\xf1\xe5\x4f\x1d\xf1\x51\xf1\x26\x9e\x90\x30\x47\xcd\x52\xd1\x91\x3c\x58\xcf\xbd\x35\x38\x9b\x9f\x5f\x64\xe1\x19\x46\x8c\xeb\x38\xef\x03\x84\x6c\xb7\x34\x30\x4a\xb8\x3c\x6d\xf8\x6e\xfb\xd3\x8f\x2b\xa7\xf0\xe1\xe4\xbe\xa4\x37\xde\x89\x05\xac\xfa\x3c\x75\x92\xeb\x49\x57\x9b\x64\x13\x07\x2f\xa4\x96\xe3\xff\xca\x06\xef\x8e\x9c\x31\x3c\x58\x8a\xa4\x8c\x01\xea\x9f\x8c\xf1\xcb\xf8\xd7\x4c\x51\xbc\xf1\x74\x70\xf5\xcb\xb4\x98\xd3\x0c\x50\x37\x17\xd2\x84\x27\x46\x7f\x7b\x43\xd8\xa3\x40\xc3\x20\x97\x67\xf8\x39\x3d\x26\xdf\x80\x0d\x18\x1b\x9b\x0d\x17\xcc\xf3\x2a\xd7\xae\x65\x73\xa3\x9f\x11\x24\x70\x23\xe2\x6c\x3a\x28\x6f\x5e\xf2\x05\xf3\xc7\xe3\xbb\x99\x8a\x3d\x77\x53\xfd\x83\x4a\xdd\x02\x63\x35\xbf\x4d\x9d\x36\x7f\xd4\xee\xe5\x4b\xcb\x40\xf4\x1b\xf8\x17\xef\x2e\xb5\x9c\xf4\x78\x31\x63\xfa\x6e\x8f\x46\xfb\x37\x93\x1b\x4b\x3f\x0e\x48\xff\xee\xc3\x48\xd8\x1b\xf3\xe4\x13\x32\xa3\xf7\x35\xa6\x3b\x98\x1d\x5f\x45\x06\xee\x21\x56\xe6\xdf\xee\x05\xeb\xea\xb4\xfa\x33\x00\x00\xff\xff\xb7\x14\xdd\xc9\x3a\x2f\x00\x00") + +func dataConfig_schema_v31JsonBytes() ([]byte, error) { + return bindataRead( + _dataConfig_schema_v31Json, + "data/config_schema_v3.1.json", + ) +} + +func dataConfig_schema_v31Json() (*asset, error) { + bytes, err := dataConfig_schema_v31JsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "data/config_schema_v3.1.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + // Asset loads and returns the asset for the given name. // It returns an error if the asset could not be found or // could not be loaded. @@ -141,6 +162,7 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ "data/config_schema_v3.0.json": dataConfig_schema_v30Json, + "data/config_schema_v3.1.json": dataConfig_schema_v31Json, } // AssetDir returns the file names below a certain @@ -183,8 +205,9 @@ type bintree struct { Children map[string]*bintree } var _bintree = &bintree{nil, map[string]*bintree{ - "data": &bintree{nil, map[string]*bintree{ - "config_schema_v3.0.json": &bintree{dataConfig_schema_v30Json, map[string]*bintree{}}, + "data": {nil, map[string]*bintree{ + "config_schema_v3.0.json": {dataConfig_schema_v30Json, map[string]*bintree{}}, + "config_schema_v3.1.json": {dataConfig_schema_v31Json, map[string]*bintree{}}, }}, }} diff --git a/cli/compose/schema/data/config_schema_v3.1.json b/cli/compose/schema/data/config_schema_v3.1.json new file mode 100644 index 0000000000..c43f296b55 --- /dev/null +++ b/cli/compose/schema/data/config_schema_v3.1.json @@ -0,0 +1,426 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "config_schema_v3.1.json", + "type": "object", + "required": ["version"], + + "properties": { + "version": { + "type": "string" + }, + + "services": { + "id": "#/properties/services", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/service" + } + }, + "additionalProperties": false + }, + + "networks": { + "id": "#/properties/networks", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/network" + } + } + }, + + "volumes": { + "id": "#/properties/volumes", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/volume" + } + }, + "additionalProperties": false + }, + + "secrets": { + "id": "#/properties/secrets", + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "$ref": "#/definitions/secret" + } + }, + "additionalProperties": false + } + }, + + "additionalProperties": false, + + "definitions": { + + "service": { + "id": "#/definitions/service", + "type": "object", + + "properties": { + "deploy": {"$ref": "#/definitions/deployment"}, + "build": { + "oneOf": [ + {"type": "string"}, + { + "type": "object", + "properties": { + "context": {"type": "string"}, + "dockerfile": {"type": "string"}, + "args": {"$ref": "#/definitions/list_or_dict"} + }, + "additionalProperties": false + } + ] + }, + "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "cgroup_parent": {"type": "string"}, + "command": { + "oneOf": [ + {"type": "string"}, + {"type": "array", "items": {"type": "string"}} + ] + }, + "container_name": {"type": "string"}, + "depends_on": {"$ref": "#/definitions/list_of_strings"}, + "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "dns": {"$ref": "#/definitions/string_or_list"}, + "dns_search": {"$ref": "#/definitions/string_or_list"}, + "domainname": {"type": "string"}, + "entrypoint": { + "oneOf": [ + {"type": "string"}, + {"type": "array", "items": {"type": "string"}} + ] + }, + "env_file": {"$ref": "#/definitions/string_or_list"}, + "environment": {"$ref": "#/definitions/list_or_dict"}, + + "expose": { + "type": "array", + "items": { + "type": ["string", "number"], + "format": "expose" + }, + "uniqueItems": true + }, + + "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "extra_hosts": {"$ref": "#/definitions/list_or_dict"}, + "healthcheck": {"$ref": "#/definitions/healthcheck"}, + "hostname": {"type": "string"}, + "image": {"type": "string"}, + "ipc": {"type": "string"}, + "labels": {"$ref": "#/definitions/list_or_dict"}, + "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + + "logging": { + "type": "object", + + "properties": { + "driver": {"type": "string"}, + "options": { + "type": "object", + "patternProperties": { + "^.+$": {"type": ["string", "number", "null"]} + } + } + }, + "additionalProperties": false + }, + + "mac_address": {"type": "string"}, + "network_mode": {"type": "string"}, + + "networks": { + "oneOf": [ + {"$ref": "#/definitions/list_of_strings"}, + { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9._-]+$": { + "oneOf": [ + { + "type": "object", + "properties": { + "aliases": {"$ref": "#/definitions/list_of_strings"}, + "ipv4_address": {"type": "string"}, + "ipv6_address": {"type": "string"} + }, + "additionalProperties": false + }, + {"type": "null"} + ] + } + }, + "additionalProperties": false + } + ] + }, + "pid": {"type": ["string", "null"]}, + + "ports": { + "type": "array", + "items": { + "type": ["string", "number"], + "format": "ports" + }, + "uniqueItems": true + }, + + "privileged": {"type": "boolean"}, + "read_only": {"type": "boolean"}, + "restart": {"type": "string"}, + "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "shm_size": {"type": ["number", "string"]}, + "secrets": { + "type": "array", + "items": { + "oneOf": [ + {"type": "string"}, + { + "type": "object", + "properties": { + "source": {"type": "string"}, + "target": {"type": "string"}, + "uid": {"type": "string"}, + "gid": {"type": "string"}, + "mode": {"type": "number"} + } + } + ] + } + }, + "sysctls": {"$ref": "#/definitions/list_or_dict"}, + "stdin_open": {"type": "boolean"}, + "stop_signal": {"type": "string"}, + "stop_grace_period": {"type": "string", "format": "duration"}, + "tmpfs": {"$ref": "#/definitions/string_or_list"}, + "tty": {"type": "boolean"}, + "ulimits": { + "type": "object", + "patternProperties": { + "^[a-z]+$": { + "oneOf": [ + {"type": "integer"}, + { + "type":"object", + "properties": { + "hard": {"type": "integer"}, + "soft": {"type": "integer"} + }, + "required": ["soft", "hard"], + "additionalProperties": false + } + ] + } + } + }, + "user": {"type": "string"}, + "userns_mode": {"type": "string"}, + "volumes": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "working_dir": {"type": "string"} + }, + "additionalProperties": false + }, + + "healthcheck": { + "id": "#/definitions/healthcheck", + "type": ["object", "null"], + "properties": { + "interval": {"type":"string"}, + "timeout": {"type":"string"}, + "retries": {"type": "number"}, + "test": { + "oneOf": [ + {"type": "string"}, + {"type": "array", "items": {"type": "string"}} + ] + }, + "disable": {"type": "boolean"} + }, + "additionalProperties": false + }, + "deployment": { + "id": "#/definitions/deployment", + "type": ["object", "null"], + "properties": { + "mode": {"type": "string"}, + "replicas": {"type": "integer"}, + "labels": {"$ref": "#/definitions/list_or_dict"}, + "update_config": { + "type": "object", + "properties": { + "parallelism": {"type": "integer"}, + "delay": {"type": "string", "format": "duration"}, + "failure_action": {"type": "string"}, + "monitor": {"type": "string", "format": "duration"}, + "max_failure_ratio": {"type": "number"} + }, + "additionalProperties": false + }, + "resources": { + "type": "object", + "properties": { + "limits": {"$ref": "#/definitions/resource"}, + "reservations": {"$ref": "#/definitions/resource"} + } + }, + "restart_policy": { + "type": "object", + "properties": { + "condition": {"type": "string"}, + "delay": {"type": "string", "format": "duration"}, + "max_attempts": {"type": "integer"}, + "window": {"type": "string", "format": "duration"} + }, + "additionalProperties": false + }, + "placement": { + "type": "object", + "properties": { + "constraints": {"type": "array", "items": {"type": "string"}} + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + + "resource": { + "id": "#/definitions/resource", + "type": "object", + "properties": { + "cpus": {"type": "string"}, + "memory": {"type": "string"} + }, + "additionalProperties": false + }, + + "network": { + "id": "#/definitions/network", + "type": ["object", "null"], + "properties": { + "driver": {"type": "string"}, + "driver_opts": { + "type": "object", + "patternProperties": { + "^.+$": {"type": ["string", "number"]} + } + }, + "ipam": { + "type": "object", + "properties": { + "driver": {"type": "string"}, + "config": { + "type": "array", + "items": { + "type": "object", + "properties": { + "subnet": {"type": "string"} + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + }, + "external": { + "type": ["boolean", "object"], + "properties": { + "name": {"type": "string"} + }, + "additionalProperties": false + }, + "labels": {"$ref": "#/definitions/list_or_dict"} + }, + "additionalProperties": false + }, + + "volume": { + "id": "#/definitions/volume", + "type": ["object", "null"], + "properties": { + "driver": {"type": "string"}, + "driver_opts": { + "type": "object", + "patternProperties": { + "^.+$": {"type": ["string", "number"]} + } + }, + "external": { + "type": ["boolean", "object"], + "properties": { + "name": {"type": "string"} + } + } + }, + "labels": {"$ref": "#/definitions/list_or_dict"}, + "additionalProperties": false + }, + + "secret": { + "id": "#/definitions/secret", + "type": "object", + "properties": { + "file": {"type": "string"}, + "external": { + "type": ["boolean", "object"], + "properties": { + "name": {"type": "string"} + } + } + }, + "labels": {"$ref": "#/definitions/list_or_dict"}, + "additionalProperties": false + }, + + "string_or_list": { + "oneOf": [ + {"type": "string"}, + {"$ref": "#/definitions/list_of_strings"} + ] + }, + + "list_of_strings": { + "type": "array", + "items": {"type": "string"}, + "uniqueItems": true + }, + + "list_or_dict": { + "oneOf": [ + { + "type": "object", + "patternProperties": { + ".+": { + "type": ["string", "number", "null"] + } + }, + "additionalProperties": false + }, + {"type": "array", "items": {"type": "string"}, "uniqueItems": true} + ] + }, + + "constraints": { + "service": { + "id": "#/definitions/constraints/service", + "anyOf": [ + {"required": ["build"]}, + {"required": ["image"]} + ], + "properties": { + "build": { + "required": ["context"] + } + } + } + } + } +} diff --git a/cli/compose/schema/schema.go b/cli/compose/schema/schema.go index 6366cab48e..ae33c77fbe 100644 --- a/cli/compose/schema/schema.go +++ b/cli/compose/schema/schema.go @@ -7,9 +7,15 @@ import ( "strings" "time" + "github.com/pkg/errors" "github.com/xeipuuv/gojsonschema" ) +const ( + defaultVersion = "1.0" + versionField = "version" +) + type portsFormatChecker struct{} func (checker portsFormatChecker) IsFormat(input string) bool { @@ -30,11 +36,29 @@ func init() { gojsonschema.FormatCheckers.Add("duration", durationFormatChecker{}) } +// Version returns the version of the config, defaulting to version 1.0 +func Version(config map[string]interface{}) string { + version, ok := config[versionField] + if !ok { + return defaultVersion + } + return normalizeVersion(fmt.Sprintf("%v", version)) +} + +func normalizeVersion(version string) string { + switch version { + case "3": + return "3.0" + default: + return version + } +} + // Validate uses the jsonschema to validate the configuration -func Validate(config map[string]interface{}) error { - schemaData, err := Asset("data/config_schema_v3.0.json") +func Validate(config map[string]interface{}, version string) error { + schemaData, err := Asset(fmt.Sprintf("data/config_schema_v%s.json", version)) if err != nil { - return err + return errors.Errorf("unsupported Compose file version: %s", version) } schemaLoader := gojsonschema.NewStringLoader(string(schemaData)) diff --git a/cli/compose/schema/schema_test.go b/cli/compose/schema/schema_test.go index be98f807de..0935d4022e 100644 --- a/cli/compose/schema/schema_test.go +++ b/cli/compose/schema/schema_test.go @@ -8,7 +8,35 @@ import ( type dict map[string]interface{} -func TestValid(t *testing.T) { +func TestValidate(t *testing.T) { + config := dict{ + "version": "3.0", + "services": dict{ + "foo": dict{ + "image": "busybox", + }, + }, + } + + assert.NoError(t, Validate(config, "3.0")) +} + +func TestValidateUndefinedTopLevelOption(t *testing.T) { + config := dict{ + "version": "3.0", + "helicopters": dict{ + "foo": dict{ + "image": "busybox", + }, + }, + } + + err := Validate(config, "3.0") + assert.Error(t, err) + assert.Contains(t, err.Error(), "Additional property helicopters is not allowed") +} + +func TestValidateInvalidVersion(t *testing.T) { config := dict{ "version": "2.1", "services": dict{ @@ -18,18 +46,7 @@ func TestValid(t *testing.T) { }, } - assert.NoError(t, Validate(config)) -} - -func TestUndefinedTopLevelOption(t *testing.T) { - config := dict{ - "version": "2.1", - "helicopters": dict{ - "foo": dict{ - "image": "busybox", - }, - }, - } - - assert.Error(t, Validate(config)) + err := Validate(config, "2.1") + assert.Error(t, err) + assert.Contains(t, err.Error(), "unsupported Compose file version: 2.1") } From 9419e7df2b54b684bef6c787239a2c92fbb4acda Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Tue, 10 Jan 2017 17:40:53 -0500 Subject: [PATCH 2/7] Implement secret types for compose file. Signed-off-by: Daniel Nephin --- cli/command/service/create.go | 2 +- cli/command/service/parse.go | 4 +- cli/command/service/update.go | 2 +- cli/command/stack/deploy.go | 29 ++++++- cli/compose/convert/compose.go | 27 +++++++ cli/compose/convert/service.go | 39 +++++++++- cli/compose/loader/loader.go | 76 +++++++++++++++---- cli/compose/loader/loader_test.go | 18 +++++ cli/compose/schema/bindata.go | 8 +- .../schema/data/config_schema_v3.1.json | 4 +- cli/compose/types/types.go | 18 +++++ 11 files changed, 201 insertions(+), 26 deletions(-) diff --git a/cli/command/service/create.go b/cli/command/service/create.go index ca2bb089fd..1355c19c65 100644 --- a/cli/command/service/create.go +++ b/cli/command/service/create.go @@ -62,7 +62,7 @@ func runCreate(dockerCli *command.DockerCli, opts *serviceOptions) error { specifiedSecrets := opts.secrets.Value() if len(specifiedSecrets) > 0 { // parse and validate secrets - secrets, err := parseSecrets(apiClient, specifiedSecrets) + secrets, err := ParseSecrets(apiClient, specifiedSecrets) if err != nil { return err } diff --git a/cli/command/service/parse.go b/cli/command/service/parse.go index 6af7e3bb8e..ce9b454edd 100644 --- a/cli/command/service/parse.go +++ b/cli/command/service/parse.go @@ -10,9 +10,9 @@ import ( "golang.org/x/net/context" ) -// parseSecrets retrieves the secrets from the requested names and converts +// ParseSecrets retrieves the secrets from the requested names and converts // them to secret references to use with the spec -func parseSecrets(client client.SecretAPIClient, requestedSecrets []*types.SecretRequestOption) ([]*swarmtypes.SecretReference, error) { +func ParseSecrets(client client.SecretAPIClient, requestedSecrets []*types.SecretRequestOption) ([]*swarmtypes.SecretReference, error) { secretRefs := make(map[string]*swarmtypes.SecretReference) ctx := context.Background() diff --git a/cli/command/service/update.go b/cli/command/service/update.go index df0977d86d..3feef4823a 100644 --- a/cli/command/service/update.go +++ b/cli/command/service/update.go @@ -443,7 +443,7 @@ func getUpdatedSecrets(apiClient client.SecretAPIClient, flags *pflag.FlagSet, s if flags.Changed(flagSecretAdd) { values := flags.Lookup(flagSecretAdd).Value.(*opts.SecretOpt).Value() - addSecrets, err := parseSecrets(apiClient, values) + addSecrets, err := ParseSecrets(apiClient, values) if err != nil { return nil, err } diff --git a/cli/command/stack/deploy.go b/cli/command/stack/deploy.go index 306a583e1e..6856624128 100644 --- a/cli/command/stack/deploy.go +++ b/cli/command/stack/deploy.go @@ -126,7 +126,16 @@ func deployCompose(ctx context.Context, dockerCli *command.DockerCli, opts deplo if err := createNetworks(ctx, dockerCli, namespace, networks); err != nil { return err } - services, err := convert.Services(namespace, config) + + secrets, err := convert.Secrets(namespace, config.Secrets) + if err != nil { + return err + } + if err := createSecrets(ctx, dockerCli, namespace, secrets); err != nil { + return err + } + + services, err := convert.Services(namespace, config, dockerCli.Client()) if err != nil { return err } @@ -211,6 +220,24 @@ func validateExternalNetworks( return nil } +func createSecrets( + ctx context.Context, + dockerCli *command.DockerCli, + namespace convert.Namespace, + secrets []swarm.SecretSpec, +) error { + client := dockerCli.Client() + + for _, secret := range secrets { + fmt.Fprintf(dockerCli.Out(), "Creating secret %s\n", secret.Name) + _, err := client.SecretCreate(ctx, secret) + if err != nil { + return err + } + } + return nil +} + func createNetworks( ctx context.Context, dockerCli *command.DockerCli, diff --git a/cli/compose/convert/compose.go b/cli/compose/convert/compose.go index 532f4c4b29..efcf8a6979 100644 --- a/cli/compose/convert/compose.go +++ b/cli/compose/convert/compose.go @@ -1,8 +1,11 @@ package convert import ( + "io/ioutil" + "github.com/docker/docker/api/types" networktypes "github.com/docker/docker/api/types/network" + "github.com/docker/docker/api/types/swarm" composetypes "github.com/docker/docker/cli/compose/types" ) @@ -82,3 +85,27 @@ func Networks(namespace Namespace, networks networkMap, servicesNetworks map[str return result, externalNetworks } + +// Secrets converts secrets from the Compose type to the engine API type +func Secrets(namespace Namespace, secrets map[string]composetypes.SecretConfig) ([]swarm.SecretSpec, error) { + result := []swarm.SecretSpec{} + for name, secret := range secrets { + if secret.External.External { + continue + } + + data, err := ioutil.ReadFile(secret.File) + if err != nil { + return nil, err + } + + result = append(result, swarm.SecretSpec{ + Annotations: swarm.Annotations{ + Name: namespace.Scope(name), + Labels: AddStackLabel(namespace, secret.Labels), + }, + Data: data, + }) + } + return result, nil +} diff --git a/cli/compose/convert/service.go b/cli/compose/convert/service.go index a245987c8f..78ad308d38 100644 --- a/cli/compose/convert/service.go +++ b/cli/compose/convert/service.go @@ -2,20 +2,26 @@ package convert import ( "fmt" + "os" "time" + "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/swarm" + servicecli "github.com/docker/docker/cli/command/service" composetypes "github.com/docker/docker/cli/compose/types" + "github.com/docker/docker/client" "github.com/docker/docker/opts" runconfigopts "github.com/docker/docker/runconfig/opts" "github.com/docker/go-connections/nat" ) // Services from compose-file types to engine API types +// TODO: fix secrets API so that SecretAPIClient is not required here func Services( namespace Namespace, config *composetypes.Config, + client client.SecretAPIClient, ) (map[string]swarm.ServiceSpec, error) { result := make(map[string]swarm.ServiceSpec) @@ -24,7 +30,12 @@ func Services( networks := config.Networks for _, service := range services { - serviceSpec, err := convertService(namespace, service, networks, volumes) + + secrets, err := convertServiceSecrets(client, namespace, service.Secrets) + if err != nil { + return nil, err + } + serviceSpec, err := convertService(namespace, service, networks, volumes, secrets) if err != nil { return nil, err } @@ -39,6 +50,7 @@ func convertService( service composetypes.ServiceConfig, networkConfigs map[string]composetypes.NetworkConfig, volumes map[string]composetypes.VolumeConfig, + secrets []*swarm.SecretReference, ) (swarm.ServiceSpec, error) { name := namespace.Scope(service.Name) @@ -108,6 +120,7 @@ func convertService( StopGracePeriod: service.StopGracePeriod, TTY: service.Tty, OpenStdin: service.StdinOpen, + Secrets: secrets, }, LogDriver: logDriver, Resources: resources, @@ -163,6 +176,30 @@ func convertServiceNetworks( return nets, nil } +// TODO: fix secrets API so that SecretAPIClient is not required here +func convertServiceSecrets( + client client.SecretAPIClient, + namespace Namespace, + secrets []composetypes.ServiceSecretConfig, +) ([]*swarm.SecretReference, error) { + opts := []*types.SecretRequestOption{} + for _, secret := range secrets { + target := secret.Target + if target == "" { + target = secret.Source + } + opts = append(opts, &types.SecretRequestOption{ + Source: namespace.Scope(secret.Source), + Target: target, + UID: secret.UID, + GID: secret.GID, + Mode: os.FileMode(secret.Mode), + }) + } + + return servicecli.ParseSecrets(client, opts) +} + func convertExtraHosts(extraHosts map[string]string) []string { hosts := []string{} for host, ip := range extraHosts { diff --git a/cli/compose/loader/loader.go b/cli/compose/loader/loader.go index c9554a4b44..a43347f475 100644 --- a/cli/compose/loader/loader.go +++ b/cli/compose/loader/loader.go @@ -109,6 +109,20 @@ func Load(configDetails types.ConfigDetails) (*types.Config, error) { cfg.Volumes = volumesMapping } + if secrets, ok := configDict["secrets"]; ok { + secretsConfig, err := interpolation.Interpolate(secrets.(types.Dict), "secret", os.LookupEnv) + if err != nil { + return nil, err + } + + secretsMapping, err := loadSecrets(secretsConfig, configDetails.WorkingDir) + if err != nil { + return nil, err + } + + cfg.Secrets = secretsMapping + } + return &cfg, nil } @@ -210,13 +224,15 @@ func transformHook( ) (interface{}, error) { switch target { case reflect.TypeOf(types.External{}): - return transformExternal(source, target, data) + return transformExternal(data) case reflect.TypeOf(make(map[string]string, 0)): return transformMapStringString(source, target, data) case reflect.TypeOf(types.UlimitsConfig{}): - return transformUlimits(source, target, data) + return transformUlimits(data) case reflect.TypeOf(types.UnitBytes(0)): return loadSize(data) + case reflect.TypeOf(types.ServiceSecretConfig{}): + return transformServiceSecret(data) } switch target.Kind() { case reflect.Struct: @@ -311,7 +327,7 @@ func resolveEnvironment(serviceConfig *types.ServiceConfig, serviceDict types.Di var envVars []string for _, file := range envFiles { - filePath := path.Join(workingDir, file) + filePath := absPath(workingDir, file) fileVars, err := opts.ParseEnvFile(filePath) if err != nil { return err @@ -341,7 +357,7 @@ func resolveVolumePaths(volumes []string, workingDir string) error { } if strings.HasPrefix(parts[0], ".") { - parts[0] = path.Join(workingDir, parts[0]) + parts[0] = absPath(workingDir, parts[0]) } parts[0] = expandUser(parts[0]) @@ -359,11 +375,7 @@ func expandUser(path string) string { return path } -func transformUlimits( - source reflect.Type, - target reflect.Type, - data interface{}, -) (interface{}, error) { +func transformUlimits(data interface{}) (interface{}, error) { switch value := data.(type) { case int: return types.UlimitsConfig{Single: value}, nil @@ -407,6 +419,32 @@ func loadVolumes(source types.Dict) (map[string]types.VolumeConfig, error) { return volumes, nil } +// TODO: remove duplicate with networks/volumes +func loadSecrets(source types.Dict, workingDir string) (map[string]types.SecretConfig, error) { + secrets := make(map[string]types.SecretConfig) + err := transform(source, &secrets) + if err != nil { + return secrets, err + } + for name, secret := range secrets { + if secret.External.External && secret.External.Name == "" { + secret.External.Name = name + secrets[name] = secret + } + if secret.File != "" { + secret.File = absPath(workingDir, secret.File) + } + } + return secrets, nil +} + +func absPath(workingDir string, filepath string) string { + if path.IsAbs(filepath) { + return filepath + } + return path.Join(workingDir, filepath) +} + func transformStruct( source reflect.Type, target reflect.Type, @@ -490,11 +528,7 @@ func convertField( return data, nil } -func transformExternal( - source reflect.Type, - target reflect.Type, - data interface{}, -) (interface{}, error) { +func transformExternal(data interface{}) (interface{}, error) { switch value := data.(type) { case bool: return map[string]interface{}{"external": value}, nil @@ -507,6 +541,20 @@ func transformExternal( } } +func transformServiceSecret(data interface{}) (interface{}, error) { + switch value := data.(type) { + case string: + return map[string]interface{}{"source": value}, nil + case types.Dict: + return data, nil + case map[string]interface{}: + return data, nil + default: + return data, fmt.Errorf("invalid type %T for external", value) + } + +} + func toYAMLName(name string) string { nameParts := fieldNameRegexp.FindAllString(name, -1) for i, p := range nameParts { diff --git a/cli/compose/loader/loader_test.go b/cli/compose/loader/loader_test.go index e15be7c549..f7fee89ede 100644 --- a/cli/compose/loader/loader_test.go +++ b/cli/compose/loader/loader_test.go @@ -163,6 +163,24 @@ func TestLoad(t *testing.T) { assert.Equal(t, sampleConfig.Volumes, actual.Volumes) } +func TestLoadV31(t *testing.T) { + actual, err := loadYAML(` +version: "3.1" +services: + foo: + image: busybox + secrets: [super] +secrets: + super: + external: true +`) + if !assert.NoError(t, err) { + return + } + assert.Equal(t, len(actual.Services), 1) + assert.Equal(t, len(actual.Secrets), 1) +} + func TestParseAndLoad(t *testing.T) { actual, err := loadYAML(sampleYAML) if !assert.NoError(t, err) { diff --git a/cli/compose/schema/bindata.go b/cli/compose/schema/bindata.go index 6d900e0a9a..3713315b2a 100644 --- a/cli/compose/schema/bindata.go +++ b/cli/compose/schema/bindata.go @@ -89,7 +89,7 @@ func dataConfig_schema_v30Json() (*asset, error) { return a, nil } -var _dataConfig_schema_v31Json = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xec\x1a\x4d\x93\xdb\xa8\xf2\xee\x5f\xa1\x52\x72\x8b\x67\x26\xaf\x5e\xea\x55\xbd\xdc\xde\xf1\x9d\x76\xcf\x3b\xe5\xa8\xb0\xd4\x96\xc9\x20\x20\x80\x9c\x71\x52\xfe\xef\x5b\xe8\xcb\x80\x41\x60\x5b\xd9\xa4\x6a\xf7\x34\x63\xd1\xdd\xf4\x77\x37\x0d\xdf\x57\x59\x96\xbf\x95\xe5\x1e\x1a\x94\x7f\xcc\xf2\xbd\x52\xfc\xe3\xd3\xd3\x67\xc9\xe8\x43\xff\xf5\x91\x89\xfa\xa9\x12\x68\xa7\x1e\xde\x7f\x78\xea\xbf\xbd\xc9\xd7\x1a\x0f\x57\x1a\xa5\x64\x74\x87\xeb\xa2\x5f\x29\x0e\xff\x7e\xfc\xd7\xa3\x46\xef\x41\xd4\x91\x83\x06\x62\xdb\xcf\x50\xaa\xfe\x9b\x80\x2f\x2d\x16\xa0\x91\x9f\xf3\x03\x08\x89\x19\xcd\x37\xeb\x95\x5e\xe3\x82\x71\x10\x0a\x83\xcc\x3f\x66\x9a\xb9\x2c\x9b\x40\xc6\x0f\x06\x59\xa9\x04\xa6\x75\xde\x7d\x3e\x75\x14\xb2\x2c\x97\x20\x0e\xb8\x34\x28\x4c\xac\xbe\x79\x3a\xd3\x7f\x9a\xc0\xd6\x2e\x55\x83\xd9\xee\x3b\x47\x4a\x81\xa0\xbf\x5f\xf2\xd6\x2d\x7f\x7a\x46\x0f\xdf\xfe\xf7\xf0\xc7\xfb\x87\xff\x3e\x16\x0f\x9b\x77\x6f\xad\x65\xad\x5f\x01\xbb\x7e\xfb\x0a\x76\x98\x62\x85\x19\x9d\xf6\xcf\x27\xc8\xd3\xf0\xdf\x69\xda\x18\x55\x55\x07\x8c\x88\xb5\xf7\x0e\x11\x09\xb6\xcc\x14\xd4\x57\x26\x5e\x62\x32\x4f\x60\x3f\x49\xe6\x61\x7f\x8f\xcc\xb6\x38\x07\x46\xda\x26\x6a\xc1\x11\xea\x27\x09\xd3\x6f\xbf\x8c\xfd\x24\x94\x02\x54\xdc\x65\x7b\xa8\x9f\xe6\xb1\x7a\xfb\xfb\x04\x5e\x8d\x42\xcf\xc2\xf6\x10\xc6\xde\x1d\x83\x56\x78\xfb\x54\xe5\x0b\xaf\xb0\xae\x26\x65\x05\xb4\x54\x01\x27\xec\xa8\xbf\x05\xf4\xd1\x03\x34\x40\x55\x3e\xa9\x20\xcb\xf2\x6d\x8b\x49\xe5\x6a\x94\x51\xf8\x4d\x93\x78\x36\x3e\x66\xd9\x77\x37\x93\x19\x74\xba\x75\xeb\x57\xd8\xe0\xd3\x7a\x40\x96\x69\xbd\x64\x54\xc1\xab\xea\x84\x9a\xdf\xba\x57\x01\x2b\x5f\x40\xec\x30\x81\x54\x0c\x24\x6a\x39\xa3\x32\x82\xa5\x2a\x98\x28\x2a\x5c\xaa\xfc\xe4\xa0\x5f\xd0\x8b\xfb\xd3\x84\x6a\xfc\xda\xac\x3c\x04\xf3\x12\xf1\x02\x55\x95\x25\x07\x12\x02\x1d\xf3\x75\x96\x63\x05\x8d\xf4\x8b\x98\xe5\x2d\xc5\x5f\x5a\xf8\xff\x00\xa2\x44\x0b\x2e\xdd\x4a\x30\xbe\x3c\xe1\x5a\xb0\x96\x17\x1c\x09\xed\x60\xf3\xea\xcf\x4b\xd6\x34\x88\x2e\xe5\x75\xd7\xc8\x91\xa0\x79\x46\x15\xc2\x14\x44\x41\x51\x13\x73\x24\x1d\x75\x40\x2b\x59\xf4\x05\x7f\xd6\x8d\x76\x45\x8f\x2f\x1d\x02\x53\xf5\x5f\xd4\x1e\x15\x9d\x73\xec\x9e\x8c\x76\x6d\xcd\x5b\xee\x20\x16\x12\x90\x28\xf7\x37\xe2\xb3\x06\x61\x9a\xa2\x3b\xa0\x4a\x1c\x39\xc3\xbd\xbf\xfc\x72\x8e\x00\xf4\x50\x4c\xb9\xe4\x6a\x35\x00\x3d\x60\xc1\x68\x33\x46\x43\x4a\x82\x99\x92\xbc\xc6\x7f\xe5\x4c\x82\xab\x18\x47\x40\x73\x69\x12\xd5\xd2\xc9\x88\xf1\x3c\x0a\xbe\xce\x72\xda\x36\x5b\x10\xba\x87\xb5\x20\x77\x4c\x34\x48\x33\x3b\xee\x6d\x2c\x5b\x9a\xf6\x78\x9e\xa9\x40\x53\x06\x5d\xd6\x11\x29\x08\xa6\x2f\xcb\xbb\x38\xbc\x2a\x81\x8a\x3d\x93\x2a\x3d\x87\x1b\xe8\x7b\x40\x44\xed\xcb\x3d\x94\x2f\x33\xe8\x26\x94\x85\xcd\xa4\x4a\x71\x72\xdc\xa0\x3a\x0e\xc4\xcb\x18\x08\x41\x5b\x20\x37\xc9\xb9\xa8\xf2\x0d\xb2\xac\xae\x35\x68\xc8\xe3\x2e\x3a\x97\x61\x39\x56\xf3\x2b\x81\x0f\x20\x52\x0b\x38\xe3\xe7\x86\xcb\x5d\x8c\x37\x20\x59\xbc\xfb\xb4\x40\x3f\x3d\xf6\xcd\xe7\x4c\x54\x75\xff\x11\x92\x6f\xdc\x76\x21\x73\xea\xbe\xef\x8b\x23\x61\x5a\x43\x61\x59\xa5\x41\xa5\xee\x1b\x04\xc8\x80\x5d\xcf\xa0\xc3\xe9\xa6\x68\x58\x15\x72\xd0\x0b\x60\x57\x37\xc1\x4c\x7d\x75\x21\xcc\x6e\xea\x1f\x93\x4c\x17\x3d\x40\x44\xa4\x09\xb1\x97\xca\xe6\x99\xdd\xb8\x8b\x75\x70\x88\x60\x24\x21\x1e\xec\x41\x45\x5a\xd4\x30\x3f\x7c\x48\xf4\x09\x1f\xee\x7f\x66\x71\x03\xa8\x41\x9a\xe9\x3d\x72\x84\xd4\x99\x95\x2e\xdc\x7c\x8c\x6c\x22\xd1\xf6\x83\x5b\x78\x8e\xab\x70\xae\xe8\x32\x84\x19\x60\x9c\x09\x75\x11\x5d\x7f\x4d\xb9\xef\xb7\xbe\xbb\xda\x73\x81\x0f\x98\x40\x0d\xf6\xa9\x65\xcb\x18\x01\x44\xad\xd4\x23\x00\x55\x05\xa3\xe4\x98\x00\x29\x15\x12\xd1\x03\x85\x84\xb2\x15\x58\x1d\x0b\xc6\xd5\xe2\x7d\x86\xdc\x37\x85\xc4\xdf\xc0\xb6\xe6\x39\xdf\x0f\x84\x36\x0e\x43\xce\x84\xe4\x46\x83\x86\x52\x52\x3c\x8c\x3d\x89\x30\x9a\xa8\xe2\x29\x2a\x97\xac\x15\x65\xea\x01\x5b\xef\x89\x44\x0d\xa9\x47\x78\xed\x6e\x76\xd8\xcc\x03\xd7\xd7\x00\x5f\x14\xba\xc1\x84\xb1\xaa\xec\xfe\x36\xf3\xca\xc9\x1b\xfa\xf2\x28\x4b\x75\x5b\xb7\x26\x55\x85\x69\xc1\x38\xd0\x68\x6c\x48\xc5\x78\x21\x71\x4d\x11\x89\xc6\x87\x06\xad\x05\x2a\xa1\xe0\x20\x30\xf3\x6a\x6d\x6d\x26\x85\xaa\x15\x48\xb3\x6a\x91\x51\x0d\xdf\xdd\x78\xac\x54\x2a\x1e\xec\x2d\xc1\x0d\x0e\x07\x8d\xc7\x6b\x13\x3a\x80\xbe\xfa\xfb\x8b\xfe\x4c\xc1\x3f\x73\x8a\xa9\x82\x5a\xbb\xc9\xa5\x53\xcd\xf4\x9c\xf3\x2d\x67\x42\xaf\xb9\x47\xc2\xb6\xd2\x0c\x1f\x59\x1f\x98\x3b\xe5\x47\xf0\x75\xa2\x5e\xbe\xac\xbb\x8e\x8e\xde\x7a\x60\x64\xe3\x85\xbf\xaa\x98\xbb\x6c\x6c\x82\xf5\xd4\x1f\x54\xad\x8c\x1e\x0b\x3a\x18\x2a\xe7\x5a\xda\x09\xd4\x18\xda\x2f\x5a\x2d\x74\x9b\xac\x83\xa0\xc2\x7e\x6e\x57\x8e\x64\x57\x8c\xdd\x9d\x13\xeb\x48\xc0\x37\x4f\x36\x41\xdd\x99\xf2\xf3\xe4\x9b\x63\x27\x72\x9e\xc4\x07\x86\xcb\xda\x95\xc4\xc1\xca\x33\x3e\x9d\x2a\xdc\x00\x6b\x55\x04\x4a\x80\x12\xd8\xd1\xfc\x98\x8a\x4d\x62\x20\x7f\xcd\xc1\x50\x85\x25\xda\x3a\x33\xe6\x29\x9d\xdd\x64\xde\xec\x3c\xc0\x1f\x07\x46\x73\xc6\x35\x20\x17\xb0\x6d\x4a\xb0\x08\xe0\x04\x97\x48\xc6\x12\xd2\x1d\x63\x8a\x96\x57\x48\x41\xd1\xdf\xcf\x5e\x55\x02\x66\x72\x3f\x47\x02\x11\x02\x04\xcb\x26\x25\x97\xe6\x15\x10\x74\xbc\xa9\x36\x76\xe8\x3b\x84\x49\x2b\xa0\x40\xa5\x1a\xae\x80\x23\x9e\x99\x37\x8c\x62\xc5\xbc\x99\x22\x6d\xcb\x06\xbd\x16\xe3\xb6\x1d\x48\xac\xc3\xb1\x9b\xfb\xd4\x09\x83\xe1\x09\x7d\x03\x78\x5d\x95\x9e\x31\xd1\xb9\xe6\x07\x3c\x66\xdc\xf1\x42\x74\x01\x52\x27\xa5\x69\x00\x14\xc5\x8f\x96\x98\xe1\xb4\x51\x70\x46\x70\x79\x5c\x4a\xc2\x92\xd1\x5e\xc9\x29\x0e\x71\xa7\x07\x6a\x77\xd0\x2d\x51\xc3\x55\x34\x58\x3b\x84\xaf\x98\x56\xec\xeb\x15\x1b\x2e\xe7\x4a\x9c\xa0\x12\x9c\x7c\x77\xaf\xa2\xa5\x12\x08\x53\x75\x75\x59\xbf\x57\xac\x3b\xaa\xfa\xe4\x9f\x91\xac\x3f\xc1\xc5\xef\xd3\x03\x99\xbe\xe4\x6d\x74\x2a\xd8\x40\xc3\x84\xd7\x01\x17\x78\xf0\x11\x13\x71\x04\x5b\xa0\xaa\x25\x8d\x91\x07\xa8\x82\xf1\xe5\x4f\x1d\xf1\x51\xf1\x26\x9e\x90\x30\x47\xcd\x52\xd1\x91\x3c\x58\xcf\xbd\x35\x38\x9b\x9f\x5f\x64\xe1\x19\x46\x8c\xeb\x38\xef\x03\x84\x6c\xb7\x34\x30\x4a\xb8\x3c\x6d\xf8\x6e\xfb\xd3\x8f\x2b\xa7\xf0\xe1\xe4\xbe\xa4\x37\xde\x89\x05\xac\xfa\x3c\x75\x92\xeb\x49\x57\x9b\x64\x13\x07\x2f\xa4\x96\xe3\xff\xca\x06\xef\x8e\x9c\x31\x3c\x58\x8a\xa4\x8c\x01\xea\x9f\x8c\xf1\xcb\xf8\xd7\x4c\x51\xbc\xf1\x74\x70\xf5\xcb\xb4\x98\xd3\x0c\x50\x37\x17\xd2\x84\x27\x46\x7f\x7b\x43\xd8\xa3\x40\xc3\x20\x97\x67\xf8\x39\x3d\x26\xdf\x80\x0d\x18\x1b\x9b\x0d\x17\xcc\xf3\x2a\xd7\xae\x65\x73\xa3\x9f\x11\x24\x70\x23\xe2\x6c\x3a\x28\x6f\x5e\xf2\x05\xf3\xc7\xe3\xbb\x99\x8a\x3d\x77\x53\xfd\x83\x4a\xdd\x02\x63\x35\xbf\x4d\x9d\x36\x7f\xd4\xee\xe5\x4b\xcb\x40\xf4\x1b\xf8\x17\xef\x2e\xb5\x9c\xf4\x78\x31\x63\xfa\x6e\x8f\x46\xfb\x37\x93\x1b\x4b\x3f\x0e\x48\xff\xee\xc3\x48\xd8\x1b\xf3\xe4\x13\x32\xa3\xf7\x35\xa6\x3b\x98\x1d\x5f\x45\x06\xee\x21\x56\xe6\xdf\xee\x05\xeb\xea\xb4\xfa\x33\x00\x00\xff\xff\xb7\x14\xdd\xc9\x3a\x2f\x00\x00") +var _dataConfig_schema_v31Json = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xec\x1a\x4d\x93\xdb\xa8\xf2\xee\x5f\xa1\x52\x72\x8b\x67\x26\xaf\x5e\xea\x55\xbd\xdc\xf6\xb8\xa7\xdd\xf3\x4e\x39\x2a\x46\x6a\xcb\x64\x10\x10\x40\xce\x38\x29\xff\xf7\x2d\xf4\x65\xc0\x20\xb0\xad\xec\xcc\x61\x4f\x33\x16\xdd\x4d\x7f\x77\xd3\xf0\x73\x95\x65\xf9\x7b\x59\xee\xa0\x41\xf9\xe7\x2c\xdf\x29\xc5\x3f\x3f\x3c\x7c\x95\x8c\xde\xf5\x5f\xef\x99\xa8\x1f\x2a\x81\xb6\xea\xee\xe3\xa7\x87\xfe\xdb\xbb\x7c\xad\xf1\x70\xa5\x51\x4a\x46\xb7\xb8\x2e\xfa\x95\x62\xff\xdf\xfb\xff\xdc\x6b\xf4\x1e\x44\x1d\x38\x68\x20\xf6\xf4\x15\x4a\xd5\x7f\x13\xf0\xad\xc5\x02\x34\xf2\x63\xbe\x07\x21\x31\xa3\xf9\x66\xbd\xd2\x6b\x5c\x30\x0e\x42\x61\x90\xf9\xe7\x4c\x33\x97\x65\x13\xc8\xf8\xc1\x20\x2b\x95\xc0\xb4\xce\xbb\xcf\xc7\x8e\x42\x96\xe5\x12\xc4\x1e\x97\x06\x85\x89\xd5\x77\x0f\x27\xfa\x0f\x13\xd8\xda\xa5\x6a\x30\xdb\x7d\xe7\x48\x29\x10\xf4\xcf\x73\xde\xba\xe5\x2f\x8f\xe8\xee\xc7\x6f\x77\x7f\x7d\xbc\xfb\xff\x7d\x71\xb7\xf9\xf0\xde\x5a\xd6\xfa\x15\xb0\xed\xb7\xaf\x60\x8b\x29\x56\x98\xd1\x69\xff\x7c\x82\x3c\x0e\xff\x1d\xa7\x8d\x51\x55\x75\xc0\x88\x58\x7b\x6f\x11\x91\x60\xcb\x4c\x41\x7d\x67\xe2\x39\x26\xf3\x04\xf6\x4a\x32\x0f\xfb\x7b\x64\xb6\xc5\xd9\x33\xd2\x36\x51\x0b\x8e\x50\xaf\x24\x4c\xbf\xfd\x32\xf6\x93\x50\x0a\x50\x71\x97\xed\xa1\x5e\xcd\x63\xf5\xf6\xb7\x09\xbc\x1a\x85\x9e\x85\xed\x21\x8c\xbd\x3b\x06\xad\xf0\xf6\xa9\xca\x17\x5e\x61\x5d\x4d\xca\x0a\x68\xa9\x02\x4e\xd8\x41\x7f\x0b\xe8\xa3\x07\x68\x80\xaa\x7c\x52\x41\x96\xe5\x4f\x2d\x26\x95\xab\x51\x46\xe1\x0f\x4d\xe2\xd1\xf8\x98\x65\x3f\xdd\x4c\x66\xd0\xe9\xd6\xad\x5f\x61\x83\x4f\xeb\x01\x59\xa6\xf5\x92\x51\x05\x2f\xaa\x13\x6a\x7e\xeb\x5e\x05\xac\x7c\x06\xb1\xc5\x04\x52\x31\x90\xa8\xe5\x8c\xca\x08\x96\xaa\x60\xa2\xa8\x70\xa9\xf2\xa3\x83\x7e\x46\x2f\xee\x4f\x13\xaa\xf1\x6b\xb3\xf2\x10\xcc\x4b\xc4\x0b\x54\x55\x96\x1c\x48\x08\x74\xc8\xd7\x59\x8e\x15\x34\xd2\x2f\x62\x96\xb7\x14\x7f\x6b\xe1\xf7\x01\x44\x89\x16\x5c\xba\x95\x60\x7c\x79\xc2\xb5\x60\x2d\x2f\x38\x12\xda\xc1\xe6\xd5\x9f\x97\xac\x69\x10\x5d\xca\xeb\x2e\x91\x23\x41\xf3\x8c\x2a\x84\x29\x88\x82\xa2\x26\xe6\x48\x3a\xea\x80\x56\xb2\xe8\x0b\xfe\xac\x1b\x6d\x8b\x1e\x5f\x3a\x04\xa6\xea\xbf\xa8\x3d\x2a\x3a\xe7\xd8\x3d\x19\xed\xda\x9a\xb7\xdc\x41\x2c\x24\x20\x51\xee\xae\xc4\x67\x0d\xc2\x34\x45\x77\x40\x95\x38\x70\x86\x7b\x7f\x79\x73\x8e\x00\x74\x5f\x4c\xb9\xe4\x62\x35\x00\xdd\x63\xc1\x68\x33\x46\x43\x4a\x82\x99\x92\xbc\xc6\x7f\xe1\x4c\x82\xab\x18\x47\x40\x73\x69\x12\xd5\xd2\xc9\x88\xf1\x38\x0a\xbe\xce\x72\xda\x36\x4f\x20\x74\x0f\x6b\x41\x6e\x99\x68\x90\x66\x76\xdc\xdb\x58\xb6\x34\xed\xf1\x3c\x53\x81\xa6\x0c\xba\xac\x23\x52\x10\x4c\x9f\x97\x77\x71\x78\x51\x02\x15\x3b\x26\x55\x7a\x0e\x37\xd0\x77\x80\x88\xda\x95\x3b\x28\x9f\x67\xd0\x4d\x28\x0b\x9b\x49\x95\xe2\xe4\xb8\x41\x75\x1c\x88\x97\x31\x10\x82\x9e\x80\x5c\x25\xe7\xa2\xca\x37\xc8\xb2\xba\xd6\xa0\x21\x8f\x3b\xeb\x5c\x86\xe5\x58\xcd\xaf\x04\xde\x83\x48\x2d\xe0\x8c\x9f\x1a\x2e\x77\x31\xde\x80\x64\xf1\xee\xd3\x02\xfd\x72\xdf\x37\x9f\x33\x51\xd5\xfd\x47\x48\xbe\x71\xdb\x85\xcc\xa9\xfb\xbe\x2f\x8e\x84\x69\x0d\x85\x65\x95\x06\x95\xba\x6f\x10\x20\x03\x76\x3d\x81\x0e\xa7\x9b\xa2\x61\x55\xc8\x41\xcf\x80\x5d\xdd\x04\x33\xf5\xc5\x85\x30\xbb\xaa\x7f\x4c\x32\x5d\xf4\x00\x11\x91\x26\xc4\x5e\x2a\x9b\x27\x76\xe3\x2e\xd6\xc1\x21\x82\x91\x84\x78\xb0\x07\x15\x69\x51\xc3\x7c\xff\x29\xd1\x27\x7c\xb8\xff\x9b\xc5\x0d\xa0\x06\x69\xa6\xf7\xc8\x11\x52\x27\x56\xba\x70\xf3\x31\xb2\x89\x44\xdb\x2f\x6e\xe1\x39\xae\xc2\xb9\xa2\xcb\x10\x66\x80\x71\x26\xd4\x59\x74\xfd\x33\xe5\xbe\xdf\xfa\xe6\x6a\xcf\x05\xde\x63\x02\x35\xd8\xa7\x96\x27\xc6\x08\x20\x6a\xa5\x1e\x01\xa8\x2a\x18\x25\x87\x04\x48\xa9\x90\x88\x1e\x28\x24\x94\xad\xc0\xea\x50\x30\xae\x16\xef\x33\xe4\xae\x29\x24\xfe\x01\xb6\x35\x4f\xf9\x7e\x20\xb4\x71\x18\x72\x26\x24\x57\x1a\x34\x94\x92\xe2\x61\xec\x49\x84\xd1\x44\x15\x4f\x51\xb9\x64\xad\x28\x53\x0f\xd8\x7a\x4f\x24\x6a\x48\x3d\xc2\x6b\x77\xb3\xc3\x66\x1e\xb8\xbe\x04\xf8\xac\xd0\x0d\x26\x8c\x55\x65\xf7\xb7\x99\x57\x8e\xde\xd0\x97\x07\x59\xaa\xeb\xba\x35\xa9\x2a\x4c\x0b\xc6\x81\x46\x63\x43\x2a\xc6\x0b\x89\x6b\x8a\x48\x34\x3e\x34\x68\x2d\x50\x09\x05\x07\x81\x99\x57\x6b\x6b\x33\x29\x54\xad\x40\x9a\x55\x8b\x8c\x6a\xf8\xf6\xca\x63\xa5\x52\xf1\x60\x6f\x09\x6e\x70\x38\x68\x3c\x5e\x9b\xd0\x01\xf4\xd5\xdf\x5f\xf4\x67\x0a\xfe\x89\x53\x4c\x15\xd4\xda\x4d\xce\x9d\x6a\xa6\xe7\x9c\x6f\x39\x13\x7a\xcd\x1d\x12\xb6\x95\x66\xf8\xc8\xfa\xc0\xdc\x2a\x3f\x82\xaf\x13\xf5\xf2\x65\xdd\x75\x74\xf4\xd6\x03\x23\x1b\x2f\xfc\x45\xc5\xdc\x65\x63\x13\xac\xa7\xfe\xa0\x6a\x65\xf4\x58\xd0\xc1\x50\x39\xd7\xd2\x4e\xa0\xc6\xd0\x7e\xd1\x6a\xa1\xdb\x64\x1d\x04\x15\xf6\x73\xbb\x72\x24\xbb\x60\xec\xee\x9c\x58\x47\x02\xbe\x79\xb2\x09\xea\xce\x94\x1f\x27\xdf\x1c\x3b\x91\xd3\x24\x3e\x30\x5c\xd6\xae\x24\xf6\x56\x9e\xf1\xe9\x54\xe1\x06\x58\xab\x22\x50\x02\x94\xc0\x8e\xe6\xc7\x54\x6c\x12\x03\xf9\x36\x07\x43\x15\x96\xe8\xc9\x99\x31\x4f\xe9\xec\x2a\xf3\x66\xa7\x01\xfe\x38\x30\x9a\x33\xae\x01\xb9\x80\x6d\x53\x82\x45\x00\x27\xb8\x44\x32\x96\x90\x6e\x18\x53\xb4\xbc\x42\x0a\x8a\xfe\x7e\xf6\xa2\x12\x30\x93\xfb\x39\x12\x88\x10\x20\x58\x36\x29\xb9\x34\xaf\x80\xa0\xc3\x55\xb5\xb1\x43\xdf\x22\x4c\x5a\x01\x05\x2a\xd5\x70\x05\x1c\xf1\xcc\xbc\x61\x14\x2b\xe6\xcd\x14\x69\x5b\x36\xe8\xa5\x18\xb7\xed\x40\x62\x1d\x8e\xdd\xdc\xa7\x4e\x18\x0c\x4f\xe8\x1b\xc0\xcb\xaa\xf4\x8c\x89\x4e\x35\x3f\xe0\x31\xe3\x8e\x67\xa2\x0b\x90\x3a\x29\x4d\x03\xa0\x28\x7e\xb4\xc4\x0c\xa7\x8d\x82\x33\x82\xcb\xc3\x52\x12\x96\x8c\xf6\x4a\x4e\x71\x88\x1b\x3d\x50\xbb\x83\x6e\x89\x1a\xae\xa2\xc1\xda\x21\x7c\xc7\xb4\x62\xdf\x2f\xd8\x70\x39\x57\xe2\x04\x95\xe0\xe4\xbb\x5b\x15\x2d\x95\x40\x98\xaa\x8b\xcb\xfa\xad\x62\xdd\x50\xd5\x27\xff\x8c\x64\xfd\x09\x2e\x7e\x9f\x1e\xc8\xf4\x25\x6f\xa3\x53\xc1\x06\x1a\x26\xbc\x0e\xb8\xc0\x83\x8f\x98\x88\x23\xd8\x02\x55\x2d\x69\x8c\x3c\x40\x15\x8c\x2f\x7f\xea\x88\x8f\x8a\x37\xf1\x84\x84\x39\x6a\x96\x8a\x8e\xe4\xc1\x7a\xee\xad\xc1\xd9\xfc\xfc\x22\x0b\xcf\x30\x62\x5c\xc7\x79\x1f\x20\x64\xfb\x44\x03\xa3\x84\xf3\xd3\x86\xef\xb6\x3f\xfd\xb8\x72\x0c\x1f\x4e\x6e\x4b\x7a\xe3\x9d\x58\xc0\xaa\x8f\x53\x27\xb9\x9e\x74\xb5\x49\x36\x71\xf0\x42\x6a\x39\xfe\x2f\x6c\xf0\x6e\xc8\x19\xc3\x83\xa5\x48\xca\x18\xa0\xfe\xcd\x18\x6f\xc6\xbf\x66\x8a\xe2\x95\xa7\x83\x8b\x5f\xa6\xc5\x9c\x66\x80\xba\xba\x90\x26\x3c\x31\x7a\x53\x86\x78\x95\xf8\x75\x86\x81\x86\x49\xce\x4f\xf1\x73\x9a\x4c\xbe\x03\x1b\x30\x36\x36\x1b\x2e\x98\xe7\x5d\xae\x5d\xcd\xe6\x86\x3f\x23\x48\xe0\x4e\xc4\xd9\x74\x50\xe2\xbc\xe4\x0b\x66\x90\xfb\x0f\x33\x35\x7b\xee\xae\xfa\x17\x15\xbb\x05\x06\x6b\x7e\x9b\x3a\x8d\xfe\xa8\xdd\xf3\xb7\x96\x81\xf8\x37\xf0\xcf\x5e\x5e\x6a\x39\xe9\xe1\x6c\xca\xf4\xd3\x1e\x8e\xf6\xaf\x26\x37\x96\x7e\x1c\x90\xfe\xe5\x87\x91\xb2\x37\xe6\xd9\x27\x64\x46\xef\x7b\x4c\x77\x34\x3b\xbe\x8b\x0c\xdc\x44\xac\xcc\xbf\xdd\x1b\xd6\xd5\x71\xf5\x77\x00\x00\x00\xff\xff\xc8\x0f\x22\x69\x3c\x2f\x00\x00") func dataConfig_schema_v31JsonBytes() ([]byte, error) { return bindataRead( @@ -205,9 +205,9 @@ type bintree struct { Children map[string]*bintree } var _bintree = &bintree{nil, map[string]*bintree{ - "data": {nil, map[string]*bintree{ - "config_schema_v3.0.json": {dataConfig_schema_v30Json, map[string]*bintree{}}, - "config_schema_v3.1.json": {dataConfig_schema_v31Json, map[string]*bintree{}}, + "data": &bintree{nil, map[string]*bintree{ + "config_schema_v3.0.json": &bintree{dataConfig_schema_v30Json, map[string]*bintree{}}, + "config_schema_v3.1.json": &bintree{dataConfig_schema_v31Json, map[string]*bintree{}}, }}, }} diff --git a/cli/compose/schema/data/config_schema_v3.1.json b/cli/compose/schema/data/config_schema_v3.1.json index c43f296b55..b67203218a 100644 --- a/cli/compose/schema/data/config_schema_v3.1.json +++ b/cli/compose/schema/data/config_schema_v3.1.json @@ -374,9 +374,9 @@ "properties": { "name": {"type": "string"} } - } + }, + "labels": {"$ref": "#/definitions/list_or_dict"} }, - "labels": {"$ref": "#/definitions/list_or_dict"}, "additionalProperties": false }, diff --git a/cli/compose/types/types.go b/cli/compose/types/types.go index 3f2f038834..d70d01ed29 100644 --- a/cli/compose/types/types.go +++ b/cli/compose/types/types.go @@ -71,6 +71,7 @@ type Config struct { Services []ServiceConfig Networks map[string]NetworkConfig Volumes map[string]VolumeConfig + Secrets map[string]SecretConfig } // ServiceConfig is the configuration of one service @@ -108,6 +109,7 @@ type ServiceConfig struct { Privileged bool ReadOnly bool `mapstructure:"read_only"` Restart string + Secrets []ServiceSecretConfig SecurityOpt []string `mapstructure:"security_opt"` StdinOpen bool `mapstructure:"stdin_open"` StopGracePeriod *time.Duration `mapstructure:"stop_grace_period"` @@ -191,6 +193,15 @@ type ServiceNetworkConfig struct { Ipv6Address string `mapstructure:"ipv6_address"` } +// ServiceSecretConfig is the secret configuration for a service +type ServiceSecretConfig struct { + Source string + Target string + UID string + GID string + Mode uint32 +} + // UlimitsConfig the ulimit configuration type UlimitsConfig struct { Single int @@ -233,3 +244,10 @@ type External struct { Name string External bool } + +// SecretConfig for a secret +type SecretConfig struct { + File string + External External + Labels map[string]string `compose:"list_or_dict_equals"` +} From 6ec84ef76df30663d5728f903b314f4486587135 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Fri, 13 Jan 2017 11:26:29 -0500 Subject: [PATCH 3/7] Add integration test for stack deploy with secrets. Signed-off-by: Daniel Nephin --- cli/command/secret/utils.go | 5 ++- cli/command/stack/deploy.go | 27 ++++++++---- cli/compose/convert/compose_test.go | 32 ++++++++++++++ client/secret_update.go | 3 +- integration-cli/docker_cli_stack_test.go | 44 ++++++++++++++++++-- integration-cli/fixtures/deploy/secrets.yaml | 16 +++++++ integration-cli/fixtures/secrets/default | 1 + 7 files changed, 113 insertions(+), 15 deletions(-) create mode 100644 integration-cli/fixtures/deploy/secrets.yaml create mode 100644 integration-cli/fixtures/secrets/default diff --git a/cli/command/secret/utils.go b/cli/command/secret/utils.go index 42493896ca..11d31ffd16 100644 --- a/cli/command/secret/utils.go +++ b/cli/command/secret/utils.go @@ -11,7 +11,8 @@ import ( "golang.org/x/net/context" ) -func getSecretsByNameOrIDPrefixes(ctx context.Context, client client.APIClient, terms []string) ([]swarm.Secret, error) { +// GetSecretsByNameOrIDPrefixes returns secrets given a list of ids or names +func GetSecretsByNameOrIDPrefixes(ctx context.Context, client client.APIClient, terms []string) ([]swarm.Secret, error) { args := filters.NewArgs() for _, n := range terms { args.Add("names", n) @@ -24,7 +25,7 @@ func getSecretsByNameOrIDPrefixes(ctx context.Context, client client.APIClient, } func getCliRequestedSecretIDs(ctx context.Context, client client.APIClient, terms []string) ([]string, error) { - secrets, err := getSecretsByNameOrIDPrefixes(ctx, client, terms) + secrets, err := GetSecretsByNameOrIDPrefixes(ctx, client, terms) if err != nil { return nil, err } diff --git a/cli/command/stack/deploy.go b/cli/command/stack/deploy.go index 6856624128..203ae6d39c 100644 --- a/cli/command/stack/deploy.go +++ b/cli/command/stack/deploy.go @@ -1,24 +1,24 @@ package stack import ( - "errors" "fmt" "io/ioutil" "os" "sort" "strings" - "github.com/spf13/cobra" - "golang.org/x/net/context" - "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" + secretcli "github.com/docker/docker/cli/command/secret" "github.com/docker/docker/cli/compose/convert" "github.com/docker/docker/cli/compose/loader" composetypes "github.com/docker/docker/cli/compose/types" dockerclient "github.com/docker/docker/client" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "golang.org/x/net/context" ) const ( @@ -228,9 +228,22 @@ func createSecrets( ) error { client := dockerCli.Client() - for _, secret := range secrets { - fmt.Fprintf(dockerCli.Out(), "Creating secret %s\n", secret.Name) - _, err := client.SecretCreate(ctx, secret) + for _, secretSpec := range secrets { + // TODO: fix this after https://github.com/docker/docker/pull/29218 + secrets, err := secretcli.GetSecretsByNameOrIDPrefixes(ctx, client, []string{secretSpec.Name}) + switch { + case err != nil: + return err + case len(secrets) > 1: + return errors.Errorf("ambiguous secret name: %s", secretSpec.Name) + case len(secrets) == 0: + fmt.Fprintf(dockerCli.Out(), "Creating secret %s\n", secretSpec.Name) + _, err = client.SecretCreate(ctx, secretSpec) + default: + secret := secrets[0] + // Update secret to ensure that the local data hasn't changed + err = client.SecretUpdate(ctx, secret.ID, secret.Meta.Version, secretSpec) + } if err != nil { return err } diff --git a/cli/compose/convert/compose_test.go b/cli/compose/convert/compose_test.go index d88ac7f7c4..18c7aac938 100644 --- a/cli/compose/convert/compose_test.go +++ b/cli/compose/convert/compose_test.go @@ -7,6 +7,7 @@ import ( "github.com/docker/docker/api/types/network" composetypes "github.com/docker/docker/cli/compose/types" "github.com/docker/docker/pkg/testutil/assert" + "github.com/docker/docker/pkg/testutil/tempfile" ) func TestNamespaceScope(t *testing.T) { @@ -88,3 +89,34 @@ func TestNetworks(t *testing.T) { assert.DeepEqual(t, networks, expected) assert.DeepEqual(t, externals, []string{"special"}) } + +func TestSecrets(t *testing.T) { + namespace := Namespace{name: "foo"} + + secretText := "this is the first secret" + secretFile := tempfile.NewTempFile(t, "convert-secrets", secretText) + defer secretFile.Remove() + + source := map[string]composetypes.SecretConfig{ + "one": { + File: secretFile.Name(), + Labels: map[string]string{"monster": "mash"}, + }, + "ext": { + External: composetypes.External{ + External: true, + }, + }, + } + + specs, err := Secrets(namespace, source) + assert.NilError(t, err) + assert.Equal(t, len(specs), 1) + secret := specs[0] + assert.Equal(t, secret.Name, "foo_one") + assert.DeepEqual(t, secret.Labels, map[string]string{ + "monster": "mash", + LabelNamespace: "foo", + }) + assert.DeepEqual(t, secret.Data, []byte(secretText)) +} diff --git a/client/secret_update.go b/client/secret_update.go index b94e24aab0..42cdbbe176 100644 --- a/client/secret_update.go +++ b/client/secret_update.go @@ -8,8 +8,7 @@ import ( "golang.org/x/net/context" ) -// SecretUpdate updates a Secret. Currently, the only part of a secret spec -// which can be updated is Labels. +// SecretUpdate attempts to updates a Secret func (cli *Client) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error { query := url.Values{} query.Set("version", strconv.FormatUint(version.Index, 10)) diff --git a/integration-cli/docker_cli_stack_test.go b/integration-cli/docker_cli_stack_test.go index 2354e1aafa..03e1e5f08c 100644 --- a/integration-cli/docker_cli_stack_test.go +++ b/integration-cli/docker_cli_stack_test.go @@ -1,15 +1,17 @@ package main import ( + "encoding/json" "io/ioutil" "os" + "sort" + "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/integration-cli/checker" "github.com/go-check/check" ) func (s *DockerSwarmSuite) TestStackRemove(c *check.C) { - testRequires(c, ExperimentalDaemon) d := s.AddDaemon(c, true, true) stackArgs := append([]string{"stack", "remove", "UNKNOWN_STACK"}) @@ -20,7 +22,6 @@ func (s *DockerSwarmSuite) TestStackRemove(c *check.C) { } func (s *DockerSwarmSuite) TestStackTasks(c *check.C) { - testRequires(c, ExperimentalDaemon) d := s.AddDaemon(c, true, true) stackArgs := append([]string{"stack", "ps", "UNKNOWN_STACK"}) @@ -31,7 +32,6 @@ func (s *DockerSwarmSuite) TestStackTasks(c *check.C) { } func (s *DockerSwarmSuite) TestStackServices(c *check.C) { - testRequires(c, ExperimentalDaemon) d := s.AddDaemon(c, true, true) stackArgs := append([]string{"stack", "services", "UNKNOWN_STACK"}) @@ -42,7 +42,6 @@ func (s *DockerSwarmSuite) TestStackServices(c *check.C) { } func (s *DockerSwarmSuite) TestStackDeployComposeFile(c *check.C) { - testRequires(c, ExperimentalDaemon) d := s.AddDaemon(c, true, true) testStackName := "testdeploy" @@ -65,6 +64,43 @@ func (s *DockerSwarmSuite) TestStackDeployComposeFile(c *check.C) { c.Assert(out, check.Equals, "NAME SERVICES\n") } +func (s *DockerSwarmSuite) TestStackDeployWithSecretsTwice(c *check.C) { + d := s.AddDaemon(c, true, true) + + testStackName := "testdeploy" + stackArgs := []string{ + "stack", "deploy", + "--compose-file", "fixtures/deploy/secrets.yaml", + testStackName, + } + out, err := d.Cmd(stackArgs...) + c.Assert(err, checker.IsNil, check.Commentf(out)) + + out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", "testdeploy_web") + c.Assert(err, checker.IsNil) + + var refs []swarm.SecretReference + c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil) + c.Assert(refs, checker.HasLen, 2) + + sort.Sort(sortSecrets(refs)) + c.Assert(refs[0].SecretName, checker.Equals, "testdeploy_special") + c.Assert(refs[0].File.Name, checker.Equals, "special") + c.Assert(refs[1].SecretName, checker.Equals, "testdeploy_super") + c.Assert(refs[1].File.Name, checker.Equals, "foo.txt") + c.Assert(refs[1].File.Mode, checker.Equals, os.FileMode(0400)) + + // Deploy again to ensure there are no errors when secret hasn't changed + out, err = d.Cmd(stackArgs...) + c.Assert(err, checker.IsNil, check.Commentf(out)) +} + +type sortSecrets []swarm.SecretReference + +func (s sortSecrets) Len() int { return len(s) } +func (s sortSecrets) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s sortSecrets) Less(i, j int) bool { return s[i].SecretName < s[j].SecretName } + // testDAB is the DAB JSON used for testing. // TODO: Use template/text and substitute "Image" with the result of // `docker inspect --format '{{index .RepoDigests 0}}' busybox:latest` diff --git a/integration-cli/fixtures/deploy/secrets.yaml b/integration-cli/fixtures/deploy/secrets.yaml new file mode 100644 index 0000000000..965260f8cd --- /dev/null +++ b/integration-cli/fixtures/deploy/secrets.yaml @@ -0,0 +1,16 @@ + +version: "3.1" +services: + web: + image: busybox@sha256:e4f93f6ed15a0cdd342f5aae387886fba0ab98af0a102da6276eaf24d6e6ade0 + command: top + secrets: + - special + - source: super + target: foo.txt + mode: 0400 +secrets: + special: + file: fixtures/secrets/default + super: + file: fixtures/secrets/default diff --git a/integration-cli/fixtures/secrets/default b/integration-cli/fixtures/secrets/default new file mode 100644 index 0000000000..e8ca7d8874 --- /dev/null +++ b/integration-cli/fixtures/secrets/default @@ -0,0 +1 @@ +this is the secret From b3427e43edc56824f762e964c955b906fa363a3a Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Wed, 18 Jan 2017 13:06:36 -0500 Subject: [PATCH 4/7] Test and fix external secrets in stack deploy. Signed-off-by: Daniel Nephin --- cli/compose/convert/service.go | 12 ++++++++-- cli/compose/loader/loader.go | 3 +-- integration-cli/docker_cli_stack_test.go | 24 ++++++++++++-------- integration-cli/fixtures/deploy/secrets.yaml | 4 ++++ 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/cli/compose/convert/service.go b/cli/compose/convert/service.go index 78ad308d38..573f7723fd 100644 --- a/cli/compose/convert/service.go +++ b/cli/compose/convert/service.go @@ -31,7 +31,7 @@ func Services( for _, service := range services { - secrets, err := convertServiceSecrets(client, namespace, service.Secrets) + secrets, err := convertServiceSecrets(client, namespace, service.Secrets, config.Secrets) if err != nil { return nil, err } @@ -181,6 +181,7 @@ func convertServiceSecrets( client client.SecretAPIClient, namespace Namespace, secrets []composetypes.ServiceSecretConfig, + secretSpecs map[string]composetypes.SecretConfig, ) ([]*swarm.SecretReference, error) { opts := []*types.SecretRequestOption{} for _, secret := range secrets { @@ -188,8 +189,15 @@ func convertServiceSecrets( if target == "" { target = secret.Source } + + source := namespace.Scope(secret.Source) + secretSpec := secretSpecs[secret.Source] + if secretSpec.External.External { + source = secretSpec.External.Name + } + opts = append(opts, &types.SecretRequestOption{ - Source: namespace.Scope(secret.Source), + Source: source, Target: target, UID: secret.UID, GID: secret.GID, diff --git a/cli/compose/loader/loader.go b/cli/compose/loader/loader.go index a43347f475..39f69a03ff 100644 --- a/cli/compose/loader/loader.go +++ b/cli/compose/loader/loader.go @@ -422,8 +422,7 @@ func loadVolumes(source types.Dict) (map[string]types.VolumeConfig, error) { // TODO: remove duplicate with networks/volumes func loadSecrets(source types.Dict, workingDir string) (map[string]types.SecretConfig, error) { secrets := make(map[string]types.SecretConfig) - err := transform(source, &secrets) - if err != nil { + if err := transform(source, &secrets); err != nil { return secrets, err } for name, secret := range secrets { diff --git a/integration-cli/docker_cli_stack_test.go b/integration-cli/docker_cli_stack_test.go index 03e1e5f08c..af43836831 100644 --- a/integration-cli/docker_cli_stack_test.go +++ b/integration-cli/docker_cli_stack_test.go @@ -53,13 +53,13 @@ func (s *DockerSwarmSuite) TestStackDeployComposeFile(c *check.C) { out, err := d.Cmd(stackArgs...) c.Assert(err, checker.IsNil, check.Commentf(out)) - out, err = d.Cmd([]string{"stack", "ls"}...) + out, err = d.Cmd("stack", "ls") c.Assert(err, checker.IsNil) c.Assert(out, check.Equals, "NAME SERVICES\n"+"testdeploy 2\n") - out, err = d.Cmd([]string{"stack", "rm", testStackName}...) + out, err = d.Cmd("stack", "rm", testStackName) c.Assert(err, checker.IsNil) - out, err = d.Cmd([]string{"stack", "ls"}...) + out, err = d.Cmd("stack", "ls") c.Assert(err, checker.IsNil) c.Assert(out, check.Equals, "NAME SERVICES\n") } @@ -67,13 +67,16 @@ func (s *DockerSwarmSuite) TestStackDeployComposeFile(c *check.C) { func (s *DockerSwarmSuite) TestStackDeployWithSecretsTwice(c *check.C) { d := s.AddDaemon(c, true, true) + out, err := d.Cmd("secret", "create", "outside", "fixtures/secrets/default") + c.Assert(err, checker.IsNil, check.Commentf(out)) + testStackName := "testdeploy" stackArgs := []string{ "stack", "deploy", "--compose-file", "fixtures/deploy/secrets.yaml", testStackName, } - out, err := d.Cmd(stackArgs...) + out, err = d.Cmd(stackArgs...) c.Assert(err, checker.IsNil, check.Commentf(out)) out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.TaskTemplate.ContainerSpec.Secrets }}", "testdeploy_web") @@ -81,14 +84,15 @@ func (s *DockerSwarmSuite) TestStackDeployWithSecretsTwice(c *check.C) { var refs []swarm.SecretReference c.Assert(json.Unmarshal([]byte(out), &refs), checker.IsNil) - c.Assert(refs, checker.HasLen, 2) + c.Assert(refs, checker.HasLen, 3) sort.Sort(sortSecrets(refs)) - c.Assert(refs[0].SecretName, checker.Equals, "testdeploy_special") - c.Assert(refs[0].File.Name, checker.Equals, "special") - c.Assert(refs[1].SecretName, checker.Equals, "testdeploy_super") - c.Assert(refs[1].File.Name, checker.Equals, "foo.txt") - c.Assert(refs[1].File.Mode, checker.Equals, os.FileMode(0400)) + c.Assert(refs[0].SecretName, checker.Equals, "outside") + c.Assert(refs[1].SecretName, checker.Equals, "testdeploy_special") + c.Assert(refs[1].File.Name, checker.Equals, "special") + c.Assert(refs[2].SecretName, checker.Equals, "testdeploy_super") + c.Assert(refs[2].File.Name, checker.Equals, "foo.txt") + c.Assert(refs[2].File.Mode, checker.Equals, os.FileMode(0400)) // Deploy again to ensure there are no errors when secret hasn't changed out, err = d.Cmd(stackArgs...) diff --git a/integration-cli/fixtures/deploy/secrets.yaml b/integration-cli/fixtures/deploy/secrets.yaml index 965260f8cd..6ac92cddee 100644 --- a/integration-cli/fixtures/deploy/secrets.yaml +++ b/integration-cli/fixtures/deploy/secrets.yaml @@ -9,8 +9,12 @@ services: - source: super target: foo.txt mode: 0400 + - star secrets: special: file: fixtures/secrets/default super: file: fixtures/secrets/default + star: + external: + name: outside From f0a5531c46c3fe602b372de434125a3cd046a0b6 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Wed, 18 Jan 2017 14:40:29 -0500 Subject: [PATCH 5/7] Remove secrets as part of stack remove. Signed-off-by: Daniel Nephin --- cli/command/stack/common.go | 10 +++ cli/command/stack/remove.go | 75 ++++++++++++++++----- integration-cli/docker_cli_stack_test.go | 30 ++++++++- integration-cli/fixtures/deploy/remove.yaml | 11 +++ 4 files changed, 105 insertions(+), 21 deletions(-) create mode 100644 integration-cli/fixtures/deploy/remove.yaml diff --git a/cli/command/stack/common.go b/cli/command/stack/common.go index 5c4996d666..72719f94fc 100644 --- a/cli/command/stack/common.go +++ b/cli/command/stack/common.go @@ -48,3 +48,13 @@ func getStackNetworks( ctx, types.NetworkListOptions{Filters: getStackFilter(namespace)}) } + +func getStackSecrets( + ctx context.Context, + apiclient client.APIClient, + namespace string, +) ([]swarm.Secret, error) { + return apiclient.SecretList( + ctx, + types.SecretListOptions{Filters: getStackFilter(namespace)}) +} diff --git a/cli/command/stack/remove.go b/cli/command/stack/remove.go index 734ff92a53..966c1aa6bf 100644 --- a/cli/command/stack/remove.go +++ b/cli/command/stack/remove.go @@ -3,11 +3,12 @@ package stack import ( "fmt" - "golang.org/x/net/context" - + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" "github.com/spf13/cobra" + "golang.org/x/net/context" ) type removeOptions struct { @@ -33,41 +34,79 @@ func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command { func runRemove(dockerCli *command.DockerCli, opts removeOptions) error { namespace := opts.namespace client := dockerCli.Client() - stderr := dockerCli.Err() ctx := context.Background() - hasError := false services, err := getServices(ctx, client, namespace) if err != nil { return err } - for _, service := range services { - fmt.Fprintf(stderr, "Removing service %s\n", service.Spec.Name) - if err := client.ServiceRemove(ctx, service.ID); err != nil { - hasError = true - fmt.Fprintf(stderr, "Failed to remove service %s: %s", service.ID, err) - } - } networks, err := getStackNetworks(ctx, client, namespace) if err != nil { return err } - for _, network := range networks { - fmt.Fprintf(stderr, "Removing network %s\n", network.Name) - if err := client.NetworkRemove(ctx, network.ID); err != nil { - hasError = true - fmt.Fprintf(stderr, "Failed to remove network %s: %s", network.ID, err) - } + + secrets, err := getStackSecrets(ctx, client, namespace) + if err != nil { + return err } - if len(services) == 0 && len(networks) == 0 { + if len(services)+len(networks)+len(secrets) == 0 { fmt.Fprintf(dockerCli.Out(), "Nothing found in stack: %s\n", namespace) return nil } + hasError := removeServices(ctx, dockerCli, services) + hasError = removeSecrets(ctx, dockerCli, secrets) || hasError + hasError = removeNetworks(ctx, dockerCli, networks) || hasError + if hasError { return fmt.Errorf("Failed to remove some resources") } return nil } + +func removeServices( + ctx context.Context, + dockerCli *command.DockerCli, + services []swarm.Service, +) bool { + var err error + for _, service := range services { + fmt.Fprintf(dockerCli.Err(), "Removing service %s\n", service.Spec.Name) + if err = dockerCli.Client().ServiceRemove(ctx, service.ID); err != nil { + fmt.Fprintf(dockerCli.Err(), "Failed to remove service %s: %s", service.ID, err) + } + } + return err != nil +} + +func removeNetworks( + ctx context.Context, + dockerCli *command.DockerCli, + networks []types.NetworkResource, +) bool { + var err error + for _, network := range networks { + fmt.Fprintf(dockerCli.Err(), "Removing network %s\n", network.Name) + if err = dockerCli.Client().NetworkRemove(ctx, network.ID); err != nil { + fmt.Fprintf(dockerCli.Err(), "Failed to remove network %s: %s", network.ID, err) + } + } + return err != nil +} + +func removeSecrets( + ctx context.Context, + dockerCli *command.DockerCli, + secrets []swarm.Secret, +) bool { + var err error + for _, secret := range secrets { + fmt.Fprintf(dockerCli.Err(), "Removing secret %s\n", secret.Spec.Name) + if err = dockerCli.Client().SecretRemove(ctx, secret.ID); err != nil { + fmt.Fprintf(dockerCli.Err(), "Failed to remove secret %s: %s", secret.ID, err) + } + } + return err != nil +} diff --git a/integration-cli/docker_cli_stack_test.go b/integration-cli/docker_cli_stack_test.go index af43836831..43b890276a 100644 --- a/integration-cli/docker_cli_stack_test.go +++ b/integration-cli/docker_cli_stack_test.go @@ -5,13 +5,14 @@ import ( "io/ioutil" "os" "sort" + "strings" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/integration-cli/checker" "github.com/go-check/check" ) -func (s *DockerSwarmSuite) TestStackRemove(c *check.C) { +func (s *DockerSwarmSuite) TestStackRemoveUnknown(c *check.C) { d := s.AddDaemon(c, true, true) stackArgs := append([]string{"stack", "remove", "UNKNOWN_STACK"}) @@ -21,7 +22,7 @@ func (s *DockerSwarmSuite) TestStackRemove(c *check.C) { c.Assert(out, check.Equals, "Nothing found in stack: UNKNOWN_STACK\n") } -func (s *DockerSwarmSuite) TestStackTasks(c *check.C) { +func (s *DockerSwarmSuite) TestStackPSUnknown(c *check.C) { d := s.AddDaemon(c, true, true) stackArgs := append([]string{"stack", "ps", "UNKNOWN_STACK"}) @@ -31,7 +32,7 @@ func (s *DockerSwarmSuite) TestStackTasks(c *check.C) { c.Assert(out, check.Equals, "Nothing found in stack: UNKNOWN_STACK\n") } -func (s *DockerSwarmSuite) TestStackServices(c *check.C) { +func (s *DockerSwarmSuite) TestStackServicesUnknown(c *check.C) { d := s.AddDaemon(c, true, true) stackArgs := append([]string{"stack", "services", "UNKNOWN_STACK"}) @@ -99,6 +100,29 @@ func (s *DockerSwarmSuite) TestStackDeployWithSecretsTwice(c *check.C) { c.Assert(err, checker.IsNil, check.Commentf(out)) } +func (s *DockerSwarmSuite) TestStackRemove(c *check.C) { + d := s.AddDaemon(c, true, true) + + stackName := "testdeploy" + stackArgs := []string{ + "stack", "deploy", + "--compose-file", "fixtures/deploy/remove.yaml", + stackName, + } + out, err := d.Cmd(stackArgs...) + c.Assert(err, checker.IsNil, check.Commentf(out)) + + out, err = d.Cmd("stack", "ps", stackName) + c.Assert(err, checker.IsNil) + c.Assert(strings.Split(strings.TrimSpace(out), "\n"), checker.HasLen, 2) + + out, err = d.Cmd("stack", "rm", stackName) + c.Assert(err, checker.IsNil, check.Commentf(out)) + c.Assert(out, checker.Contains, "Removing service testdeploy_web") + c.Assert(out, checker.Contains, "Removing network testdeploy_default") + c.Assert(out, checker.Contains, "Removing secret testdeploy_special") +} + type sortSecrets []swarm.SecretReference func (s sortSecrets) Len() int { return len(s) } diff --git a/integration-cli/fixtures/deploy/remove.yaml b/integration-cli/fixtures/deploy/remove.yaml new file mode 100644 index 0000000000..4ec8cacc9b --- /dev/null +++ b/integration-cli/fixtures/deploy/remove.yaml @@ -0,0 +1,11 @@ + +version: "3.1" +services: + web: + image: busybox@sha256:e4f93f6ed15a0cdd342f5aae387886fba0ab98af0a102da6276eaf24d6e6ade0 + command: top + secrets: + - special +secrets: + special: + file: fixtures/secrets/default From b4f23bbc69f58662f8d94b0492d693e43d523e47 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Fri, 20 Jan 2017 17:06:35 -0500 Subject: [PATCH 6/7] Rebase Compose v3.1 schema on the latest v3 schema. Signed-off-by: Daniel Nephin --- cli/compose/schema/bindata.go | 2 +- .../schema/data/config_schema_v3.1.json | 22 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/cli/compose/schema/bindata.go b/cli/compose/schema/bindata.go index 3713315b2a..9486e91ae0 100644 --- a/cli/compose/schema/bindata.go +++ b/cli/compose/schema/bindata.go @@ -89,7 +89,7 @@ func dataConfig_schema_v30Json() (*asset, error) { return a, nil } -var _dataConfig_schema_v31Json = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xec\x1a\x4d\x93\xdb\xa8\xf2\xee\x5f\xa1\x52\x72\x8b\x67\x26\xaf\x5e\xea\x55\xbd\xdc\xf6\xb8\xa7\xdd\xf3\x4e\x39\x2a\x46\x6a\xcb\x64\x10\x10\x40\xce\x38\x29\xff\xf7\x2d\xf4\x65\xc0\x20\xb0\xad\xec\xcc\x61\x4f\x33\x16\xdd\x4d\x7f\x77\xd3\xf0\x73\x95\x65\xf9\x7b\x59\xee\xa0\x41\xf9\xe7\x2c\xdf\x29\xc5\x3f\x3f\x3c\x7c\x95\x8c\xde\xf5\x5f\xef\x99\xa8\x1f\x2a\x81\xb6\xea\xee\xe3\xa7\x87\xfe\xdb\xbb\x7c\xad\xf1\x70\xa5\x51\x4a\x46\xb7\xb8\x2e\xfa\x95\x62\xff\xdf\xfb\xff\xdc\x6b\xf4\x1e\x44\x1d\x38\x68\x20\xf6\xf4\x15\x4a\xd5\x7f\x13\xf0\xad\xc5\x02\x34\xf2\x63\xbe\x07\x21\x31\xa3\xf9\x66\xbd\xd2\x6b\x5c\x30\x0e\x42\x61\x90\xf9\xe7\x4c\x33\x97\x65\x13\xc8\xf8\xc1\x20\x2b\x95\xc0\xb4\xce\xbb\xcf\xc7\x8e\x42\x96\xe5\x12\xc4\x1e\x97\x06\x85\x89\xd5\x77\x0f\x27\xfa\x0f\x13\xd8\xda\xa5\x6a\x30\xdb\x7d\xe7\x48\x29\x10\xf4\xcf\x73\xde\xba\xe5\x2f\x8f\xe8\xee\xc7\x6f\x77\x7f\x7d\xbc\xfb\xff\x7d\x71\xb7\xf9\xf0\xde\x5a\xd6\xfa\x15\xb0\xed\xb7\xaf\x60\x8b\x29\x56\x98\xd1\x69\xff\x7c\x82\x3c\x0e\xff\x1d\xa7\x8d\x51\x55\x75\xc0\x88\x58\x7b\x6f\x11\x91\x60\xcb\x4c\x41\x7d\x67\xe2\x39\x26\xf3\x04\xf6\x4a\x32\x0f\xfb\x7b\x64\xb6\xc5\xd9\x33\xd2\x36\x51\x0b\x8e\x50\xaf\x24\x4c\xbf\xfd\x32\xf6\x93\x50\x0a\x50\x71\x97\xed\xa1\x5e\xcd\x63\xf5\xf6\xb7\x09\xbc\x1a\x85\x9e\x85\xed\x21\x8c\xbd\x3b\x06\xad\xf0\xf6\xa9\xca\x17\x5e\x61\x5d\x4d\xca\x0a\x68\xa9\x02\x4e\xd8\x41\x7f\x0b\xe8\xa3\x07\x68\x80\xaa\x7c\x52\x41\x96\xe5\x4f\x2d\x26\x95\xab\x51\x46\xe1\x0f\x4d\xe2\xd1\xf8\x98\x65\x3f\xdd\x4c\x66\xd0\xe9\xd6\xad\x5f\x61\x83\x4f\xeb\x01\x59\xa6\xf5\x92\x51\x05\x2f\xaa\x13\x6a\x7e\xeb\x5e\x05\xac\x7c\x06\xb1\xc5\x04\x52\x31\x90\xa8\xe5\x8c\xca\x08\x96\xaa\x60\xa2\xa8\x70\xa9\xf2\xa3\x83\x7e\x46\x2f\xee\x4f\x13\xaa\xf1\x6b\xb3\xf2\x10\xcc\x4b\xc4\x0b\x54\x55\x96\x1c\x48\x08\x74\xc8\xd7\x59\x8e\x15\x34\xd2\x2f\x62\x96\xb7\x14\x7f\x6b\xe1\xf7\x01\x44\x89\x16\x5c\xba\x95\x60\x7c\x79\xc2\xb5\x60\x2d\x2f\x38\x12\xda\xc1\xe6\xd5\x9f\x97\xac\x69\x10\x5d\xca\xeb\x2e\x91\x23\x41\xf3\x8c\x2a\x84\x29\x88\x82\xa2\x26\xe6\x48\x3a\xea\x80\x56\xb2\xe8\x0b\xfe\xac\x1b\x6d\x8b\x1e\x5f\x3a\x04\xa6\xea\xbf\xa8\x3d\x2a\x3a\xe7\xd8\x3d\x19\xed\xda\x9a\xb7\xdc\x41\x2c\x24\x20\x51\xee\xae\xc4\x67\x0d\xc2\x34\x45\x77\x40\x95\x38\x70\x86\x7b\x7f\x79\x73\x8e\x00\x74\x5f\x4c\xb9\xe4\x62\x35\x00\xdd\x63\xc1\x68\x33\x46\x43\x4a\x82\x99\x92\xbc\xc6\x7f\xe1\x4c\x82\xab\x18\x47\x40\x73\x69\x12\xd5\xd2\xc9\x88\xf1\x38\x0a\xbe\xce\x72\xda\x36\x4f\x20\x74\x0f\x6b\x41\x6e\x99\x68\x90\x66\x76\xdc\xdb\x58\xb6\x34\xed\xf1\x3c\x53\x81\xa6\x0c\xba\xac\x23\x52\x10\x4c\x9f\x97\x77\x71\x78\x51\x02\x15\x3b\x26\x55\x7a\x0e\x37\xd0\x77\x80\x88\xda\x95\x3b\x28\x9f\x67\xd0\x4d\x28\x0b\x9b\x49\x95\xe2\xe4\xb8\x41\x75\x1c\x88\x97\x31\x10\x82\x9e\x80\x5c\x25\xe7\xa2\xca\x37\xc8\xb2\xba\xd6\xa0\x21\x8f\x3b\xeb\x5c\x86\xe5\x58\xcd\xaf\x04\xde\x83\x48\x2d\xe0\x8c\x9f\x1a\x2e\x77\x31\xde\x80\x64\xf1\xee\xd3\x02\xfd\x72\xdf\x37\x9f\x33\x51\xd5\xfd\x47\x48\xbe\x71\xdb\x85\xcc\xa9\xfb\xbe\x2f\x8e\x84\x69\x0d\x85\x65\x95\x06\x95\xba\x6f\x10\x20\x03\x76\x3d\x81\x0e\xa7\x9b\xa2\x61\x55\xc8\x41\xcf\x80\x5d\xdd\x04\x33\xf5\xc5\x85\x30\xbb\xaa\x7f\x4c\x32\x5d\xf4\x00\x11\x91\x26\xc4\x5e\x2a\x9b\x27\x76\xe3\x2e\xd6\xc1\x21\x82\x91\x84\x78\xb0\x07\x15\x69\x51\xc3\x7c\xff\x29\xd1\x27\x7c\xb8\xff\x9b\xc5\x0d\xa0\x06\x69\xa6\xf7\xc8\x11\x52\x27\x56\xba\x70\xf3\x31\xb2\x89\x44\xdb\x2f\x6e\xe1\x39\xae\xc2\xb9\xa2\xcb\x10\x66\x80\x71\x26\xd4\x59\x74\xfd\x33\xe5\xbe\xdf\xfa\xe6\x6a\xcf\x05\xde\x63\x02\x35\xd8\xa7\x96\x27\xc6\x08\x20\x6a\xa5\x1e\x01\xa8\x2a\x18\x25\x87\x04\x48\xa9\x90\x88\x1e\x28\x24\x94\xad\xc0\xea\x50\x30\xae\x16\xef\x33\xe4\xae\x29\x24\xfe\x01\xb6\x35\x4f\xf9\x7e\x20\xb4\x71\x18\x72\x26\x24\x57\x1a\x34\x94\x92\xe2\x61\xec\x49\x84\xd1\x44\x15\x4f\x51\xb9\x64\xad\x28\x53\x0f\xd8\x7a\x4f\x24\x6a\x48\x3d\xc2\x6b\x77\xb3\xc3\x66\x1e\xb8\xbe\x04\xf8\xac\xd0\x0d\x26\x8c\x55\x65\xf7\xb7\x99\x57\x8e\xde\xd0\x97\x07\x59\xaa\xeb\xba\x35\xa9\x2a\x4c\x0b\xc6\x81\x46\x63\x43\x2a\xc6\x0b\x89\x6b\x8a\x48\x34\x3e\x34\x68\x2d\x50\x09\x05\x07\x81\x99\x57\x6b\x6b\x33\x29\x54\xad\x40\x9a\x55\x8b\x8c\x6a\xf8\xf6\xca\x63\xa5\x52\xf1\x60\x6f\x09\x6e\x70\x38\x68\x3c\x5e\x9b\xd0\x01\xf4\xd5\xdf\x5f\xf4\x67\x0a\xfe\x89\x53\x4c\x15\xd4\xda\x4d\xce\x9d\x6a\xa6\xe7\x9c\x6f\x39\x13\x7a\xcd\x1d\x12\xb6\x95\x66\xf8\xc8\xfa\xc0\xdc\x2a\x3f\x82\xaf\x13\xf5\xf2\x65\xdd\x75\x74\xf4\xd6\x03\x23\x1b\x2f\xfc\x45\xc5\xdc\x65\x63\x13\xac\xa7\xfe\xa0\x6a\x65\xf4\x58\xd0\xc1\x50\x39\xd7\xd2\x4e\xa0\xc6\xd0\x7e\xd1\x6a\xa1\xdb\x64\x1d\x04\x15\xf6\x73\xbb\x72\x24\xbb\x60\xec\xee\x9c\x58\x47\x02\xbe\x79\xb2\x09\xea\xce\x94\x1f\x27\xdf\x1c\x3b\x91\xd3\x24\x3e\x30\x5c\xd6\xae\x24\xf6\x56\x9e\xf1\xe9\x54\xe1\x06\x58\xab\x22\x50\x02\x94\xc0\x8e\xe6\xc7\x54\x6c\x12\x03\xf9\x36\x07\x43\x15\x96\xe8\xc9\x99\x31\x4f\xe9\xec\x2a\xf3\x66\xa7\x01\xfe\x38\x30\x9a\x33\xae\x01\xb9\x80\x6d\x53\x82\x45\x00\x27\xb8\x44\x32\x96\x90\x6e\x18\x53\xb4\xbc\x42\x0a\x8a\xfe\x7e\xf6\xa2\x12\x30\x93\xfb\x39\x12\x88\x10\x20\x58\x36\x29\xb9\x34\xaf\x80\xa0\xc3\x55\xb5\xb1\x43\xdf\x22\x4c\x5a\x01\x05\x2a\xd5\x70\x05\x1c\xf1\xcc\xbc\x61\x14\x2b\xe6\xcd\x14\x69\x5b\x36\xe8\xa5\x18\xb7\xed\x40\x62\x1d\x8e\xdd\xdc\xa7\x4e\x18\x0c\x4f\xe8\x1b\xc0\xcb\xaa\xf4\x8c\x89\x4e\x35\x3f\xe0\x31\xe3\x8e\x67\xa2\x0b\x90\x3a\x29\x4d\x03\xa0\x28\x7e\xb4\xc4\x0c\xa7\x8d\x82\x33\x82\xcb\xc3\x52\x12\x96\x8c\xf6\x4a\x4e\x71\x88\x1b\x3d\x50\xbb\x83\x6e\x89\x1a\xae\xa2\xc1\xda\x21\x7c\xc7\xb4\x62\xdf\x2f\xd8\x70\x39\x57\xe2\x04\x95\xe0\xe4\xbb\x5b\x15\x2d\x95\x40\x98\xaa\x8b\xcb\xfa\xad\x62\xdd\x50\xd5\x27\xff\x8c\x64\xfd\x09\x2e\x7e\x9f\x1e\xc8\xf4\x25\x6f\xa3\x53\xc1\x06\x1a\x26\xbc\x0e\xb8\xc0\x83\x8f\x98\x88\x23\xd8\x02\x55\x2d\x69\x8c\x3c\x40\x15\x8c\x2f\x7f\xea\x88\x8f\x8a\x37\xf1\x84\x84\x39\x6a\x96\x8a\x8e\xe4\xc1\x7a\xee\xad\xc1\xd9\xfc\xfc\x22\x0b\xcf\x30\x62\x5c\xc7\x79\x1f\x20\x64\xfb\x44\x03\xa3\x84\xf3\xd3\x86\xef\xb6\x3f\xfd\xb8\x72\x0c\x1f\x4e\x6e\x4b\x7a\xe3\x9d\x58\xc0\xaa\x8f\x53\x27\xb9\x9e\x74\xb5\x49\x36\x71\xf0\x42\x6a\x39\xfe\x2f\x6c\xf0\x6e\xc8\x19\xc3\x83\xa5\x48\xca\x18\xa0\xfe\xcd\x18\x6f\xc6\xbf\x66\x8a\xe2\x95\xa7\x83\x8b\x5f\xa6\xc5\x9c\x66\x80\xba\xba\x90\x26\x3c\x31\x7a\x53\x86\x78\x95\xf8\x75\x86\x81\x86\x49\xce\x4f\xf1\x73\x9a\x4c\xbe\x03\x1b\x30\x36\x36\x1b\x2e\x98\xe7\x5d\xae\x5d\xcd\xe6\x86\x3f\x23\x48\xe0\x4e\xc4\xd9\x74\x50\xe2\xbc\xe4\x0b\x66\x90\xfb\x0f\x33\x35\x7b\xee\xae\xfa\x17\x15\xbb\x05\x06\x6b\x7e\x9b\x3a\x8d\xfe\xa8\xdd\xf3\xb7\x96\x81\xf8\x37\xf0\xcf\x5e\x5e\x6a\x39\xe9\xe1\x6c\xca\xf4\xd3\x1e\x8e\xf6\xaf\x26\x37\x96\x7e\x1c\x90\xfe\xe5\x87\x91\xb2\x37\xe6\xd9\x27\x64\x46\xef\x7b\x4c\x77\x34\x3b\xbe\x8b\x0c\xdc\x44\xac\xcc\xbf\xdd\x1b\xd6\xd5\x71\xf5\x77\x00\x00\x00\xff\xff\xc8\x0f\x22\x69\x3c\x2f\x00\x00") +var _dataConfig_schema_v31Json = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xec\x1a\xcb\x8e\xdb\x36\xf0\xee\xaf\x10\x94\xdc\xe2\xdd\x4d\xd1\xa0\x40\x73\xeb\xb1\xa7\xf6\xdc\x85\x23\xd0\xd2\x58\x66\x96\x22\x19\x92\x72\xd6\x09\xfc\xef\x05\xf5\x32\x45\x91\x22\x6d\x2b\xd9\x45\xd1\xd3\xae\xc5\x99\xe1\xbc\x67\x38\xe4\xf7\x55\x92\xa4\x6f\x65\xbe\x87\x0a\xa5\x1f\x93\x74\xaf\x14\xff\xf8\xf0\xf0\x59\x32\x7a\xd7\x7e\xbd\x67\xa2\x7c\x28\x04\xda\xa9\xbb\xf7\x1f\x1e\xda\x6f\x6f\xd2\xb5\xc6\xc3\x85\x46\xc9\x19\xdd\xe1\x32\x6b\x57\xb2\xc3\xaf\xf7\xbf\xdc\x6b\xf4\x16\x44\x1d\x39\x68\x20\xb6\xfd\x0c\xb9\x6a\xbf\x09\xf8\x52\x63\x01\x1a\xf9\x31\x3d\x80\x90\x98\xd1\x74\xb3\x5e\xe9\x35\x2e\x18\x07\xa1\x30\xc8\xf4\x63\xa2\x99\x4b\x92\x01\xa4\xff\x60\x90\x95\x4a\x60\x5a\xa6\xcd\xe7\x53\x43\x21\x49\x52\x09\xe2\x80\x73\x83\xc2\xc0\xea\x9b\x87\x33\xfd\x87\x01\x6c\x6d\x53\x35\x98\x6d\xbe\x73\xa4\x14\x08\xfa\xf7\x94\xb7\x66\xf9\xd3\x23\xba\xfb\xf6\xc7\xdd\x3f\xef\xef\x7e\xbf\xcf\xee\x36\xef\xde\x8e\x96\xb5\x7e\x05\xec\xda\xed\x0b\xd8\x61\x8a\x15\x66\x74\xd8\x3f\x1d\x20\x4f\xdd\x7f\xa7\x61\x63\x54\x14\x0d\x30\x22\xa3\xbd\x77\x88\x48\x18\xcb\x4c\x41\x7d\x65\xe2\x29\x24\xf3\x00\xf6\x42\x32\x77\xfb\x3b\x64\x1e\x8b\x73\x60\xa4\xae\x82\x16\xec\xa1\x5e\x48\x98\x76\xfb\x65\xec\x27\x21\x17\xa0\xc2\x2e\xdb\x42\xbd\x98\xc7\xea\xed\x6f\x13\x78\xd5\x0b\x3d\x0b\xdb\x42\x18\x7b\x37\x0c\x8e\xc2\xdb\xa5\x2a\x57\x78\xf9\x75\x35\x28\xcb\xa3\xa5\x02\x38\x61\x47\xfd\xcd\xa3\x8f\x16\xa0\x02\xaa\xd2\x41\x05\x49\x92\x6e\x6b\x4c\x0a\x5b\xa3\x8c\xc2\x5f\x9a\xc4\xa3\xf1\x31\x49\xbe\xdb\x99\xcc\xa0\xd3\xac\x8f\x7e\xf9\x0d\x3e\xac\x7b\x64\x19\xd6\x73\x46\x15\x3c\xab\x46\xa8\xf9\xad\x5b\x15\xb0\xfc\x09\xc4\x0e\x13\x88\xc5\x40\xa2\x94\x33\x2a\x23\x58\xaa\x8c\x89\xac\xc0\xb9\x4a\x4f\x16\xfa\x84\x5e\xd8\x9f\x06\x54\xe3\xd7\x66\xe5\x20\x98\xe6\x88\x67\xa8\x28\x46\x72\x20\x21\xd0\x31\x5d\x27\x29\x56\x50\x49\xb7\x88\x49\x5a\x53\xfc\xa5\x86\x3f\x3b\x10\x25\x6a\xb0\xe9\x16\x82\xf1\xe5\x09\x97\x82\xd5\x3c\xe3\x48\x68\x07\x9b\x57\x7f\x9a\xb3\xaa\x42\x74\x29\xaf\xbb\x44\x8e\x08\xcd\x33\xaa\x10\xa6\x20\x32\x8a\xaa\x90\x23\xe9\xa8\x03\x5a\xc8\xac\x2d\xf8\xb3\x6e\xb4\xcb\x5a\x7c\x69\x11\x18\xaa\xff\xa2\xf6\x28\xe8\x9c\x63\xb7\x64\xb4\x6b\x6b\xde\x52\x0b\x31\x93\x80\x44\xbe\xbf\x12\x9f\x55\x08\xd3\x18\xdd\x01\x55\xe2\xc8\x19\x6e\xfd\xe5\xd5\x39\x02\xd0\x43\x36\xe4\x92\x8b\xd5\x00\xf4\x80\x05\xa3\x55\x1f\x0d\x31\x09\x66\x48\xf2\x1a\xff\x99\x33\x09\xb6\x62\x2c\x01\xcd\xa5\x41\xd4\x91\x4e\x7a\x8c\xc7\x5e\xf0\x75\x92\xd2\xba\xda\x82\xd0\x3d\xec\x08\x72\xc7\x44\x85\x34\xb3\xfd\xde\xc6\xf2\x48\xd3\x0e\xcf\x33\x15\x68\xca\xa0\xcb\x3a\x22\x19\xc1\xf4\x69\x79\x17\x87\x67\x25\x50\xb6\x67\x52\xc5\xe7\x70\x03\x7d\x0f\x88\xa8\x7d\xbe\x87\xfc\x69\x06\xdd\x84\x1a\x61\x33\xa9\x62\x9c\x1c\x57\xa8\x0c\x03\xf1\x3c\x04\x42\xd0\x16\xc8\x55\x72\x2e\xaa\x7c\x83\x2c\x2b\x4b\x0d\xea\xf3\xb8\x49\xe7\xd2\x2d\x87\x6a\x7e\x21\xf0\x01\x44\x6c\x01\x67\xfc\xdc\x70\xd9\x8b\xe1\x06\x24\x09\x77\x9f\x23\xd0\x4f\xf7\x6d\xf3\x39\x13\x55\xcd\x7f\x84\xa4\x1b\xbb\x5d\x48\xac\xba\xef\xfa\x62\x49\x18\xd7\x50\x8c\xac\x52\xa1\x5c\xf7\x0d\x02\xa4\xc7\xae\x67\xd0\xee\x74\x93\x55\xac\xf0\x39\xe8\x04\xd8\xd6\x8d\x37\x53\x5f\x5c\x08\x93\xab\xfa\xc7\x28\xd3\x05\x0f\x10\x01\x69\x7c\xec\xc5\xb2\x79\x66\x37\xec\x62\x0d\x1c\x22\x18\x49\x08\x07\xbb\x57\x91\x23\x6a\x98\x1f\x3e\x44\xfa\x84\x0b\xf7\xb7\x59\x5c\x0f\xaa\x97\x66\x7c\x8f\x1c\x20\x75\x66\xa5\x09\x37\x17\x23\x9b\x40\xb4\xfd\xe0\x16\x9e\xe3\xc2\x9f\x2b\x9a\x0c\x61\x06\x18\x67\x42\x4d\xa2\xeb\xe7\x94\xfb\x76\xeb\x9b\xab\x3d\x17\xf8\x80\x09\x94\x30\x3e\xb5\x6c\x19\x23\x80\xe8\x28\xf5\x08\x40\x45\xc6\x28\x39\x46\x40\x4a\x85\x44\xf0\x40\x21\x21\xaf\x05\x56\xc7\x8c\x71\xb5\x78\x9f\x21\xf7\x55\x26\xf1\x37\x18\x5b\xf3\x9c\xef\x3b\x42\x1b\x8b\x21\x6b\x42\x72\xa5\x41\x7d\x29\x29\x1c\xc6\x8e\x44\x18\x4c\x54\xe1\x14\x95\x4a\x56\x8b\x3c\xf6\x80\xad\xf7\x44\xa2\x84\xd8\x23\xbc\x76\xb7\x71\xd8\xcc\x03\x97\x97\x00\x4f\x0a\x5d\x67\xc2\x50\x55\xb6\x7f\x9b\x79\xe5\xe4\x0c\x7d\x79\x94\xb9\xba\xae\x5b\x93\xaa\xc0\x34\x63\x1c\x68\x30\x36\xa4\x62\x3c\x2b\x05\xca\x21\xe3\x20\x30\x73\xaa\x62\x6d\x46\x7a\x51\x0b\xa4\xf7\x9f\x92\x91\xb8\xa4\x88\x84\xc2\x4c\x55\x7c\x77\xe5\xb1\x52\xa9\x70\xb0\xd7\x04\x57\xd8\x1f\x34\x0e\xaf\x8d\xe8\x00\xda\xea\xef\x2e\xfa\x33\x05\xff\xcc\x29\xa6\x0a\x4a\xed\x26\x53\xa7\x9a\xe9\x39\xe7\x5b\xce\x88\x5e\x73\x8f\xc4\xd8\xa0\x33\x7c\x24\x6d\x60\xee\x94\x1b\xc1\xd5\x89\x3a\xf9\x1a\xdd\x75\x34\xf4\xd6\x1d\x23\x1b\x27\xfc\x45\xc5\xdc\x66\x63\xe3\xad\xa7\xee\xa0\xaa\x65\xf0\x58\xd0\xc0\x50\x39\xd7\xd2\x0e\xa0\xc6\xd0\x7e\xd1\x6a\xa1\xdb\x64\x1d\x04\x05\x76\x73\xbb\xb2\x24\xbb\x60\xec\x6e\x9d\x58\x7b\x02\xae\x79\xb2\x09\x1a\x9c\xbf\xcf\xcf\xb6\x3b\x20\xef\xdc\x19\x4b\xb4\xb5\x26\xae\xae\xe0\xd6\xde\x28\x0e\xe1\x1c\x23\x40\x09\x6c\xd9\xa5\x4f\xd4\x66\x3e\x01\xf9\x3a\xc7\x46\x0a\x57\xc0\x6a\x77\xc1\x5b\x99\xfe\xdd\x21\xa5\xc6\x5c\x3e\x60\x54\x03\xd2\xb6\xe9\xe3\x60\xd4\xbe\xbb\x0c\x1a\x2e\x26\x48\x04\x70\x82\x73\x24\x43\x89\xe8\x86\xf1\x44\xcd\x0b\xa4\x20\x6b\xef\x65\x2f\x4a\xfd\x33\x39\x9f\x23\x81\x08\x01\x82\x65\x15\x93\x43\xd3\x02\x08\x3a\x5e\x55\x3e\x1b\xf4\x1d\xc2\xa4\x16\x90\xa1\x5c\x75\x57\xbf\x01\x9f\x4b\x2b\x46\xb1\x62\xce\x0c\x11\xb7\x65\x85\x9e\xb3\x7e\xdb\x06\x24\xd4\xd9\x8c\x9b\xfa\xd8\xc9\x82\xe1\x09\x6d\xe3\x77\x59\x75\x9e\x31\xd1\xb9\xd6\x7b\x3c\xa6\xdf\x71\x22\xba\x00\xa9\x33\xc9\x30\xf8\x09\xe2\x07\x4b\x4b\x77\xca\xc8\x38\x23\x38\x3f\x2e\x25\x61\xce\x68\xab\xe4\x18\x87\xb8\xd1\x03\xb5\x3b\xe8\x56\xa8\xe2\x2a\x18\xac\x0d\xc2\x57\x4c\x0b\xf6\xf5\x82\x0d\x97\x73\x25\x4e\x50\x0e\x56\xbe\xbb\x55\xd1\x52\x09\x84\xa9\xba\xb8\x9c\xdf\x2a\xd6\x0d\xd5\x7c\xf0\xcf\x40\xd6\x1f\xe0\xc2\xf7\xe8\x9e\x4c\x9f\xf3\x3a\x38\x0d\xac\xa0\x62\xc2\xe9\x80\x0b\x3c\xf4\x08\x89\xd8\x83\x2d\x50\xd5\xa2\xc6\xc7\x1d\x54\xc6\xf8\xf2\xa7\x8d\xf0\x88\x78\x13\x4e\x48\x98\xa3\x6a\xa9\xe8\x88\x1e\xa8\xa7\xce\x1a\x9c\xcc\xcf\x2d\x12\xff\xec\x22\xc4\x75\x98\xf7\x0e\x42\xd6\x5b\xea\x19\x21\x4c\x4f\x19\xae\x5b\xfe\xf8\x63\xca\xc9\x7f\x28\xb9\x2d\xe9\xf5\x77\x61\x1e\xab\x3e\x0e\x3d\xf3\x7a\xd0\xd5\x26\xda\xc4\xde\x8b\xa8\xe5\xf8\x6f\xda\x77\x7b\x44\xe0\xea\xf3\x2f\xec\x04\x6f\x48\x2e\xdd\x8b\xa6\x40\x6e\xe9\xa0\xfe\x4f\x2d\xff\x11\x47\xfc\x79\xfe\xd5\x3d\x20\x0b\xbe\xdc\x6a\xa0\xae\x2e\xce\x11\xcf\x95\x5e\x81\xcd\x5e\xda\x14\xe3\xc1\xa2\x61\x92\xe9\x99\x7f\x4e\x93\xd1\xf7\x69\x1d\xc6\x66\xcc\x86\x0d\xe6\x78\xe3\x3b\xae\x90\x73\x83\xa4\x1e\xc4\x73\xbf\x62\x6d\xda\x29\x71\x5e\xf2\x05\x93\xcd\xfd\xbb\x99\x3e\x60\xee\xde\xfb\x07\x15\xd0\x05\x86\x74\x6e\x9b\x5a\x87\x87\x5e\xbb\xd3\x77\x9b\x9e\xf8\x37\xf0\x27\xaf\x38\xb5\x9c\xf4\x38\x99\x49\x7d\x1f\x0f\x5a\xdb\x17\x98\x9b\x91\x7e\x2c\x90\xf6\x15\x89\x91\xdd\x37\xe6\x79\xca\x67\x46\xe7\xdb\x4e\x7b\xcc\xdb\xbf\xb1\xf4\xdc\x6a\xac\xcc\xbf\xcd\x7b\xd8\xd5\x69\xf5\x6f\x00\x00\x00\xff\xff\xfc\xf3\x11\x6a\x88\x2f\x00\x00") func dataConfig_schema_v31JsonBytes() ([]byte, error) { return bindataRead( diff --git a/cli/compose/schema/data/config_schema_v3.1.json b/cli/compose/schema/data/config_schema_v3.1.json index b67203218a..b7037485f9 100644 --- a/cli/compose/schema/data/config_schema_v3.1.json +++ b/cli/compose/schema/data/config_schema_v3.1.json @@ -198,8 +198,8 @@ }, "sysctls": {"$ref": "#/definitions/list_or_dict"}, "stdin_open": {"type": "boolean"}, - "stop_signal": {"type": "string"}, "stop_grace_period": {"type": "string", "format": "duration"}, + "stop_signal": {"type": "string"}, "tmpfs": {"$ref": "#/definitions/string_or_list"}, "tty": {"type": "boolean"}, "ulimits": { @@ -231,10 +231,11 @@ "healthcheck": { "id": "#/definitions/healthcheck", - "type": ["object", "null"], + "type": "object", + "additionalProperties": false, "properties": { - "interval": {"type":"string"}, - "timeout": {"type":"string"}, + "disable": {"type": "boolean"}, + "interval": {"type": "string"}, "retries": {"type": "number"}, "test": { "oneOf": [ @@ -242,9 +243,8 @@ {"type": "array", "items": {"type": "string"}} ] }, - "disable": {"type": "boolean"} - }, - "additionalProperties": false + "timeout": {"type": "string"} + } }, "deployment": { "id": "#/definitions/deployment", @@ -337,6 +337,7 @@ }, "additionalProperties": false }, + "internal": {"type": "boolean"}, "labels": {"$ref": "#/definitions/list_or_dict"} }, "additionalProperties": false @@ -357,10 +358,11 @@ "type": ["boolean", "object"], "properties": { "name": {"type": "string"} - } - } + }, + "additionalProperties": false + }, + "labels": {"$ref": "#/definitions/list_or_dict"} }, - "labels": {"$ref": "#/definitions/list_or_dict"}, "additionalProperties": false }, From 167b3f95b7f659817f8421742ca4078edd42dbaa Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Thu, 26 Jan 2017 12:00:46 -0500 Subject: [PATCH 7/7] Set default values for uid and gid to prevent errors when starting a service. Signed-off-by: Daniel Nephin --- cli/compose/convert/service.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cli/compose/convert/service.go b/cli/compose/convert/service.go index 573f7723fd..f23df26127 100644 --- a/cli/compose/convert/service.go +++ b/cli/compose/convert/service.go @@ -196,11 +196,20 @@ func convertServiceSecrets( source = secretSpec.External.Name } + uid := secret.UID + gid := secret.GID + if uid == "" { + uid = "0" + } + if gid == "" { + gid = "0" + } + opts = append(opts, &types.SecretRequestOption{ Source: source, Target: target, - UID: secret.UID, - GID: secret.GID, + UID: uid, + GID: gid, Mode: os.FileMode(secret.Mode), }) }