From dea809813ecddab1cbca25210b2c1fa1ccc279bc Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Mon, 10 Apr 2017 13:11:58 +0200 Subject: [PATCH 01/18] remove --init-path from client Signed-off-by: Antonio Murdaca --- cli/command/container/opts.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/cli/command/container/opts.go b/cli/command/container/opts.go index 30c356a050..ad5c88d205 100644 --- a/cli/command/container/opts.go +++ b/cli/command/container/opts.go @@ -118,7 +118,6 @@ type containerOptions struct { runtime string autoRemove bool init bool - initPath string Image string Args []string @@ -284,8 +283,6 @@ func addFlags(flags *pflag.FlagSet) *containerOptions { flags.BoolVar(&copts.init, "init", false, "Run an init inside the container that forwards signals and reaps processes") flags.SetAnnotation("init", "version", []string{"1.25"}) - flags.StringVar(&copts.initPath, "init-path", "", "Path to the docker-init binary") - flags.SetAnnotation("init-path", "version", []string{"1.25"}) return copts } From c44e74e676ab6bda3eac97690893f73350c34e9e Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Tue, 11 Apr 2017 17:21:21 -0400 Subject: [PATCH 02/18] Add logdrivers to /info This is required for swarmkit to be able to filter based on log driver. Signed-off-by: Brian Goff --- cli/command/system/info.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cli/command/system/info.go b/cli/command/system/info.go index 8c5dec3f5a..c951e42eb7 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -91,6 +91,10 @@ func prettyPrintInfo(dockerCli *command.DockerCli, info types.Info) error { fmt.Fprintf(dockerCli.Out(), "\n") } + fmt.Fprintf(dockerCli.Out(), " Log:") + fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Log, " ")) + fmt.Fprintf(dockerCli.Out(), "\n") + fmt.Fprintf(dockerCli.Out(), "Swarm: %v\n", info.Swarm.LocalNodeState) if info.Swarm.LocalNodeState != swarm.LocalNodeStateInactive && info.Swarm.LocalNodeState != swarm.LocalNodeStateLocked { fmt.Fprintf(dockerCli.Out(), " NodeID: %s\n", info.Swarm.NodeID) From a316b380e7293614060d54ba0b445741932d2529 Mon Sep 17 00:00:00 2001 From: Dong Chen Date: Mon, 10 Apr 2017 14:12:44 -0700 Subject: [PATCH 03/18] do not allow duration less than 1 ms in healthcheck parameters Signed-off-by: Dong Chen --- cli/command/container/opts.go | 6 +++--- cli/command/service/opts.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/command/container/opts.go b/cli/command/container/opts.go index ad5c88d205..5c8e2c53c4 100644 --- a/cli/command/container/opts.go +++ b/cli/command/container/opts.go @@ -229,10 +229,10 @@ func addFlags(flags *pflag.FlagSet) *containerOptions { // Health-checking flags.StringVar(&copts.healthCmd, "health-cmd", "", "Command to run to check health") - flags.DurationVar(&copts.healthInterval, "health-interval", 0, "Time between running the check (ns|us|ms|s|m|h) (default 0s)") + flags.DurationVar(&copts.healthInterval, "health-interval", 0, "Time between running the check (ms|s|m|h) (default 0s)") flags.IntVar(&copts.healthRetries, "health-retries", 0, "Consecutive failures needed to report unhealthy") - flags.DurationVar(&copts.healthTimeout, "health-timeout", 0, "Maximum time to allow one check to run (ns|us|ms|s|m|h) (default 0s)") - flags.DurationVar(&copts.healthStartPeriod, "health-start-period", 0, "Start period for the container to initialize before starting health-retries countdown (ns|us|ms|s|m|h) (default 0s)") + flags.DurationVar(&copts.healthTimeout, "health-timeout", 0, "Maximum time to allow one check to run (ms|s|m|h) (default 0s)") + flags.DurationVar(&copts.healthStartPeriod, "health-start-period", 0, "Start period for the container to initialize before starting health-retries countdown (ms|s|m|h) (default 0s)") flags.SetAnnotation("health-start-period", "version", []string{"1.29"}) flags.BoolVar(&copts.noHealthcheck, "no-healthcheck", false, "Disable any container-specified HEALTHCHECK") diff --git a/cli/command/service/opts.go b/cli/command/service/opts.go index ed23a562fe..d043b78cf8 100644 --- a/cli/command/service/opts.go +++ b/cli/command/service/opts.go @@ -802,13 +802,13 @@ func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions, defaultFlagValu flags.StringVar(&opts.healthcheck.cmd, flagHealthCmd, "", "Command to run to check health") flags.SetAnnotation(flagHealthCmd, "version", []string{"1.25"}) - flags.Var(&opts.healthcheck.interval, flagHealthInterval, "Time between running the check (ns|us|ms|s|m|h)") + flags.Var(&opts.healthcheck.interval, flagHealthInterval, "Time between running the check (ms|s|m|h)") flags.SetAnnotation(flagHealthInterval, "version", []string{"1.25"}) - flags.Var(&opts.healthcheck.timeout, flagHealthTimeout, "Maximum time to allow one check to run (ns|us|ms|s|m|h)") + flags.Var(&opts.healthcheck.timeout, flagHealthTimeout, "Maximum time to allow one check to run (ms|s|m|h)") flags.SetAnnotation(flagHealthTimeout, "version", []string{"1.25"}) flags.IntVar(&opts.healthcheck.retries, flagHealthRetries, 0, "Consecutive failures needed to report unhealthy") flags.SetAnnotation(flagHealthRetries, "version", []string{"1.25"}) - flags.Var(&opts.healthcheck.startPeriod, flagHealthStartPeriod, "Start period for the container to initialize before counting retries towards unstable (ns|us|ms|s|m|h)") + flags.Var(&opts.healthcheck.startPeriod, flagHealthStartPeriod, "Start period for the container to initialize before counting retries towards unstable (ms|s|m|h)") flags.SetAnnotation(flagHealthStartPeriod, "version", []string{"1.29"}) flags.BoolVar(&opts.healthcheck.noHealthcheck, flagNoHealthcheck, false, "Disable any container-specified HEALTHCHECK") flags.SetAnnotation(flagNoHealthcheck, "version", []string{"1.25"}) From a2c8291e1ec3f385c96b27c5e47cb516623e8237 Mon Sep 17 00:00:00 2001 From: Colin Hebert Date: Sat, 15 Apr 2017 05:30:42 +1000 Subject: [PATCH 04/18] Add support for labels during build with compose Signed-off-by: Colin Hebert --- cli/compose/schema/data/config_schema_v3.2.json | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/compose/schema/data/config_schema_v3.2.json b/cli/compose/schema/data/config_schema_v3.2.json index a5bd3686fa..1a290476e1 100644 --- a/cli/compose/schema/data/config_schema_v3.2.json +++ b/cli/compose/schema/data/config_schema_v3.2.json @@ -72,6 +72,7 @@ "context": {"type": "string"}, "dockerfile": {"type": "string"}, "args": {"$ref": "#/definitions/list_or_dict"}, + "labels": {"$ref": "#/definitions/list_or_dict"}, "cache_from": {"$ref": "#/definitions/list_of_strings"} }, "additionalProperties": false From 53b43f02e21056600cf5d033c1eca10c3e4d0c01 Mon Sep 17 00:00:00 2001 From: Colin Hebert Date: Sat, 15 Apr 2017 10:57:43 +1000 Subject: [PATCH 05/18] Run go generate github.com/docker/docker/cli/compose/schema Signed-off-by: Colin Hebert --- cli/compose/schema/bindata.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/compose/schema/bindata.go b/cli/compose/schema/bindata.go index fa9a29f96c..f850076008 100644 --- a/cli/compose/schema/bindata.go +++ b/cli/compose/schema/bindata.go @@ -70,7 +70,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _dataConfig_schema_v30Json = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xec\x5a\x4b\x8f\xdb\x38\x12\xbe\xfb\x57\x08\x4a\x6e\x71\x77\x07\xd8\x60\x81\xcd\x6d\x8f\x7b\xda\x39\x4f\x43\x11\x68\xa9\x6c\x33\x4d\x91\x4c\x91\x72\xda\x09\xfc\xdf\x07\xd4\xcb\x14\x4d\x8a\xb2\xad\x3c\x30\x98\x53\xb7\xc5\xaa\x62\xbd\xf8\x55\xb1\xa4\xef\xab\x24\x49\xdf\xaa\x62\x0f\x15\x49\x3f\x26\xe9\x5e\x6b\xf9\xf1\xe9\xe9\xb3\x12\xfc\xa1\x7d\xfa\x28\x70\xf7\x54\x22\xd9\xea\x87\xf7\x1f\x9e\xda\x67\x6f\xd2\xb5\xe1\xa3\xa5\x61\x29\x04\xdf\xd2\x5d\xde\xae\xe4\x87\x7f\x3d\xbe\x7f\x34\xec\x2d\x89\x3e\x4a\x30\x44\x62\xf3\x19\x0a\xdd\x3e\x43\xf8\x52\x53\x04\xc3\xfc\x9c\x1e\x00\x15\x15\x3c\xcd\xd6\x2b\xb3\x26\x51\x48\x40\x4d\x41\xa5\x1f\x13\xa3\x5c\x92\x0c\x24\xfd\x03\x4b\xac\xd2\x48\xf9\x2e\x6d\x1e\x9f\x1a\x09\x49\x92\x2a\xc0\x03\x2d\x2c\x09\x83\xaa\x6f\x9e\xce\xf2\x9f\x06\xb2\xb5\x2b\xd5\x52\xb6\x79\x2e\x89\xd6\x80\xfc\x8f\x4b\xdd\x9a\xe5\x4f\xcf\xe4\xe1\xdb\x7f\x1f\xfe\x7c\xff\xf0\x9f\xc7\xfc\x21\x7b\xf7\x76\xb4\x6c\xfc\x8b\xb0\x6d\xb7\x2f\x61\x4b\x39\xd5\x54\xf0\x61\xff\x74\xa0\x3c\x75\xff\x9d\x86\x8d\x49\x59\x36\xc4\x84\x8d\xf6\xde\x12\xa6\x60\x6c\x33\x07\xfd\x55\xe0\x4b\xcc\xe6\x81\xec\x17\xd9\xdc\xed\xef\xb1\x79\x6c\xce\x41\xb0\xba\x8a\x46\xb0\xa7\xfa\x45\xc6\xb4\xdb\xdf\x17\xbf\x55\x6f\xf4\x24\x6d\x4b\x61\xed\xdd\x28\x38\xca\x76\x9f\xab\x7c\xd9\x16\xf6\xd5\xe0\xac\x80\x97\x4a\x90\x4c\x1c\xcd\xb3\x80\x3f\x5a\x82\x0a\xb8\x4e\x07\x17\x24\x49\xba\xa9\x29\x2b\x5d\x8f\x0a\x0e\xff\x37\x22\x9e\xad\x87\x49\xf2\xdd\x3d\xd8\x96\x9c\x66\x7d\xf4\x2b\x1c\xf0\x61\x3d\x60\xcb\xb0\x5e\x08\xae\xe1\x55\x37\x46\x4d\x6f\xdd\xba\x40\x14\x2f\x80\x5b\xca\x60\x2e\x07\xc1\x9d\x9a\x70\x19\xa3\x4a\xe7\x02\xf3\x92\x16\x3a\x3d\x39\xec\x17\xf2\xe2\xf9\x34\xb0\x5a\xbf\xb2\x95\x47\x60\x5a\x10\x99\x93\xb2\x1c\xd9\x41\x10\xc9\x31\x5d\x27\x29\xd5\x50\x29\xbf\x89\x49\x5a\x73\xfa\xa5\x86\xff\x75\x24\x1a\x6b\x70\xe5\x96\x28\xe4\xf2\x82\x77\x28\x6a\x99\x4b\x82\x26\xc1\xa6\xdd\x9f\x16\xa2\xaa\x08\x5f\x2a\xeb\xae\xb1\x63\x86\xe7\x05\xd7\x84\x72\xc0\x9c\x93\x2a\x96\x48\xe6\xd4\x01\x2f\x55\xde\xd6\xbf\xc9\x34\xda\xe6\x2d\xbf\x72\x04\x0c\xc5\x70\xd1\x78\x94\x7c\x2a\xb1\x5b\x31\x26\xb5\x8d\x6e\xa9\xc3\x98\x2b\x20\x58\xec\x6f\xe4\x17\x15\xa1\x7c\x8e\xef\x80\x6b\x3c\x4a\x41\xdb\x7c\xf9\xed\x12\x01\xf8\x21\x1f\xb0\xe4\x6a\x37\x00\x3f\x50\x14\xbc\xea\x4f\xc3\x1c\x80\x19\x40\xde\xf0\xbf\x4a\xa1\xc0\x75\x8c\x63\xa0\xbd\x34\x98\x3a\xf2\x49\xcf\xf1\xdc\x1b\xbe\x4e\x52\x5e\x57\x1b\x40\xd3\xd2\x8d\x28\xb7\x02\x2b\x62\x94\xed\xf7\xb6\x96\x47\x9e\xf6\x64\x9e\xed\x40\xdb\x06\x53\xd6\x09\xcb\x19\xe5\x2f\xcb\xa7\x38\xbc\x6a\x24\xf9\x5e\x28\x3d\x1f\xc3\x2d\xf6\x3d\x10\xa6\xf7\xc5\x1e\x8a\x97\x09\x76\x9b\x6a\xc4\x2d\x94\x9e\x93\xe4\xb4\x22\xbb\x38\x91\x2c\x62\x24\x8c\x6c\x80\xdd\x64\xe7\xa2\xce\xb7\xc4\x8a\xdd\xce\x90\x86\x32\xee\xa2\x73\xe9\x96\x63\x35\xbf\x44\x7a\x00\x9c\x5b\xc0\x85\x3c\x37\x5c\xee\x62\xbc\x01\x49\xe2\xdd\xe7\x88\xf4\xd3\x63\xdb\x7c\x4e\x9c\xaa\xe6\x3f\xc6\xd2\xcc\x6d\x17\x12\xa7\xee\xfb\x9e\x38\x16\xce\x6b\x28\x46\x51\xa9\x48\x61\xfa\x06\x04\x15\x88\xeb\x99\xb4\x6b\xf6\xf3\x4a\x94\xa1\x04\xbd\x20\x76\x7d\x13\x44\xea\xab\x0b\x61\x72\x53\xff\x38\x2b\x74\xd1\x0b\x44\xc4\x9a\x90\x7a\x73\xd5\x3c\xab\x1b\x4f\xb1\x86\x8e\x30\x4a\x14\xc4\x0f\x7b\xd0\x91\x23\x69\x54\x1e\x3e\xcc\xcc\x09\x1f\xef\xbf\x27\x79\x03\xac\x41\x99\xf3\x7b\xe4\x88\xa8\xb3\x2a\xcd\x71\xf3\x29\x92\x45\x4e\xdb\x0f\x6e\xe1\x25\x2d\xc3\x58\xd1\x20\x84\x7d\xc0\xa4\x40\x7d\x71\xba\x7e\x4e\xb9\x6f\xb7\xbe\xbb\xda\x4b\xa4\x07\xca\x60\x07\xe3\x5b\xcb\x46\x08\x06\x84\x8f\xa0\x07\x81\x94\xb9\xe0\xec\x38\x83\x52\x69\x82\xd1\x0b\x85\x82\xa2\x46\xaa\x8f\xb9\x90\x7a\xf1\x3e\x43\xed\xab\x5c\xd1\x6f\x30\x8e\xe6\x19\xef\x3b\x41\xd9\x88\xe7\xa8\x0a\x7d\x5b\xbd\x56\xba\xa4\x3c\x17\x12\x78\xd4\x3b\x4a\x0b\x99\xef\x90\x14\x90\x4b\x40\x2a\x4a\x9f\x81\x6b\x3b\xd6\x65\x8d\xc4\xec\x7f\x29\x46\xd1\x1d\x27\x2c\xe6\x68\x5d\xc9\xed\x8d\x17\x0b\xad\xe3\xe1\xae\x19\xad\x68\xf8\x1c\x78\x00\x76\x46\x0d\x68\xf1\xdf\x0f\xfb\x13\x90\x7f\xd6\x94\x72\x0d\x3b\x40\x1f\x52\x4e\x74\x1d\xd3\x4d\xc7\x8c\x6e\x63\x4f\x70\x1c\xd0\x09\x3d\x1a\x06\x25\xb6\xda\xcf\xe0\xeb\x45\xbc\x7a\x8d\x86\xbf\x8d\xbc\x75\xa7\x48\xe6\xa5\xbf\x0a\xce\x5d\x35\xb2\x20\xa2\x9e\xbc\x88\x5a\xab\x68\x63\xd8\xd0\x70\x35\xd5\xd4\x0c\xa4\xd6\x14\x73\x51\xbc\x30\x8d\x92\x39\x04\x25\xf5\x6b\xbb\x72\x2c\xbb\x62\x8e\xec\xdc\x59\x7a\x01\xbe\x89\xa2\x4d\x1a\x9d\xc0\x4e\x4f\x37\x3b\xa2\xe0\xe4\x91\x2a\xb2\x71\x66\x6e\xbe\xc3\x6d\xb2\x11\x0f\x71\x8c\x41\xd0\x48\x9d\xb8\x74\x68\x3b\xc2\x13\x50\xbf\xe7\xe0\x40\xd3\x0a\x44\xed\xaf\x59\x2b\x3b\xbf\x3b\xa6\xd4\x9a\xcc\x46\x82\x6a\x51\xba\x31\x7d\x1e\x82\xda\xf7\x17\xd1\xc0\xcd\x39\x24\x08\x92\xd1\x82\xa8\x18\x10\xdd\x71\x41\xad\x65\x49\x34\xe4\xed\x8b\xaa\xab\xa0\x7f\x02\xf3\x25\x41\xc2\x18\x30\xaa\xaa\x39\x18\x9a\x96\xc0\xc8\xf1\xa6\xf2\xd9\xb0\x6f\x09\x65\x35\x42\x4e\x0a\xdd\xbd\x0b\x8b\xe4\x5c\x5a\x09\x4e\xb5\xf0\x22\xc4\xbc\x2d\x2b\xf2\x9a\xf7\xdb\x36\x24\xde\x03\x13\x6c\xeb\xe6\xde\x2d\xad\x4c\x50\xa2\xc6\xe2\xc2\xd9\x37\x87\xe8\x5c\xeb\x03\x19\xd3\xef\x78\x61\x3a\x82\x32\x48\x32\x5c\xfd\xa3\xfc\xd1\xd2\xd2\xf5\x99\xb9\x14\x8c\x16\xc7\xa5\x2c\x2c\x04\x6f\x9d\x3c\x27\x21\xee\xcc\x40\x93\x0e\xa6\x15\xaa\xa4\x8e\x1e\xd6\x86\xe1\x2b\xe5\xa5\xf8\x7a\xc5\x86\xcb\xa5\x92\x64\xa4\x00\x07\xef\xee\x75\xb4\xd2\x48\x28\xd7\x57\x97\xf3\x7b\xcd\xba\xa3\x9a\x0f\xf9\x19\x41\xfd\x81\x2e\xfe\x26\x35\x80\xf4\x85\xac\xa3\xf3\xa0\x0a\x2a\x81\xde\x04\x5c\xe0\xcd\x77\xcc\xc4\x9e\x6c\x81\xaa\x36\x6b\x80\xd8\x51\x99\xfb\xe2\xe2\xb7\x8d\xf8\x90\x30\x8b\x03\x12\x95\xa4\x5a\xea\x74\xcc\x1e\xa9\xa6\xde\x1a\x9c\x4c\x8f\x22\x92\xf0\x38\x22\xa6\x75\x5c\xf7\x8e\x42\xd5\x1b\x0e\x93\x1d\x95\xe5\x4f\xdf\x7b\xde\xf9\xd7\x94\x53\xf8\x52\x72\x1f\xe8\xf5\x6f\x43\x02\x51\x7d\x1e\x7a\xe6\xf5\xe0\xab\x6c\x76\x88\x83\xaf\x22\x96\xd3\xbf\x69\xdf\xdd\x11\x81\xaf\xcf\xbf\xb2\x13\xbc\x03\x5c\xba\x4f\x3c\x22\xd8\xd2\x51\xfd\x03\x2d\x7f\x93\x44\xfc\x79\xf9\xe5\x4c\xb3\xac\x3c\xbb\xbc\x68\x4e\xa5\xc4\xec\x31\x7e\xc7\x91\x8d\xd5\x70\xc9\x3c\x5f\xda\x8d\x61\x79\x6a\x7a\xd1\x93\x04\xc6\xba\xce\xa6\x9d\x13\xa7\x2d\x5f\x30\xc3\x1f\xdf\x4d\x14\x9f\xa9\xd7\x6d\x3f\x08\xb5\x17\x98\x0c\xf9\x63\xea\x74\xac\xbd\x77\x2f\x3f\x17\x0b\x80\x9a\xc5\x7f\xf1\xf1\x98\xb1\x93\x1f\x2f\x06\x21\xdf\xc7\xd3\xbd\xf6\xc3\xaf\x6c\xe4\x1f\x87\xa4\x7d\x79\x6d\x41\x4a\x66\x37\xf1\xa1\x30\x7a\x3f\x29\x73\x67\x8b\xfd\xa7\x5d\x99\x1f\xae\x56\xf6\xdf\xe6\x33\xbc\xd5\x69\xf5\x57\x00\x00\x00\xff\xff\x78\x30\xec\x51\x0e\x2b\x00\x00") +var _dataConfig_schema_v30Json = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5a\x4b\x8f\xdb\x38\x12\xbe\xfb\x57\x08\x4a\x6e\x71\x77\x07\xd8\x60\x81\xcd\x6d\x8f\x7b\xda\x39\x4f\x43\x11\x68\xa9\x6c\x33\x4d\x91\x4c\x91\x72\xda\x09\xfc\xdf\x07\xd4\xcb\x14\x4d\x8a\xb2\xad\x3c\x30\x98\x53\xb7\xc5\xaa\x62\xbd\xf8\x55\xb1\xa4\xef\xab\x24\x49\xdf\xaa\x62\x0f\x15\x49\x3f\x26\xe9\x5e\x6b\xf9\xf1\xe9\xe9\xb3\x12\xfc\xa1\x7d\xfa\x28\x70\xf7\x54\x22\xd9\xea\x87\xf7\x1f\x9e\xda\x67\x6f\xd2\xb5\xe1\xa3\xa5\x61\x29\x04\xdf\xd2\x5d\xde\xae\xe4\x87\x7f\x3d\xbe\x7f\x34\xec\x2d\x89\x3e\x4a\x30\x44\x62\xf3\x19\x0a\xdd\x3e\x43\xf8\x52\x53\x04\xc3\xfc\x9c\x1e\x00\x15\x15\x3c\xcd\xd6\x2b\xb3\x26\x51\x48\x40\x4d\x41\xa5\x1f\x13\xa3\x5c\x92\x0c\x24\xfd\x03\x4b\xac\xd2\x48\xf9\x2e\x6d\x1e\x9f\x1a\x09\x49\x92\x2a\xc0\x03\x2d\x2c\x09\x83\xaa\x6f\x9e\xce\xf2\x9f\x06\xb2\xb5\x2b\xd5\x52\xb6\x79\x2e\x89\xd6\x80\xfc\x8f\x4b\xdd\x9a\xe5\x4f\xcf\xe4\xe1\xdb\x7f\x1f\xfe\x7c\xff\xf0\x9f\xc7\xfc\x21\x7b\xf7\x76\xb4\x6c\xfc\x8b\xb0\x6d\xb7\x2f\x61\x4b\x39\xd5\x54\xf0\x61\xff\x74\xa0\x3c\x75\xff\x9d\x86\x8d\x49\x59\x36\xc4\x84\x8d\xf6\xde\x12\xa6\x60\x6c\x33\x07\xfd\x55\xe0\x4b\xcc\xe6\x81\xec\x17\xd9\xdc\xed\xef\xb1\x79\x6c\xce\x41\xb0\xba\x8a\x46\xb0\xa7\xfa\x45\xc6\xb4\xdb\xdf\x17\xbf\x55\x6f\xf4\x24\x6d\x4b\x61\xed\xdd\x28\x38\xca\x76\x9f\xab\x7c\xd9\x16\xf6\xd5\xe0\xac\x80\x97\x4a\x90\x4c\x1c\xcd\xb3\x80\x3f\x5a\x82\x0a\xb8\x4e\x07\x17\x24\x49\xba\xa9\x29\x2b\x5d\x8f\x0a\x0e\xff\x37\x22\x9e\xad\x87\x49\xf2\xdd\x3d\xd8\x96\x9c\x66\x7d\xf4\x2b\x1c\xf0\x61\x3d\x60\xcb\xb0\x5e\x08\xae\xe1\x55\x37\x46\x4d\x6f\xdd\xba\x40\x14\x2f\x80\x5b\xca\x60\x2e\x07\xc1\x9d\x9a\x70\x19\xa3\x4a\xe7\x02\xf3\x92\x16\x3a\x3d\x39\xec\x17\xf2\xe2\xf9\x34\xb0\x5a\xbf\xb2\x95\x47\x60\x5a\x10\x99\x93\xb2\x1c\xd9\x41\x10\xc9\x31\x5d\x27\x29\xd5\x50\x29\xbf\x89\x49\x5a\x73\xfa\xa5\x86\xff\x75\x24\x1a\x6b\x70\xe5\x96\x28\xe4\xf2\x82\x77\x28\x6a\x99\x4b\x82\x26\xc1\xa6\xdd\x9f\x16\xa2\xaa\x08\x5f\x2a\xeb\xae\xb1\x63\x86\xe7\x05\xd7\x84\x72\xc0\x9c\x93\x2a\x96\x48\xe6\xd4\x01\x2f\x55\xde\xd6\xbf\xc9\x34\xda\xe6\x2d\xbf\x72\x04\x0c\xc5\x70\xd1\x78\x94\x7c\x2a\xb1\x5b\x31\x26\xb5\x8d\x6e\xa9\xc3\x98\x2b\x20\x58\xec\x6f\xe4\x17\x15\xa1\x7c\x8e\xef\x80\x6b\x3c\x4a\x41\xdb\x7c\xf9\xed\x12\x01\xf8\x21\x1f\xb0\xe4\x6a\x37\x00\x3f\x50\x14\xbc\xea\x4f\xc3\x1c\x80\x19\x40\xde\xf0\xbf\x4a\xa1\xc0\x75\x8c\x63\xa0\xbd\x34\x98\x3a\xf2\x49\xcf\xf1\xdc\x1b\xbe\x4e\x52\x5e\x57\x1b\x40\xd3\xd2\x8d\x28\xb7\x02\x2b\x62\x94\xed\xf7\xb6\x96\x47\x9e\xf6\x64\x9e\xed\x40\xdb\x06\x53\xd6\x09\xcb\x19\xe5\x2f\xcb\xa7\x38\xbc\x6a\x24\xf9\x5e\x28\x3d\x1f\xc3\x2d\xf6\x3d\x10\xa6\xf7\xc5\x1e\x8a\x97\x09\x76\x9b\x6a\xc4\x2d\x94\x9e\x93\xe4\xb4\x22\xbb\x38\x91\x2c\x62\x24\x8c\x6c\x80\xdd\x64\xe7\xa2\xce\xb7\xc4\x8a\xdd\xce\x90\x86\x32\xee\xa2\x73\xe9\x96\x63\x35\xbf\x44\x7a\x00\x9c\x5b\xc0\x85\x3c\x37\x5c\xee\x62\xbc\x01\x49\xe2\xdd\xe7\x88\xf4\xd3\x63\xdb\x7c\x4e\x9c\xaa\xe6\x3f\xc6\xd2\xcc\x6d\x17\x12\xa7\xee\xfb\x9e\x38\x16\xce\x6b\x28\x46\x51\xa9\x48\x61\xfa\x06\x04\x15\x88\xeb\x99\xb4\x6b\xf6\xf3\x4a\x94\xa1\x04\xbd\x20\x76\x7d\x13\x44\xea\xab\x0b\x61\x72\x53\xff\x38\x2b\x74\xd1\x0b\x44\xc4\x9a\x90\x7a\x73\xd5\x3c\xab\x1b\x4f\xb1\x86\x8e\x30\x4a\x14\xc4\x0f\x7b\xd0\x91\x23\x69\x54\x1e\x3e\xcc\xcc\x09\x1f\xef\xbf\x27\x79\x03\xac\x41\x99\xf3\x7b\xe4\x88\xa8\xb3\x2a\xcd\x71\xf3\x29\x92\x45\x4e\xdb\x0f\x6e\xe1\x25\x2d\xc3\x58\xd1\x20\x84\x7d\xc0\xa4\x40\x7d\x71\xba\x7e\x4e\xb9\x6f\xb7\xbe\xbb\xda\x4b\xa4\x07\xca\x60\x07\xe3\x5b\xcb\x46\x08\x06\x84\x8f\xa0\x07\x81\x94\xb9\xe0\xec\x38\x83\x52\x69\x82\xd1\x0b\x85\x82\xa2\x46\xaa\x8f\xb9\x90\x7a\xf1\x3e\x43\xed\xab\x5c\xd1\x6f\x30\x8e\xe6\x19\xef\x3b\x41\xd9\x88\xe7\xa8\x0a\x7d\x5b\xbd\x56\xba\xa4\x3c\x17\x12\x78\xd4\x3b\x4a\x0b\x99\xef\x90\x14\x90\x4b\x40\x2a\x4a\x9f\x81\x6b\x3b\xd6\x65\x8d\xc4\xec\x7f\x29\x46\xd1\x1d\x27\x2c\xe6\x68\x5d\xc9\xed\x8d\x17\x0b\xad\xe3\xe1\xae\x19\xad\x68\xf8\x1c\x78\x00\x76\x46\x0d\x68\xf1\xdf\x0f\xfb\x13\x90\x7f\xd6\x94\x72\x0d\x3b\x40\x1f\x52\x4e\x74\x1d\xd3\x4d\xc7\x8c\x6e\x63\x4f\x70\x1c\xd0\x09\x3d\x1a\x06\x25\xb6\xda\xcf\xe0\xeb\x45\xbc\x7a\x8d\x86\xbf\x8d\xbc\x75\xa7\x48\xe6\xa5\xbf\x0a\xce\x5d\x35\xb2\x20\xa2\x9e\xbc\x88\x5a\xab\x68\x63\xd8\xd0\x70\x35\xd5\xd4\x0c\xa4\xd6\x14\x73\x51\xbc\x30\x8d\x92\x39\x04\x25\xf5\x6b\xbb\x72\x2c\xbb\x62\x8e\xec\xdc\x59\x7a\x01\xbe\x89\xa2\x4d\x1a\x9d\xc0\x4e\x4f\x37\x3b\xa2\xe0\xe4\x91\x2a\xb2\x71\x66\x6e\xbe\xc3\x6d\xb2\x11\x0f\x71\x8c\x41\xd0\x48\x9d\xb8\x74\x68\x3b\xc2\x13\x50\xbf\xe7\xe0\x40\xd3\x0a\x44\xed\xaf\x59\x2b\x3b\xbf\x3b\xa6\xd4\x9a\xcc\x46\x82\x6a\x51\xba\x31\x7d\x1e\x82\xda\xf7\x17\xd1\xc0\xcd\x39\x24\x08\x92\xd1\x82\xa8\x18\x10\xdd\x71\x41\xad\x65\x49\x34\xe4\xed\x8b\xaa\xab\xa0\x7f\x02\xf3\x25\x41\xc2\x18\x30\xaa\xaa\x39\x18\x9a\x96\xc0\xc8\xf1\xa6\xf2\xd9\xb0\x6f\x09\x65\x35\x42\x4e\x0a\xdd\xbd\x0b\x8b\xe4\x5c\x5a\x09\x4e\xb5\xf0\x22\xc4\xbc\x2d\x2b\xf2\x9a\xf7\xdb\x36\x24\xde\x03\x13\x6c\xeb\xe6\xde\x2d\xad\x4c\x50\xa2\xc6\xe2\xc2\xd9\x37\x87\xe8\x5c\xeb\x03\x19\xd3\xef\x78\x61\x3a\x82\x32\x48\x32\x5c\xfd\xa3\xfc\xd1\xd2\xd2\xf5\x99\xb9\x14\x8c\x16\xc7\xa5\x2c\x2c\x04\x6f\x9d\x3c\x27\x21\xee\xcc\x40\x93\x0e\xa6\x15\xaa\xa4\x8e\x1e\xd6\x86\xe1\x2b\xe5\xa5\xf8\x7a\xc5\x86\xcb\xa5\x92\x64\xa4\x00\x07\xef\xee\x75\xb4\xd2\x48\x28\xd7\x57\x97\xf3\x7b\xcd\xba\xa3\x9a\x0f\xf9\x19\x41\xfd\x81\x2e\xfe\x26\x35\x80\xf4\x85\xac\xa3\xf3\xa0\x0a\x2a\x81\xde\x04\x5c\xe0\xcd\x77\xcc\xc4\x9e\x6c\x81\xaa\x36\x6b\x80\xd8\x51\x99\xfb\xe2\xe2\xb7\x8d\xf8\x90\x30\x8b\x03\x12\x95\xa4\x5a\xea\x74\xcc\x1e\xa9\xa6\xde\x1a\x9c\x4c\x8f\x22\x92\xf0\x38\x22\xa6\x75\x5c\xf7\x8e\x42\xd5\x1b\x0e\x93\x1d\x95\xe5\x4f\xdf\x7b\xde\xf9\xd7\x94\x53\xf8\x52\x72\x1f\xe8\xf5\x6f\x43\x02\x51\x7d\x1e\x7a\xe6\xf5\xe0\xab\x6c\x76\x88\x83\xaf\x22\x96\xd3\xbf\x69\xdf\xdd\x11\x81\xaf\xcf\xbf\xb2\x13\xbc\x03\x5c\xba\x4f\x3c\x22\xd8\xd2\x51\xfd\x03\x2d\x7f\x93\x44\xfc\x79\xf9\xe5\x4c\xb3\xac\x3c\xbb\xbc\x68\x4e\xa5\xc4\xec\x31\x7e\xc7\x91\x8d\xd5\x70\xc9\x3c\x5f\xda\x8d\x61\x79\x6a\x7a\xd1\x93\x04\xc6\xba\xce\xa6\x9d\x13\xa7\x2d\x5f\x30\xc3\x1f\xdf\x4d\x14\x9f\xa9\xd7\x6d\x3f\x08\xb5\x17\x98\x0c\xf9\x63\xea\x74\xac\xbd\x77\x2f\x3f\x17\x0b\x80\x9a\xc5\x7f\xf1\xf1\x98\xb1\x93\x1f\x2f\x06\x21\xdf\xc7\xd3\xbd\xf6\xc3\xaf\x6c\xe4\x1f\x87\xa4\x7d\x79\x6d\x41\x4a\x66\x37\xf1\xa1\x30\x7a\x3f\x29\x73\x67\x8b\xfd\xa7\x5d\x99\x1f\xae\x56\xf6\xdf\xe6\x33\xbc\xd5\x69\xf5\x57\x00\x00\x00\xff\xff\x78\x30\xec\x51\x0e\x2b\x00\x00") func dataConfig_schema_v30JsonBytes() ([]byte, error) { return bindataRead( @@ -90,7 +90,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\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") +var _dataConfig_schema_v31Json = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\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( @@ -110,7 +110,7 @@ func dataConfig_schema_v31Json() (*asset, error) { return a, nil } -var _dataConfig_schema_v32Json = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xec\x1b\x4d\x73\xdc\xa8\xf2\x3e\xbf\x42\xa5\xe4\x16\x7f\xa4\xde\x4b\xbd\xaa\x97\xdb\x3b\xbe\xd3\xee\x79\x5d\x13\x15\x83\x7a\x34\xc4\x12\x10\x40\x63\x4f\x52\xfe\xef\x5b\xfa\x1c\x40\x20\xd0\x8c\x1c\x67\xb7\xf6\x64\x5b\x74\x37\xf4\x77\x37\x8d\x7f\x6c\x92\x24\x7d\x2f\xf1\x01\x2a\x94\x7e\x4e\xd2\x83\x52\xfc\xf3\xfd\xfd\x57\xc9\xe8\x6d\xf7\xf5\x8e\x89\xe2\x3e\x17\x68\xaf\x6e\x3f\x7e\xba\xef\xbe\xbd\x4b\x6f\x1a\x3c\x92\x37\x28\x98\xd1\x3d\x29\xb2\x6e\x25\x3b\xfe\xfb\xee\x5f\x77\x0d\x7a\x07\xa2\x4e\x1c\x1a\x20\xb6\xfb\x0a\x58\x75\xdf\x04\x7c\xab\x89\x80\x06\xf9\x21\x3d\x82\x90\x84\xd1\x74\x7b\xb3\x69\xd6\xb8\x60\x1c\x84\x22\x20\xd3\xcf\x49\x73\xb8\x24\x19\x41\x86\x0f\x1a\x59\xa9\x04\xa1\x45\xda\x7e\x7e\x69\x29\x24\x49\x2a\x41\x1c\x09\xd6\x28\x8c\x47\x7d\x77\x7f\xa6\x7f\x3f\x82\xdd\xd8\x54\xb5\xc3\xb6\xdf\x39\x52\x0a\x04\xfd\x7d\x7a\xb6\x76\xf9\xcb\x03\xba\xfd\xfe\xbf\xdb\x3f\x3e\xde\xfe\xf7\x2e\xbb\xdd\x7e\x78\x6f\x2c\x37\xf2\x15\xb0\xef\xb6\xcf\x61\x4f\x28\x51\x84\xd1\x71\xff\x74\x84\x7c\xe9\x7f\x7b\x19\x37\x46\x79\xde\x02\xa3\xd2\xd8\x7b\x8f\x4a\x09\x26\xcf\x14\xd4\x13\x13\x8f\x21\x9e\x47\xb0\x37\xe2\xb9\xdf\xdf\xc1\xb3\xc9\xce\x91\x95\x75\x15\xd4\xe0\x00\xf5\x46\xcc\x74\xdb\xaf\xa3\x3f\x09\x58\x80\x0a\x9b\x6c\x07\xf5\x66\x16\xdb\x6c\x7f\x1d\xc3\x9b\x81\xe9\x59\xd8\x0e\x42\xdb\xbb\x3d\xa0\xe1\xde\x2e\x51\xb9\xdc\xcb\x2f\xab\x51\x58\x1e\x29\xe5\xc0\x4b\x76\x6a\xbe\x79\xe4\xd1\x01\x54\x40\x55\x3a\x8a\x20\x49\xd2\x5d\x4d\xca\xdc\x96\x28\xa3\xf0\x5b\x43\xe2\x41\xfb\x98\x24\x3f\xec\x48\xa6\xd1\x69\xd7\x8d\xbf\xfc\x0a\x1f\xd7\x3d\xbc\x8c\xeb\x98\x51\x05\xcf\xaa\x65\x6a\x7e\xeb\x4e\x04\x0c\x3f\x82\xd8\x93\x12\x62\x31\x90\x28\xe4\x8c\xc8\x4a\x22\x55\xc6\x44\x96\x13\xac\x9c\xf8\x18\xe1\x03\x64\x7b\xc1\xaa\x20\x95\x7d\xd6\x9d\x43\xa6\x2f\x16\x9d\x09\xe1\xb0\x61\x8e\xa8\xda\x5f\xdb\x8d\x83\x60\x8a\x11\xcf\x50\x9e\x1b\x02\x41\x42\xa0\x53\x7a\x93\xa4\x44\x41\x25\xdd\xb2\x4a\xd2\x9a\x92\x6f\x35\xfc\xbf\x07\x51\xa2\x06\x9b\x6e\x2e\x18\x5f\x9f\x70\x21\x58\xcd\x33\x8e\x44\x63\xa9\xf3\x7a\x4c\x31\xab\x2a\x44\xd7\x32\xdf\x25\x7c\x44\x48\x9e\x51\x85\x08\x05\x91\x51\x54\x85\x2c\xb2\x71\x5f\xa0\xb9\xcc\xba\xca\x21\xd6\x92\x0c\x02\x63\x19\xb1\xaa\x3e\x72\x3a\xe7\x21\x1d\x99\xc6\x47\x9a\xb3\xa5\x16\x62\x26\x01\x09\x7c\xb8\x10\x9f\x55\x88\xd0\x18\xd9\x01\x55\xe2\xc4\x19\xe9\xec\xe5\x97\x33\x04\xa0\xc7\x6c\x0c\x4a\x8b\xc5\x00\xf4\x48\x04\xa3\xd5\xe0\x0d\x71\x91\x4a\xc3\x7f\xe6\x4c\x82\x2d\x18\x8b\x41\x7d\x69\x64\xd5\x90\xc9\x80\xf1\x30\x30\x7e\x93\xa4\xb4\xae\x76\x20\x9a\x62\xd8\x80\xdc\x33\x51\xa1\xe6\xb0\xc3\xde\xda\xb2\x21\x69\x87\xe5\xe9\x02\xd4\x79\x68\xea\x03\x54\x66\x25\xa1\x8f\xeb\x9b\x38\x3c\x2b\x81\xb2\x03\x93\xea\x92\x64\x90\x1e\x00\x95\xea\x80\x0f\x80\x1f\x67\xd0\x75\x28\x03\x9b\x49\x15\x63\xe4\xa4\x42\x45\x18\x88\xe3\x10\x48\x89\x76\x50\x5e\xc4\xe7\xaa\xc2\xd7\xc8\xb2\xa2\x68\x40\x7d\x16\x37\x29\x81\xfa\xe5\x50\xf1\x90\x0b\x72\x04\x11\x5b\x09\x30\x7e\xae\xdc\xec\xc5\x70\x25\x93\x84\xcb\x58\x03\xf4\xcb\x5d\x57\xc5\xce\x78\x55\xfb\x5b\x59\xa6\x5b\xbb\x5c\x48\xac\xbc\xef\xfa\x62\x71\x18\x57\x50\x18\x5a\xa9\x10\x6e\xea\x06\x01\xd2\xa3\xd7\x33\x68\xdf\x26\x65\x15\xcb\x7d\x06\x3a\x01\xb6\x65\xe3\x8d\xd4\x8b\x13\x61\x72\x51\x21\x1a\xa5\xba\x60\x27\x12\xe0\xc6\x77\xbc\xd8\x63\x9e\x8f\x1b\x36\xb1\x16\x0e\x95\x04\x49\x08\x3b\xbb\x57\x90\x06\x35\xc2\x8f\x9f\x22\x6d\xc2\x85\xfb\x9f\x59\x5c\x0f\xaa\x97\x66\x7c\x8d\x1c\x20\x75\x3e\x4a\xeb\x6e\xae\x83\x6c\x03\xde\xf6\xca\x25\x3c\x27\xb9\x3f\x56\xb4\x11\x42\x77\x30\xce\x84\x9a\x78\xd7\xf2\x74\xef\xb3\x60\x5d\x5c\x43\x9c\x3a\x27\xfc\x6e\xf3\x89\x34\x26\xea\x8e\x42\x9a\xfa\x5f\xd0\x3f\xc2\x9e\x91\xce\x44\x29\x07\xb4\x42\xa2\x00\xb3\x0d\x21\x54\x41\x01\xc2\x83\xc0\xeb\x5d\x49\xe4\x01\xf2\x25\x38\x82\x29\x86\x59\x19\xe7\x18\xce\x3e\x36\xde\x19\x4c\x82\xdb\xab\x6b\x33\x2e\xc8\x91\x94\x50\x58\x1c\xef\x18\x2b\x01\x51\x23\x51\x08\x40\x79\xc6\x68\x79\x8a\x80\x94\x0a\x89\x60\xfb\x27\x01\xd7\x82\xa8\x53\xc6\xb8\x5a\xbd\x2a\x94\x87\x2a\x93\xe4\x3b\x98\xbe\x77\xb6\xfa\x9e\xd0\xd6\x3a\x90\x75\x31\x96\xbc\x96\xfb\xf9\xcc\xf6\x95\xdc\x46\xb2\x5a\xe0\xeb\x1c\x67\x16\xbe\x36\x83\xdc\x3c\x70\xb1\x04\x78\xe2\xf0\xbd\x0a\x43\x35\xd4\xac\xab\x38\x03\xb5\x3c\x49\xac\x2e\xab\xad\xa5\xca\x09\xcd\x18\x07\x1a\xf4\x0d\xa9\x18\xcf\x0a\x81\x30\x64\x1c\x04\x61\x4e\x51\x18\x01\x36\xaf\x05\x6a\xf6\x9f\x92\x91\xa4\xa0\xc8\x1d\x77\x34\x50\x55\xf1\xfd\x85\x97\x00\x4a\x85\x9d\xbd\x2e\x49\x45\xfc\x4e\xe3\xb0\xda\x88\x7a\xad\xab\xd5\xdc\x25\xda\x4c\x79\x16\x15\xb2\x67\x3a\x84\xf9\x06\x21\xa2\x33\x38\x20\xb1\x20\x75\xb4\x8e\xb9\xf7\xe4\x27\x57\xdf\xe0\x3c\x97\x31\xe2\x6a\xe9\xdd\xf4\x07\xd9\x3a\xe1\x17\x95\x5e\xf6\x31\xb6\xde\xea\xc7\xed\x54\xb5\x0c\x36\x71\x2d\x0c\x95\x73\x0d\xc8\x08\x3a\x9d\xd5\x24\x7f\x89\x08\x6d\xe8\xa8\x05\x77\xe8\x26\x22\x8e\xf7\x3b\x45\xc6\xce\xd7\x8e\xfa\xd1\x15\x81\x86\x83\x19\x95\x44\x2a\xa0\xf8\x14\xbf\xd1\x8e\x4c\x6e\x89\xa7\x42\x99\xef\xbb\xe2\xba\xae\x16\x0a\x15\x5d\xbc\x8d\x6e\x74\xe2\x7d\xb5\x1f\xe3\xfd\x14\x56\x28\xc3\x8c\x7b\x54\x13\xcf\xc6\xd2\x34\x6b\x5d\x5d\xcc\xd4\xa1\xbe\x90\xf1\xc4\xc4\x63\x93\x90\x72\xe2\x8e\x1c\x1b\x0b\x65\xc1\xe4\xd3\xba\xeb\x1b\x08\xb8\x46\x7a\x3a\x68\x70\x04\x3a\x3f\x5e\xec\x81\xbc\xa3\x3f\x22\xd1\xce\x1a\x7a\xb9\x12\x6d\x93\x19\xc4\x31\x9c\xef\x05\x28\x41\xac\x51\xc2\x50\x34\xe9\xb9\x1d\xe4\xaf\x79\xe1\xae\x48\x05\xac\x76\x87\xa1\x8d\x6e\x38\x3d\x52\xaa\x8d\x46\x03\x4a\xd5\x20\x6d\x9d\x3e\x8c\x4a\x1d\xfa\xf2\xa0\xe2\x62\x12\x16\xd0\xbc\x1d\x6d\x44\x65\x37\x01\xbc\x24\x18\xc9\x50\x05\x71\xc5\x2d\x70\xcd\x73\xa4\x20\xeb\xde\xd1\x2c\xaa\xd9\x66\x8a\x35\x8e\x04\x2a\x4b\x28\x89\xac\x62\x8a\x9f\x34\x87\x12\x39\xa3\x7f\xb0\xee\x6d\xd1\xf7\x88\x94\xb5\x80\x0c\x61\x6f\x98\xb6\x30\x2a\x46\x89\x62\xce\x70\x12\xb7\x65\x85\x9e\xb3\x61\xdb\x16\x24\xd4\x92\x98\xdd\x78\xec\x05\xae\x66\x09\x5d\xee\x5e\x56\x56\xcf\xa8\xe8\x5c\xa4\x7b\x2c\x66\xd8\x71\xc2\xba\x00\xd9\x84\x9d\xf1\x7e\x3d\x88\x1f\x0c\xf0\xfd\xf5\x40\xc6\x59\x49\xba\x2a\x60\x0d\x0e\x31\xa3\x9d\x90\x63\x0c\xe2\x4a\x0b\x6c\xcc\xa1\xe9\x61\x2a\xae\x82\xce\xda\x22\x3c\x11\x9a\xb3\xa7\x05\x1b\xae\x67\x4a\xbc\x44\x18\xac\xe0\x78\xad\xa0\xa5\x12\x88\x50\xb5\x78\x9c\x74\x2d\x5b\x57\xa4\xfe\xd1\x3e\x03\x29\x62\x84\x0b\x26\x7d\x5f\x5a\xc0\xbc\x0e\x0e\x5d\x2a\xa8\x98\x70\x17\xc0\x57\xf0\x38\xbc\x78\x0b\xb0\x38\x80\xad\x90\x02\xa3\xa6\x74\x3d\x54\xc6\xf8\xfa\xd7\x04\xe1\x49\xdc\x36\x1c\x90\x08\x47\xd5\x5a\xde\x11\x3d\xb7\x4c\x9d\x39\x38\x99\x6f\x67\x13\x7f\x4b\x1b\x3a\x75\xf8\xec\x3d\x84\xac\x77\xd4\xd3\x05\x4e\x9b\x81\x35\x6f\xb3\x57\x0c\x7a\xc3\x93\x03\x8f\x56\x1f\xc6\x02\xfb\x66\x94\xd5\x36\x5a\xc5\xde\x79\xff\x7a\xe7\x6f\x6b\x7d\xfb\x6e\xcf\xd5\x14\x20\xa5\x10\x3e\x44\xf5\x0f\x0b\x8b\xc6\x2b\xe2\xd0\xa4\xcb\x75\x86\xa1\x1e\xea\x9f\x28\xf4\x37\xb1\xd9\x9f\x67\x5f\xfd\xdb\xe0\xe0\xa3\xdc\x16\xea\xe2\x3c\x1e\xf1\x12\xf5\x17\xd0\xd9\x5b\xab\xc2\x1c\x1e\x68\x2a\x99\xde\x25\xcc\x49\x72\xe9\xeb\xdb\xad\x79\x0c\x1b\xcc\xf1\xef\x1b\x66\x32\x9d\x1b\x2d\x0e\x20\x9e\xbb\x2b\x6b\xd3\x5e\x88\xf3\x9c\xaf\x18\x6c\xee\x3e\xcc\x94\x0c\x73\x2f\x91\x5e\x29\xd7\xae\x30\xb6\x75\xeb\xd4\xea\x33\x06\xe9\x4e\x9f\xe4\x7b\xfc\x5f\xc3\x9f\x3c\xd0\x6f\xf8\xa4\xa7\xc9\x5d\xd7\x0f\xf3\xa2\xbe\x7b\x5c\xbf\x35\xe4\x63\x81\x74\xef\xfa\xb4\xe8\xbe\xd5\x5b\x2f\x9f\x1a\x9d\xcf\xf6\xed\x31\xc1\xf0\x7c\xde\x33\xb9\xdc\xe8\x3f\xdb\x7f\x75\xd8\xbc\x6c\xfe\x0c\x00\x00\xff\xff\xa9\x16\x7b\x3d\x63\x35\x00\x00") +var _dataConfig_schema_v32Json = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x1b\x4d\x73\xdc\xa8\xf2\x3e\xbf\x42\xa5\xe4\x16\x7f\xa4\xde\x4b\xbd\xaa\x97\xdb\x3b\xbe\xd3\xee\x79\x5d\x13\x15\x83\x7a\x34\xc4\x12\x10\x40\x63\x4f\x52\xfe\xef\x5b\xfa\x1c\x40\x20\xd0\x8c\x1c\x67\xb7\xf6\x64\x5b\x74\x37\xf4\x77\x37\x8d\x7f\x6c\x92\x24\x7d\x2f\xf1\x01\x2a\x94\x7e\x4e\xd2\x83\x52\xfc\xf3\xfd\xfd\x57\xc9\xe8\x6d\xf7\xf5\x8e\x89\xe2\x3e\x17\x68\xaf\x6e\x3f\x7e\xba\xef\xbe\xbd\x4b\x6f\x1a\x3c\x92\x37\x28\x98\xd1\x3d\x29\xb2\x6e\x25\x3b\xfe\xfb\xee\x5f\x77\x0d\x7a\x07\xa2\x4e\x1c\x1a\x20\xb6\xfb\x0a\x58\x75\xdf\x04\x7c\xab\x89\x80\x06\xf9\x21\x3d\x82\x90\x84\xd1\x74\x7b\xb3\x69\xd6\xb8\x60\x1c\x84\x22\x20\xd3\xcf\x49\x73\xb8\x24\x19\x41\x86\x0f\x1a\x59\xa9\x04\xa1\x45\xda\x7e\x7e\x69\x29\x24\x49\x2a\x41\x1c\x09\xd6\x28\x8c\x47\x7d\x77\x7f\xa6\x7f\x3f\x82\xdd\xd8\x54\xb5\xc3\xb6\xdf\x39\x52\x0a\x04\xfd\x7d\x7a\xb6\x76\xf9\xcb\x03\xba\xfd\xfe\xbf\xdb\x3f\x3e\xde\xfe\xf7\x2e\xbb\xdd\x7e\x78\x6f\x2c\x37\xf2\x15\xb0\xef\xb6\xcf\x61\x4f\x28\x51\x84\xd1\x71\xff\x74\x84\x7c\xe9\x7f\x7b\x19\x37\x46\x79\xde\x02\xa3\xd2\xd8\x7b\x8f\x4a\x09\x26\xcf\x14\xd4\x13\x13\x8f\x21\x9e\x47\xb0\x37\xe2\xb9\xdf\xdf\xc1\xb3\xc9\xce\x91\x95\x75\x15\xd4\xe0\x00\xf5\x46\xcc\x74\xdb\xaf\xa3\x3f\x09\x58\x80\x0a\x9b\x6c\x07\xf5\x66\x16\xdb\x6c\x7f\x1d\xc3\x9b\x81\xe9\x59\xd8\x0e\x42\xdb\xbb\x3d\xa0\xe1\xde\x2e\x51\xb9\xdc\xcb\x2f\xab\x51\x58\x1e\x29\xe5\xc0\x4b\x76\x6a\xbe\x79\xe4\xd1\x01\x54\x40\x55\x3a\x8a\x20\x49\xd2\x5d\x4d\xca\xdc\x96\x28\xa3\xf0\x5b\x43\xe2\x41\xfb\x98\x24\x3f\xec\x48\xa6\xd1\x69\xd7\x8d\xbf\xfc\x0a\x1f\xd7\x3d\xbc\x8c\xeb\x98\x51\x05\xcf\xaa\x65\x6a\x7e\xeb\x4e\x04\x0c\x3f\x82\xd8\x93\x12\x62\x31\x90\x28\xe4\x8c\xc8\x4a\x22\x55\xc6\x44\x96\x13\xac\x9c\xf8\x25\xda\x41\x79\x15\x05\x8c\xf0\x01\xb2\xbd\x60\x55\x90\xca\x3e\xeb\x38\x91\xe9\x8b\x45\x67\x42\x38\x6c\xda\x23\xaa\xf6\xd7\x76\xe3\x20\x98\x62\xc4\x33\x94\xe7\x86\x48\x91\x10\xe8\x94\xde\x24\x29\x51\x50\x49\xb7\xb4\x93\xb4\xa6\xe4\x5b\x0d\xff\xef\x41\x94\xa8\xc1\xa6\x9b\x0b\xc6\xd7\x27\x5c\x08\x56\xf3\x8c\x23\xd1\xd8\xfa\xbc\x25\xa4\x98\x55\x15\xa2\x6b\x39\xc0\x12\x3e\x22\x24\xcf\xa8\x42\x84\x82\xc8\x28\xaa\x42\x36\xdd\x04\x00\xa0\xb9\xcc\xba\xda\x23\xd6\x92\x0c\x02\x63\x21\xb2\xaa\x3e\x72\x3a\xe7\x21\x1d\x99\xc6\x47\x9a\xb3\xa5\x16\x62\x26\x01\x09\x7c\xb8\x10\x9f\x55\x88\xd0\x18\xd9\x01\x55\xe2\xc4\x19\xe9\xec\xe5\x97\x33\x04\xa0\xc7\x6c\x0c\x6b\x8b\xc5\x00\xf4\x48\x04\xa3\xd5\xe0\x0d\x71\x91\x4a\xc3\x7f\xe6\x4c\x82\x2d\x18\x8b\x41\x7d\x69\x64\xd5\x90\xc9\x80\xf1\x30\x30\x7e\x93\xa4\xb4\xae\x76\x20\x9a\x72\xda\x80\xdc\x33\x51\xa1\xe6\xb0\xc3\xde\xda\xb2\x21\x69\x87\xe5\xe9\x02\xd4\x79\x68\x2a\x0c\x54\x66\x25\xa1\x8f\xeb\x9b\x38\x3c\x2b\x81\xb2\x03\x93\xea\x92\x64\x90\x1e\x00\x95\xea\x80\x0f\x80\x1f\x67\xd0\x75\x28\x03\x9b\x49\x15\x63\xe4\xa4\x42\x45\x18\x88\xe3\x10\xc8\xc5\x49\x2f\x5d\x55\xf8\x1a\x59\x56\x14\x0d\xa8\xcf\xe2\x26\x45\x54\xbf\x1c\x2a\x3f\x72\x41\x8e\x20\x62\x6b\x09\xc6\xcf\xb5\x9f\xbd\x18\xae\x85\x92\x70\x21\x6c\x80\x7e\xb9\xeb\xea\xe0\x19\xaf\x6a\x7f\x2b\xcb\x74\x6b\x97\x0b\x89\x95\xf7\x5d\x5f\x2c\x0e\xe3\x0a\x0a\x43\x2b\x15\xc2\x4d\xdd\x20\x40\x7a\xf4\x7a\x06\xed\x1b\xad\xac\x62\xb9\xcf\x40\x27\xc0\xb6\x6c\xbc\x91\x7a\x71\x22\x4c\x2e\x2a\x65\xa3\x54\x17\xec\x65\x02\xdc\xf8\x8e\x17\x7b\xcc\xf3\x71\xc3\x26\xd6\xc2\xa1\x92\x20\x09\x61\x67\xf7\x0a\xd2\xa0\x46\xf8\xf1\x53\xa4\x4d\xb8\x70\xff\x33\x8b\xeb\x41\xf5\xd2\x8c\xaf\x91\x03\xa4\xce\x47\x69\xdd\xcd\x75\x90\x6d\xc0\xdb\x5e\xb9\x84\xe7\x24\xf7\xc7\x8a\x36\x42\xe8\x0e\xc6\x99\x50\x13\xef\x5a\x9e\xee\x7d\x16\xac\x8b\x6b\x88\x53\xe7\x84\xdf\x6d\x3e\x91\xc6\x44\xdd\x51\x48\x53\xff\x0b\xfa\x47\xd8\x33\xd2\x99\x28\xe5\x80\x56\x48\x14\x60\xb6\x21\x84\x2a\x28\x40\x78\x10\x78\xbd\x2b\x89\x3c\x40\xbe\x04\x47\x30\xc5\x30\x2b\xe3\x1c\xc3\xd9\x09\xc7\x3b\x83\x49\x70\x7b\x75\x6d\xc6\x05\x39\x92\x12\x0a\x8b\xe3\x1d\x63\x25\x20\x6a\x24\x0a\x01\x28\xcf\x18\x2d\x4f\x11\x90\x52\x21\x11\x6c\xff\x24\xe0\x5a\x10\x75\xca\x18\x57\xab\x57\x85\xf2\x50\x65\x92\x7c\x07\xd3\xf7\xce\x56\xdf\x13\xda\x5a\x07\xb2\xae\xd6\x92\xd7\x72\x3f\x9f\xd9\xbe\x92\xdb\x48\x56\x0b\x7c\x9d\xe3\xcc\xc2\xd7\x66\x90\x9b\x07\x2e\x96\x00\x4f\x1c\xbe\x57\x61\xa8\x86\x9a\x75\x15\x67\xa0\x96\x27\x89\xd5\x65\xb5\xb5\x54\x39\xa1\x19\xe3\x40\x83\xbe\x21\x15\xe3\x59\x21\x10\x86\x8c\x83\x20\xcc\x29\x0a\x23\xc0\xe6\xb5\x40\xcd\xfe\x53\x32\x92\x14\x14\xb9\xe3\x8e\x06\xaa\x2a\xbe\xbf\xf0\x12\x40\xa9\xb0\xb3\xd7\x25\xa9\x88\xdf\x69\x1c\x56\x1b\x51\xaf\x75\xb5\x9a\xbb\x44\x9b\x29\xcf\xa2\x42\xf6\x4c\x87\x30\xdf\x20\x44\x74\x06\x07\x24\x16\xa4\x8e\xd6\x31\xf7\x9e\xfc\xe4\xea\x1b\x9c\xe7\x32\x86\x64\x2d\xbd\x9b\xfe\x20\x5b\x27\xfc\xa2\xd2\xcb\x3e\xc6\xd6\x5b\xfd\xb8\x9d\xaa\x96\xc1\x26\xae\x85\xa1\x72\xae\x01\x19\x41\xa7\xd3\x9e\xe4\x2f\x11\xa1\x0d\x1d\xb5\xe0\x0e\xdd\x44\xc4\xf1\x7e\xa7\xc8\xd8\xf9\xda\x51\x3f\xba\x22\xd0\x70\x30\xa3\x92\x48\x05\x14\x9f\xe2\x37\xda\x91\xc9\x2d\xf1\x54\x28\xf3\x7d\x57\x5c\xd7\xd5\x42\xa1\xa2\x8b\xb7\xd1\x8d\x4e\xbc\xaf\xf6\x83\xc0\x9f\xc2\x0a\x65\x98\x71\x8f\x6a\xe2\xd9\x58\x9a\x66\xad\xab\x8b\x99\x3a\xd4\x17\x32\x9e\x98\x78\x6c\x12\x52\x4e\xdc\x91\x63\x63\xa1\x2c\x98\x9d\x5a\x77\x7d\x03\x01\xd7\x50\x50\x07\x0d\x0e\x51\xe7\x07\x94\x3d\x90\x77\x78\x48\x24\xda\x59\x63\x33\x57\xa2\x6d\x32\x83\x38\x86\xf3\xbd\x00\x25\x88\x35\x4a\x18\x8a\x26\x3d\xb7\x83\xfc\x35\x2f\xdc\x15\xa9\x80\xd5\xee\x30\xb4\xd1\x0d\xa7\x47\x4a\xb5\xe1\x6a\x40\xa9\x1a\xa4\xad\xd3\x87\x51\xa9\x43\x5f\x1e\x54\x5c\x4c\xc2\x02\x9a\xb7\xa3\x8d\xa8\xec\x26\x80\x97\x04\x23\x19\xaa\x20\xae\xb8\x05\xae\x79\x8e\x14\x64\xdd\x4b\x9c\x45\x35\xdb\x4c\xb1\xc6\x91\x40\x65\x09\x25\x91\x55\x4c\xf1\x93\xe6\x50\x22\x67\xf4\x0f\xd6\xbd\x2d\xfa\x1e\x91\xb2\x16\x90\x21\xec\x0d\xd3\x16\x46\xc5\x28\x51\xcc\x19\x4e\xe2\xb6\xac\xd0\x73\x36\x6c\xdb\x82\x84\x5a\x12\xb3\x1b\x8f\xbd\xc0\xd5\x2c\xa1\xcb\xdd\xcb\xca\xea\x19\x15\x9d\x8b\x74\x8f\xc5\x0c\x3b\x4e\x58\x17\x20\x9b\xb0\x33\xde\xaf\x07\xf1\x83\x01\xbe\xbf\x1e\xc8\x38\x2b\x49\x57\x05\xac\xc1\x21\x66\xb4\x13\x72\x8c\x41\x5c\x69\x81\x8d\x39\x34\x3d\x4c\xc5\x55\xd0\x59\x5b\x84\x27\x42\x73\xf6\xb4\x60\xc3\xf5\x4c\x89\x97\x08\x83\x15\x1c\xaf\x15\xb4\x54\x02\x11\xaa\x16\x8f\x93\xae\x65\xeb\x8a\xd4\x3f\xda\x67\x20\x45\x8c\x70\xc1\xa4\xef\x4b\x0b\x98\xd7\xc1\xa1\x4b\x05\x15\x13\xee\x02\xf8\x0a\x1e\x87\x37\x73\x01\x16\x07\xb0\x15\x52\x60\xd4\x94\xae\x87\xca\x18\x5f\xff\x9a\x20\x3c\x89\xdb\x86\x03\x12\xe1\xa8\x5a\xcb\x3b\xa2\xe7\x96\xa9\x33\x07\x27\xf3\xed\x6c\xe2\x6f\x69\x43\xa7\x0e\x9f\xbd\x87\x90\xf5\x8e\x7a\xba\xc0\x69\x33\xb0\xe6\x6d\xf6\x8a\x41\x6f\x78\x72\xe0\xd1\xea\xc3\x58\x60\xdf\x8c\xb2\xda\x46\xab\xd8\x3b\xef\x5f\xef\xfc\x6d\xad\x6f\xdf\xed\xb9\x9a\x02\xa4\x14\xc2\x87\xa8\xfe\x61\x61\xd1\x78\x45\x1c\x9a\x74\xb9\xce\x30\xd4\x43\xfd\x13\x85\xfe\x26\x36\xfb\xf3\xec\xab\x7f\x5d\x1c\x7c\xd6\xdb\x42\x5d\x9c\xc7\x23\xde\xb2\xfe\x02\x3a\x7b\x6b\x55\x98\xc3\x03\x4d\x25\xd3\xbb\x84\x39\x49\x2e\x7d\x7d\xbb\x35\x8f\x61\x83\x39\xfe\x01\xc4\x4c\xa6\x73\xa3\xc5\x01\xc4\x73\x77\x65\x6d\xda\x0b\x71\x9e\xf3\x15\x83\xcd\xdd\x87\x99\x92\x61\xee\x25\xd2\x2b\xe5\xda\x15\xc6\xb6\x6e\x9d\x5a\x7d\xc6\x20\xdd\xe9\xa3\x7e\x8f\xff\x6b\xf8\x93\x27\xfe\x0d\x9f\xf4\x34\xb9\xeb\xfa\x61\x5e\xd4\x77\xcf\xf3\xb7\x86\x7c\x2c\x90\xee\x5d\x9f\x16\xdd\xb7\x7a\xeb\xe5\x53\xa3\xf3\xe1\xbf\x3d\x26\x18\x1e\xe0\x7b\x26\x97\x1b\xfd\x67\xfb\xcf\x12\x9b\x97\xcd\x9f\x01\x00\x00\xff\xff\x0c\x18\x50\x44\xa5\x35\x00\x00") func dataConfig_schema_v32JsonBytes() ([]byte, error) { return bindataRead( From 9c02b38bec54794371f780408b0d44442fc9e348 Mon Sep 17 00:00:00 2001 From: Colin Hebert Date: Sat, 15 Apr 2017 13:00:23 +1000 Subject: [PATCH 06/18] Regenerate schema with the correct libraries Signed-off-by: Colin Hebert --- cli/compose/schema/bindata.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/compose/schema/bindata.go b/cli/compose/schema/bindata.go index f850076008..7ce0fc0113 100644 --- a/cli/compose/schema/bindata.go +++ b/cli/compose/schema/bindata.go @@ -70,7 +70,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _dataConfig_schema_v30Json = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5a\x4b\x8f\xdb\x38\x12\xbe\xfb\x57\x08\x4a\x6e\x71\x77\x07\xd8\x60\x81\xcd\x6d\x8f\x7b\xda\x39\x4f\x43\x11\x68\xa9\x6c\x33\x4d\x91\x4c\x91\x72\xda\x09\xfc\xdf\x07\xd4\xcb\x14\x4d\x8a\xb2\xad\x3c\x30\x98\x53\xb7\xc5\xaa\x62\xbd\xf8\x55\xb1\xa4\xef\xab\x24\x49\xdf\xaa\x62\x0f\x15\x49\x3f\x26\xe9\x5e\x6b\xf9\xf1\xe9\xe9\xb3\x12\xfc\xa1\x7d\xfa\x28\x70\xf7\x54\x22\xd9\xea\x87\xf7\x1f\x9e\xda\x67\x6f\xd2\xb5\xe1\xa3\xa5\x61\x29\x04\xdf\xd2\x5d\xde\xae\xe4\x87\x7f\x3d\xbe\x7f\x34\xec\x2d\x89\x3e\x4a\x30\x44\x62\xf3\x19\x0a\xdd\x3e\x43\xf8\x52\x53\x04\xc3\xfc\x9c\x1e\x00\x15\x15\x3c\xcd\xd6\x2b\xb3\x26\x51\x48\x40\x4d\x41\xa5\x1f\x13\xa3\x5c\x92\x0c\x24\xfd\x03\x4b\xac\xd2\x48\xf9\x2e\x6d\x1e\x9f\x1a\x09\x49\x92\x2a\xc0\x03\x2d\x2c\x09\x83\xaa\x6f\x9e\xce\xf2\x9f\x06\xb2\xb5\x2b\xd5\x52\xb6\x79\x2e\x89\xd6\x80\xfc\x8f\x4b\xdd\x9a\xe5\x4f\xcf\xe4\xe1\xdb\x7f\x1f\xfe\x7c\xff\xf0\x9f\xc7\xfc\x21\x7b\xf7\x76\xb4\x6c\xfc\x8b\xb0\x6d\xb7\x2f\x61\x4b\x39\xd5\x54\xf0\x61\xff\x74\xa0\x3c\x75\xff\x9d\x86\x8d\x49\x59\x36\xc4\x84\x8d\xf6\xde\x12\xa6\x60\x6c\x33\x07\xfd\x55\xe0\x4b\xcc\xe6\x81\xec\x17\xd9\xdc\xed\xef\xb1\x79\x6c\xce\x41\xb0\xba\x8a\x46\xb0\xa7\xfa\x45\xc6\xb4\xdb\xdf\x17\xbf\x55\x6f\xf4\x24\x6d\x4b\x61\xed\xdd\x28\x38\xca\x76\x9f\xab\x7c\xd9\x16\xf6\xd5\xe0\xac\x80\x97\x4a\x90\x4c\x1c\xcd\xb3\x80\x3f\x5a\x82\x0a\xb8\x4e\x07\x17\x24\x49\xba\xa9\x29\x2b\x5d\x8f\x0a\x0e\xff\x37\x22\x9e\xad\x87\x49\xf2\xdd\x3d\xd8\x96\x9c\x66\x7d\xf4\x2b\x1c\xf0\x61\x3d\x60\xcb\xb0\x5e\x08\xae\xe1\x55\x37\x46\x4d\x6f\xdd\xba\x40\x14\x2f\x80\x5b\xca\x60\x2e\x07\xc1\x9d\x9a\x70\x19\xa3\x4a\xe7\x02\xf3\x92\x16\x3a\x3d\x39\xec\x17\xf2\xe2\xf9\x34\xb0\x5a\xbf\xb2\x95\x47\x60\x5a\x10\x99\x93\xb2\x1c\xd9\x41\x10\xc9\x31\x5d\x27\x29\xd5\x50\x29\xbf\x89\x49\x5a\x73\xfa\xa5\x86\xff\x75\x24\x1a\x6b\x70\xe5\x96\x28\xe4\xf2\x82\x77\x28\x6a\x99\x4b\x82\x26\xc1\xa6\xdd\x9f\x16\xa2\xaa\x08\x5f\x2a\xeb\xae\xb1\x63\x86\xe7\x05\xd7\x84\x72\xc0\x9c\x93\x2a\x96\x48\xe6\xd4\x01\x2f\x55\xde\xd6\xbf\xc9\x34\xda\xe6\x2d\xbf\x72\x04\x0c\xc5\x70\xd1\x78\x94\x7c\x2a\xb1\x5b\x31\x26\xb5\x8d\x6e\xa9\xc3\x98\x2b\x20\x58\xec\x6f\xe4\x17\x15\xa1\x7c\x8e\xef\x80\x6b\x3c\x4a\x41\xdb\x7c\xf9\xed\x12\x01\xf8\x21\x1f\xb0\xe4\x6a\x37\x00\x3f\x50\x14\xbc\xea\x4f\xc3\x1c\x80\x19\x40\xde\xf0\xbf\x4a\xa1\xc0\x75\x8c\x63\xa0\xbd\x34\x98\x3a\xf2\x49\xcf\xf1\xdc\x1b\xbe\x4e\x52\x5e\x57\x1b\x40\xd3\xd2\x8d\x28\xb7\x02\x2b\x62\x94\xed\xf7\xb6\x96\x47\x9e\xf6\x64\x9e\xed\x40\xdb\x06\x53\xd6\x09\xcb\x19\xe5\x2f\xcb\xa7\x38\xbc\x6a\x24\xf9\x5e\x28\x3d\x1f\xc3\x2d\xf6\x3d\x10\xa6\xf7\xc5\x1e\x8a\x97\x09\x76\x9b\x6a\xc4\x2d\x94\x9e\x93\xe4\xb4\x22\xbb\x38\x91\x2c\x62\x24\x8c\x6c\x80\xdd\x64\xe7\xa2\xce\xb7\xc4\x8a\xdd\xce\x90\x86\x32\xee\xa2\x73\xe9\x96\x63\x35\xbf\x44\x7a\x00\x9c\x5b\xc0\x85\x3c\x37\x5c\xee\x62\xbc\x01\x49\xe2\xdd\xe7\x88\xf4\xd3\x63\xdb\x7c\x4e\x9c\xaa\xe6\x3f\xc6\xd2\xcc\x6d\x17\x12\xa7\xee\xfb\x9e\x38\x16\xce\x6b\x28\x46\x51\xa9\x48\x61\xfa\x06\x04\x15\x88\xeb\x99\xb4\x6b\xf6\xf3\x4a\x94\xa1\x04\xbd\x20\x76\x7d\x13\x44\xea\xab\x0b\x61\x72\x53\xff\x38\x2b\x74\xd1\x0b\x44\xc4\x9a\x90\x7a\x73\xd5\x3c\xab\x1b\x4f\xb1\x86\x8e\x30\x4a\x14\xc4\x0f\x7b\xd0\x91\x23\x69\x54\x1e\x3e\xcc\xcc\x09\x1f\xef\xbf\x27\x79\x03\xac\x41\x99\xf3\x7b\xe4\x88\xa8\xb3\x2a\xcd\x71\xf3\x29\x92\x45\x4e\xdb\x0f\x6e\xe1\x25\x2d\xc3\x58\xd1\x20\x84\x7d\xc0\xa4\x40\x7d\x71\xba\x7e\x4e\xb9\x6f\xb7\xbe\xbb\xda\x4b\xa4\x07\xca\x60\x07\xe3\x5b\xcb\x46\x08\x06\x84\x8f\xa0\x07\x81\x94\xb9\xe0\xec\x38\x83\x52\x69\x82\xd1\x0b\x85\x82\xa2\x46\xaa\x8f\xb9\x90\x7a\xf1\x3e\x43\xed\xab\x5c\xd1\x6f\x30\x8e\xe6\x19\xef\x3b\x41\xd9\x88\xe7\xa8\x0a\x7d\x5b\xbd\x56\xba\xa4\x3c\x17\x12\x78\xd4\x3b\x4a\x0b\x99\xef\x90\x14\x90\x4b\x40\x2a\x4a\x9f\x81\x6b\x3b\xd6\x65\x8d\xc4\xec\x7f\x29\x46\xd1\x1d\x27\x2c\xe6\x68\x5d\xc9\xed\x8d\x17\x0b\xad\xe3\xe1\xae\x19\xad\x68\xf8\x1c\x78\x00\x76\x46\x0d\x68\xf1\xdf\x0f\xfb\x13\x90\x7f\xd6\x94\x72\x0d\x3b\x40\x1f\x52\x4e\x74\x1d\xd3\x4d\xc7\x8c\x6e\x63\x4f\x70\x1c\xd0\x09\x3d\x1a\x06\x25\xb6\xda\xcf\xe0\xeb\x45\xbc\x7a\x8d\x86\xbf\x8d\xbc\x75\xa7\x48\xe6\xa5\xbf\x0a\xce\x5d\x35\xb2\x20\xa2\x9e\xbc\x88\x5a\xab\x68\x63\xd8\xd0\x70\x35\xd5\xd4\x0c\xa4\xd6\x14\x73\x51\xbc\x30\x8d\x92\x39\x04\x25\xf5\x6b\xbb\x72\x2c\xbb\x62\x8e\xec\xdc\x59\x7a\x01\xbe\x89\xa2\x4d\x1a\x9d\xc0\x4e\x4f\x37\x3b\xa2\xe0\xe4\x91\x2a\xb2\x71\x66\x6e\xbe\xc3\x6d\xb2\x11\x0f\x71\x8c\x41\xd0\x48\x9d\xb8\x74\x68\x3b\xc2\x13\x50\xbf\xe7\xe0\x40\xd3\x0a\x44\xed\xaf\x59\x2b\x3b\xbf\x3b\xa6\xd4\x9a\xcc\x46\x82\x6a\x51\xba\x31\x7d\x1e\x82\xda\xf7\x17\xd1\xc0\xcd\x39\x24\x08\x92\xd1\x82\xa8\x18\x10\xdd\x71\x41\xad\x65\x49\x34\xe4\xed\x8b\xaa\xab\xa0\x7f\x02\xf3\x25\x41\xc2\x18\x30\xaa\xaa\x39\x18\x9a\x96\xc0\xc8\xf1\xa6\xf2\xd9\xb0\x6f\x09\x65\x35\x42\x4e\x0a\xdd\xbd\x0b\x8b\xe4\x5c\x5a\x09\x4e\xb5\xf0\x22\xc4\xbc\x2d\x2b\xf2\x9a\xf7\xdb\x36\x24\xde\x03\x13\x6c\xeb\xe6\xde\x2d\xad\x4c\x50\xa2\xc6\xe2\xc2\xd9\x37\x87\xe8\x5c\xeb\x03\x19\xd3\xef\x78\x61\x3a\x82\x32\x48\x32\x5c\xfd\xa3\xfc\xd1\xd2\xd2\xf5\x99\xb9\x14\x8c\x16\xc7\xa5\x2c\x2c\x04\x6f\x9d\x3c\x27\x21\xee\xcc\x40\x93\x0e\xa6\x15\xaa\xa4\x8e\x1e\xd6\x86\xe1\x2b\xe5\xa5\xf8\x7a\xc5\x86\xcb\xa5\x92\x64\xa4\x00\x07\xef\xee\x75\xb4\xd2\x48\x28\xd7\x57\x97\xf3\x7b\xcd\xba\xa3\x9a\x0f\xf9\x19\x41\xfd\x81\x2e\xfe\x26\x35\x80\xf4\x85\xac\xa3\xf3\xa0\x0a\x2a\x81\xde\x04\x5c\xe0\xcd\x77\xcc\xc4\x9e\x6c\x81\xaa\x36\x6b\x80\xd8\x51\x99\xfb\xe2\xe2\xb7\x8d\xf8\x90\x30\x8b\x03\x12\x95\xa4\x5a\xea\x74\xcc\x1e\xa9\xa6\xde\x1a\x9c\x4c\x8f\x22\x92\xf0\x38\x22\xa6\x75\x5c\xf7\x8e\x42\xd5\x1b\x0e\x93\x1d\x95\xe5\x4f\xdf\x7b\xde\xf9\xd7\x94\x53\xf8\x52\x72\x1f\xe8\xf5\x6f\x43\x02\x51\x7d\x1e\x7a\xe6\xf5\xe0\xab\x6c\x76\x88\x83\xaf\x22\x96\xd3\xbf\x69\xdf\xdd\x11\x81\xaf\xcf\xbf\xb2\x13\xbc\x03\x5c\xba\x4f\x3c\x22\xd8\xd2\x51\xfd\x03\x2d\x7f\x93\x44\xfc\x79\xf9\xe5\x4c\xb3\xac\x3c\xbb\xbc\x68\x4e\xa5\xc4\xec\x31\x7e\xc7\x91\x8d\xd5\x70\xc9\x3c\x5f\xda\x8d\x61\x79\x6a\x7a\xd1\x93\x04\xc6\xba\xce\xa6\x9d\x13\xa7\x2d\x5f\x30\xc3\x1f\xdf\x4d\x14\x9f\xa9\xd7\x6d\x3f\x08\xb5\x17\x98\x0c\xf9\x63\xea\x74\xac\xbd\x77\x2f\x3f\x17\x0b\x80\x9a\xc5\x7f\xf1\xf1\x98\xb1\x93\x1f\x2f\x06\x21\xdf\xc7\xd3\xbd\xf6\xc3\xaf\x6c\xe4\x1f\x87\xa4\x7d\x79\x6d\x41\x4a\x66\x37\xf1\xa1\x30\x7a\x3f\x29\x73\x67\x8b\xfd\xa7\x5d\x99\x1f\xae\x56\xf6\xdf\xe6\x33\xbc\xd5\x69\xf5\x57\x00\x00\x00\xff\xff\x78\x30\xec\x51\x0e\x2b\x00\x00") +var _dataConfig_schema_v30Json = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xec\x5a\x4b\x8f\xdb\x38\x12\xbe\xfb\x57\x08\x4a\x6e\x71\x77\x07\xd8\x60\x81\xcd\x6d\x8f\x7b\xda\x39\x4f\x43\x11\x68\xa9\x6c\x33\x4d\x91\x4c\x91\x72\xda\x09\xfc\xdf\x07\xd4\xcb\x14\x4d\x8a\xb2\xad\x3c\x30\x98\x53\xb7\xc5\xaa\x62\xbd\xf8\x55\xb1\xa4\xef\xab\x24\x49\xdf\xaa\x62\x0f\x15\x49\x3f\x26\xe9\x5e\x6b\xf9\xf1\xe9\xe9\xb3\x12\xfc\xa1\x7d\xfa\x28\x70\xf7\x54\x22\xd9\xea\x87\xf7\x1f\x9e\xda\x67\x6f\xd2\xb5\xe1\xa3\xa5\x61\x29\x04\xdf\xd2\x5d\xde\xae\xe4\x87\x7f\x3d\xbe\x7f\x34\xec\x2d\x89\x3e\x4a\x30\x44\x62\xf3\x19\x0a\xdd\x3e\x43\xf8\x52\x53\x04\xc3\xfc\x9c\x1e\x00\x15\x15\x3c\xcd\xd6\x2b\xb3\x26\x51\x48\x40\x4d\x41\xa5\x1f\x13\xa3\x5c\x92\x0c\x24\xfd\x03\x4b\xac\xd2\x48\xf9\x2e\x6d\x1e\x9f\x1a\x09\x49\x92\x2a\xc0\x03\x2d\x2c\x09\x83\xaa\x6f\x9e\xce\xf2\x9f\x06\xb2\xb5\x2b\xd5\x52\xb6\x79\x2e\x89\xd6\x80\xfc\x8f\x4b\xdd\x9a\xe5\x4f\xcf\xe4\xe1\xdb\x7f\x1f\xfe\x7c\xff\xf0\x9f\xc7\xfc\x21\x7b\xf7\x76\xb4\x6c\xfc\x8b\xb0\x6d\xb7\x2f\x61\x4b\x39\xd5\x54\xf0\x61\xff\x74\xa0\x3c\x75\xff\x9d\x86\x8d\x49\x59\x36\xc4\x84\x8d\xf6\xde\x12\xa6\x60\x6c\x33\x07\xfd\x55\xe0\x4b\xcc\xe6\x81\xec\x17\xd9\xdc\xed\xef\xb1\x79\x6c\xce\x41\xb0\xba\x8a\x46\xb0\xa7\xfa\x45\xc6\xb4\xdb\xdf\x17\xbf\x55\x6f\xf4\x24\x6d\x4b\x61\xed\xdd\x28\x38\xca\x76\x9f\xab\x7c\xd9\x16\xf6\xd5\xe0\xac\x80\x97\x4a\x90\x4c\x1c\xcd\xb3\x80\x3f\x5a\x82\x0a\xb8\x4e\x07\x17\x24\x49\xba\xa9\x29\x2b\x5d\x8f\x0a\x0e\xff\x37\x22\x9e\xad\x87\x49\xf2\xdd\x3d\xd8\x96\x9c\x66\x7d\xf4\x2b\x1c\xf0\x61\x3d\x60\xcb\xb0\x5e\x08\xae\xe1\x55\x37\x46\x4d\x6f\xdd\xba\x40\x14\x2f\x80\x5b\xca\x60\x2e\x07\xc1\x9d\x9a\x70\x19\xa3\x4a\xe7\x02\xf3\x92\x16\x3a\x3d\x39\xec\x17\xf2\xe2\xf9\x34\xb0\x5a\xbf\xb2\x95\x47\x60\x5a\x10\x99\x93\xb2\x1c\xd9\x41\x10\xc9\x31\x5d\x27\x29\xd5\x50\x29\xbf\x89\x49\x5a\x73\xfa\xa5\x86\xff\x75\x24\x1a\x6b\x70\xe5\x96\x28\xe4\xf2\x82\x77\x28\x6a\x99\x4b\x82\x26\xc1\xa6\xdd\x9f\x16\xa2\xaa\x08\x5f\x2a\xeb\xae\xb1\x63\x86\xe7\x05\xd7\x84\x72\xc0\x9c\x93\x2a\x96\x48\xe6\xd4\x01\x2f\x55\xde\xd6\xbf\xc9\x34\xda\xe6\x2d\xbf\x72\x04\x0c\xc5\x70\xd1\x78\x94\x7c\x2a\xb1\x5b\x31\x26\xb5\x8d\x6e\xa9\xc3\x98\x2b\x20\x58\xec\x6f\xe4\x17\x15\xa1\x7c\x8e\xef\x80\x6b\x3c\x4a\x41\xdb\x7c\xf9\xed\x12\x01\xf8\x21\x1f\xb0\xe4\x6a\x37\x00\x3f\x50\x14\xbc\xea\x4f\xc3\x1c\x80\x19\x40\xde\xf0\xbf\x4a\xa1\xc0\x75\x8c\x63\xa0\xbd\x34\x98\x3a\xf2\x49\xcf\xf1\xdc\x1b\xbe\x4e\x52\x5e\x57\x1b\x40\xd3\xd2\x8d\x28\xb7\x02\x2b\x62\x94\xed\xf7\xb6\x96\x47\x9e\xf6\x64\x9e\xed\x40\xdb\x06\x53\xd6\x09\xcb\x19\xe5\x2f\xcb\xa7\x38\xbc\x6a\x24\xf9\x5e\x28\x3d\x1f\xc3\x2d\xf6\x3d\x10\xa6\xf7\xc5\x1e\x8a\x97\x09\x76\x9b\x6a\xc4\x2d\x94\x9e\x93\xe4\xb4\x22\xbb\x38\x91\x2c\x62\x24\x8c\x6c\x80\xdd\x64\xe7\xa2\xce\xb7\xc4\x8a\xdd\xce\x90\x86\x32\xee\xa2\x73\xe9\x96\x63\x35\xbf\x44\x7a\x00\x9c\x5b\xc0\x85\x3c\x37\x5c\xee\x62\xbc\x01\x49\xe2\xdd\xe7\x88\xf4\xd3\x63\xdb\x7c\x4e\x9c\xaa\xe6\x3f\xc6\xd2\xcc\x6d\x17\x12\xa7\xee\xfb\x9e\x38\x16\xce\x6b\x28\x46\x51\xa9\x48\x61\xfa\x06\x04\x15\x88\xeb\x99\xb4\x6b\xf6\xf3\x4a\x94\xa1\x04\xbd\x20\x76\x7d\x13\x44\xea\xab\x0b\x61\x72\x53\xff\x38\x2b\x74\xd1\x0b\x44\xc4\x9a\x90\x7a\x73\xd5\x3c\xab\x1b\x4f\xb1\x86\x8e\x30\x4a\x14\xc4\x0f\x7b\xd0\x91\x23\x69\x54\x1e\x3e\xcc\xcc\x09\x1f\xef\xbf\x27\x79\x03\xac\x41\x99\xf3\x7b\xe4\x88\xa8\xb3\x2a\xcd\x71\xf3\x29\x92\x45\x4e\xdb\x0f\x6e\xe1\x25\x2d\xc3\x58\xd1\x20\x84\x7d\xc0\xa4\x40\x7d\x71\xba\x7e\x4e\xb9\x6f\xb7\xbe\xbb\xda\x4b\xa4\x07\xca\x60\x07\xe3\x5b\xcb\x46\x08\x06\x84\x8f\xa0\x07\x81\x94\xb9\xe0\xec\x38\x83\x52\x69\x82\xd1\x0b\x85\x82\xa2\x46\xaa\x8f\xb9\x90\x7a\xf1\x3e\x43\xed\xab\x5c\xd1\x6f\x30\x8e\xe6\x19\xef\x3b\x41\xd9\x88\xe7\xa8\x0a\x7d\x5b\xbd\x56\xba\xa4\x3c\x17\x12\x78\xd4\x3b\x4a\x0b\x99\xef\x90\x14\x90\x4b\x40\x2a\x4a\x9f\x81\x6b\x3b\xd6\x65\x8d\xc4\xec\x7f\x29\x46\xd1\x1d\x27\x2c\xe6\x68\x5d\xc9\xed\x8d\x17\x0b\xad\xe3\xe1\xae\x19\xad\x68\xf8\x1c\x78\x00\x76\x46\x0d\x68\xf1\xdf\x0f\xfb\x13\x90\x7f\xd6\x94\x72\x0d\x3b\x40\x1f\x52\x4e\x74\x1d\xd3\x4d\xc7\x8c\x6e\x63\x4f\x70\x1c\xd0\x09\x3d\x1a\x06\x25\xb6\xda\xcf\xe0\xeb\x45\xbc\x7a\x8d\x86\xbf\x8d\xbc\x75\xa7\x48\xe6\xa5\xbf\x0a\xce\x5d\x35\xb2\x20\xa2\x9e\xbc\x88\x5a\xab\x68\x63\xd8\xd0\x70\x35\xd5\xd4\x0c\xa4\xd6\x14\x73\x51\xbc\x30\x8d\x92\x39\x04\x25\xf5\x6b\xbb\x72\x2c\xbb\x62\x8e\xec\xdc\x59\x7a\x01\xbe\x89\xa2\x4d\x1a\x9d\xc0\x4e\x4f\x37\x3b\xa2\xe0\xe4\x91\x2a\xb2\x71\x66\x6e\xbe\xc3\x6d\xb2\x11\x0f\x71\x8c\x41\xd0\x48\x9d\xb8\x74\x68\x3b\xc2\x13\x50\xbf\xe7\xe0\x40\xd3\x0a\x44\xed\xaf\x59\x2b\x3b\xbf\x3b\xa6\xd4\x9a\xcc\x46\x82\x6a\x51\xba\x31\x7d\x1e\x82\xda\xf7\x17\xd1\xc0\xcd\x39\x24\x08\x92\xd1\x82\xa8\x18\x10\xdd\x71\x41\xad\x65\x49\x34\xe4\xed\x8b\xaa\xab\xa0\x7f\x02\xf3\x25\x41\xc2\x18\x30\xaa\xaa\x39\x18\x9a\x96\xc0\xc8\xf1\xa6\xf2\xd9\xb0\x6f\x09\x65\x35\x42\x4e\x0a\xdd\xbd\x0b\x8b\xe4\x5c\x5a\x09\x4e\xb5\xf0\x22\xc4\xbc\x2d\x2b\xf2\x9a\xf7\xdb\x36\x24\xde\x03\x13\x6c\xeb\xe6\xde\x2d\xad\x4c\x50\xa2\xc6\xe2\xc2\xd9\x37\x87\xe8\x5c\xeb\x03\x19\xd3\xef\x78\x61\x3a\x82\x32\x48\x32\x5c\xfd\xa3\xfc\xd1\xd2\xd2\xf5\x99\xb9\x14\x8c\x16\xc7\xa5\x2c\x2c\x04\x6f\x9d\x3c\x27\x21\xee\xcc\x40\x93\x0e\xa6\x15\xaa\xa4\x8e\x1e\xd6\x86\xe1\x2b\xe5\xa5\xf8\x7a\xc5\x86\xcb\xa5\x92\x64\xa4\x00\x07\xef\xee\x75\xb4\xd2\x48\x28\xd7\x57\x97\xf3\x7b\xcd\xba\xa3\x9a\x0f\xf9\x19\x41\xfd\x81\x2e\xfe\x26\x35\x80\xf4\x85\xac\xa3\xf3\xa0\x0a\x2a\x81\xde\x04\x5c\xe0\xcd\x77\xcc\xc4\x9e\x6c\x81\xaa\x36\x6b\x80\xd8\x51\x99\xfb\xe2\xe2\xb7\x8d\xf8\x90\x30\x8b\x03\x12\x95\xa4\x5a\xea\x74\xcc\x1e\xa9\xa6\xde\x1a\x9c\x4c\x8f\x22\x92\xf0\x38\x22\xa6\x75\x5c\xf7\x8e\x42\xd5\x1b\x0e\x93\x1d\x95\xe5\x4f\xdf\x7b\xde\xf9\xd7\x94\x53\xf8\x52\x72\x1f\xe8\xf5\x6f\x43\x02\x51\x7d\x1e\x7a\xe6\xf5\xe0\xab\x6c\x76\x88\x83\xaf\x22\x96\xd3\xbf\x69\xdf\xdd\x11\x81\xaf\xcf\xbf\xb2\x13\xbc\x03\x5c\xba\x4f\x3c\x22\xd8\xd2\x51\xfd\x03\x2d\x7f\x93\x44\xfc\x79\xf9\xe5\x4c\xb3\xac\x3c\xbb\xbc\x68\x4e\xa5\xc4\xec\x31\x7e\xc7\x91\x8d\xd5\x70\xc9\x3c\x5f\xda\x8d\x61\x79\x6a\x7a\xd1\x93\x04\xc6\xba\xce\xa6\x9d\x13\xa7\x2d\x5f\x30\xc3\x1f\xdf\x4d\x14\x9f\xa9\xd7\x6d\x3f\x08\xb5\x17\x98\x0c\xf9\x63\xea\x74\xac\xbd\x77\x2f\x3f\x17\x0b\x80\x9a\xc5\x7f\xf1\xf1\x98\xb1\x93\x1f\x2f\x06\x21\xdf\xc7\xd3\xbd\xf6\xc3\xaf\x6c\xe4\x1f\x87\xa4\x7d\x79\x6d\x41\x4a\x66\x37\xf1\xa1\x30\x7a\x3f\x29\x73\x67\x8b\xfd\xa7\x5d\x99\x1f\xae\x56\xf6\xdf\xe6\x33\xbc\xd5\x69\xf5\x57\x00\x00\x00\xff\xff\x78\x30\xec\x51\x0e\x2b\x00\x00") func dataConfig_schema_v30JsonBytes() ([]byte, error) { return bindataRead( @@ -90,7 +90,7 @@ func dataConfig_schema_v30Json() (*asset, error) { return a, nil } -var _dataConfig_schema_v31Json = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\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") +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( @@ -110,7 +110,7 @@ func dataConfig_schema_v31Json() (*asset, error) { return a, nil } -var _dataConfig_schema_v32Json = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x1b\x4d\x73\xdc\xa8\xf2\x3e\xbf\x42\xa5\xe4\x16\x7f\xa4\xde\x4b\xbd\xaa\x97\xdb\x3b\xbe\xd3\xee\x79\x5d\x13\x15\x83\x7a\x34\xc4\x12\x10\x40\x63\x4f\x52\xfe\xef\x5b\xfa\x1c\x40\x20\xd0\x8c\x1c\x67\xb7\xf6\x64\x5b\x74\x37\xf4\x77\x37\x8d\x7f\x6c\x92\x24\x7d\x2f\xf1\x01\x2a\x94\x7e\x4e\xd2\x83\x52\xfc\xf3\xfd\xfd\x57\xc9\xe8\x6d\xf7\xf5\x8e\x89\xe2\x3e\x17\x68\xaf\x6e\x3f\x7e\xba\xef\xbe\xbd\x4b\x6f\x1a\x3c\x92\x37\x28\x98\xd1\x3d\x29\xb2\x6e\x25\x3b\xfe\xfb\xee\x5f\x77\x0d\x7a\x07\xa2\x4e\x1c\x1a\x20\xb6\xfb\x0a\x58\x75\xdf\x04\x7c\xab\x89\x80\x06\xf9\x21\x3d\x82\x90\x84\xd1\x74\x7b\xb3\x69\xd6\xb8\x60\x1c\x84\x22\x20\xd3\xcf\x49\x73\xb8\x24\x19\x41\x86\x0f\x1a\x59\xa9\x04\xa1\x45\xda\x7e\x7e\x69\x29\x24\x49\x2a\x41\x1c\x09\xd6\x28\x8c\x47\x7d\x77\x7f\xa6\x7f\x3f\x82\xdd\xd8\x54\xb5\xc3\xb6\xdf\x39\x52\x0a\x04\xfd\x7d\x7a\xb6\x76\xf9\xcb\x03\xba\xfd\xfe\xbf\xdb\x3f\x3e\xde\xfe\xf7\x2e\xbb\xdd\x7e\x78\x6f\x2c\x37\xf2\x15\xb0\xef\xb6\xcf\x61\x4f\x28\x51\x84\xd1\x71\xff\x74\x84\x7c\xe9\x7f\x7b\x19\x37\x46\x79\xde\x02\xa3\xd2\xd8\x7b\x8f\x4a\x09\x26\xcf\x14\xd4\x13\x13\x8f\x21\x9e\x47\xb0\x37\xe2\xb9\xdf\xdf\xc1\xb3\xc9\xce\x91\x95\x75\x15\xd4\xe0\x00\xf5\x46\xcc\x74\xdb\xaf\xa3\x3f\x09\x58\x80\x0a\x9b\x6c\x07\xf5\x66\x16\xdb\x6c\x7f\x1d\xc3\x9b\x81\xe9\x59\xd8\x0e\x42\xdb\xbb\x3d\xa0\xe1\xde\x2e\x51\xb9\xdc\xcb\x2f\xab\x51\x58\x1e\x29\xe5\xc0\x4b\x76\x6a\xbe\x79\xe4\xd1\x01\x54\x40\x55\x3a\x8a\x20\x49\xd2\x5d\x4d\xca\xdc\x96\x28\xa3\xf0\x5b\x43\xe2\x41\xfb\x98\x24\x3f\xec\x48\xa6\xd1\x69\xd7\x8d\xbf\xfc\x0a\x1f\xd7\x3d\xbc\x8c\xeb\x98\x51\x05\xcf\xaa\x65\x6a\x7e\xeb\x4e\x04\x0c\x3f\x82\xd8\x93\x12\x62\x31\x90\x28\xe4\x8c\xc8\x4a\x22\x55\xc6\x44\x96\x13\xac\x9c\xf8\x25\xda\x41\x79\x15\x05\x8c\xf0\x01\xb2\xbd\x60\x55\x90\xca\x3e\xeb\x38\x91\xe9\x8b\x45\x67\x42\x38\x6c\xda\x23\xaa\xf6\xd7\x76\xe3\x20\x98\x62\xc4\x33\x94\xe7\x86\x48\x91\x10\xe8\x94\xde\x24\x29\x51\x50\x49\xb7\xb4\x93\xb4\xa6\xe4\x5b\x0d\xff\xef\x41\x94\xa8\xc1\xa6\x9b\x0b\xc6\xd7\x27\x5c\x08\x56\xf3\x8c\x23\xd1\xd8\xfa\xbc\x25\xa4\x98\x55\x15\xa2\x6b\x39\xc0\x12\x3e\x22\x24\xcf\xa8\x42\x84\x82\xc8\x28\xaa\x42\x36\xdd\x04\x00\xa0\xb9\xcc\xba\xda\x23\xd6\x92\x0c\x02\x63\x21\xb2\xaa\x3e\x72\x3a\xe7\x21\x1d\x99\xc6\x47\x9a\xb3\xa5\x16\x62\x26\x01\x09\x7c\xb8\x10\x9f\x55\x88\xd0\x18\xd9\x01\x55\xe2\xc4\x19\xe9\xec\xe5\x97\x33\x04\xa0\xc7\x6c\x0c\x6b\x8b\xc5\x00\xf4\x48\x04\xa3\xd5\xe0\x0d\x71\x91\x4a\xc3\x7f\xe6\x4c\x82\x2d\x18\x8b\x41\x7d\x69\x64\xd5\x90\xc9\x80\xf1\x30\x30\x7e\x93\xa4\xb4\xae\x76\x20\x9a\x72\xda\x80\xdc\x33\x51\xa1\xe6\xb0\xc3\xde\xda\xb2\x21\x69\x87\xe5\xe9\x02\xd4\x79\x68\x2a\x0c\x54\x66\x25\xa1\x8f\xeb\x9b\x38\x3c\x2b\x81\xb2\x03\x93\xea\x92\x64\x90\x1e\x00\x95\xea\x80\x0f\x80\x1f\x67\xd0\x75\x28\x03\x9b\x49\x15\x63\xe4\xa4\x42\x45\x18\x88\xe3\x10\xc8\xc5\x49\x2f\x5d\x55\xf8\x1a\x59\x56\x14\x0d\xa8\xcf\xe2\x26\x45\x54\xbf\x1c\x2a\x3f\x72\x41\x8e\x20\x62\x6b\x09\xc6\xcf\xb5\x9f\xbd\x18\xae\x85\x92\x70\x21\x6c\x80\x7e\xb9\xeb\xea\xe0\x19\xaf\x6a\x7f\x2b\xcb\x74\x6b\x97\x0b\x89\x95\xf7\x5d\x5f\x2c\x0e\xe3\x0a\x0a\x43\x2b\x15\xc2\x4d\xdd\x20\x40\x7a\xf4\x7a\x06\xed\x1b\xad\xac\x62\xb9\xcf\x40\x27\xc0\xb6\x6c\xbc\x91\x7a\x71\x22\x4c\x2e\x2a\x65\xa3\x54\x17\xec\x65\x02\xdc\xf8\x8e\x17\x7b\xcc\xf3\x71\xc3\x26\xd6\xc2\xa1\x92\x20\x09\x61\x67\xf7\x0a\xd2\xa0\x46\xf8\xf1\x53\xa4\x4d\xb8\x70\xff\x33\x8b\xeb\x41\xf5\xd2\x8c\xaf\x91\x03\xa4\xce\x47\x69\xdd\xcd\x75\x90\x6d\xc0\xdb\x5e\xb9\x84\xe7\x24\xf7\xc7\x8a\x36\x42\xe8\x0e\xc6\x99\x50\x13\xef\x5a\x9e\xee\x7d\x16\xac\x8b\x6b\x88\x53\xe7\x84\xdf\x6d\x3e\x91\xc6\x44\xdd\x51\x48\x53\xff\x0b\xfa\x47\xd8\x33\xd2\x99\x28\xe5\x80\x56\x48\x14\x60\xb6\x21\x84\x2a\x28\x40\x78\x10\x78\xbd\x2b\x89\x3c\x40\xbe\x04\x47\x30\xc5\x30\x2b\xe3\x1c\xc3\xd9\x09\xc7\x3b\x83\x49\x70\x7b\x75\x6d\xc6\x05\x39\x92\x12\x0a\x8b\xe3\x1d\x63\x25\x20\x6a\x24\x0a\x01\x28\xcf\x18\x2d\x4f\x11\x90\x52\x21\x11\x6c\xff\x24\xe0\x5a\x10\x75\xca\x18\x57\xab\x57\x85\xf2\x50\x65\x92\x7c\x07\xd3\xf7\xce\x56\xdf\x13\xda\x5a\x07\xb2\xae\xd6\x92\xd7\x72\x3f\x9f\xd9\xbe\x92\xdb\x48\x56\x0b\x7c\x9d\xe3\xcc\xc2\xd7\x66\x90\x9b\x07\x2e\x96\x00\x4f\x1c\xbe\x57\x61\xa8\x86\x9a\x75\x15\x67\xa0\x96\x27\x89\xd5\x65\xb5\xb5\x54\x39\xa1\x19\xe3\x40\x83\xbe\x21\x15\xe3\x59\x21\x10\x86\x8c\x83\x20\xcc\x29\x0a\x23\xc0\xe6\xb5\x40\xcd\xfe\x53\x32\x92\x14\x14\xb9\xe3\x8e\x06\xaa\x2a\xbe\xbf\xf0\x12\x40\xa9\xb0\xb3\xd7\x25\xa9\x88\xdf\x69\x1c\x56\x1b\x51\xaf\x75\xb5\x9a\xbb\x44\x9b\x29\xcf\xa2\x42\xf6\x4c\x87\x30\xdf\x20\x44\x74\x06\x07\x24\x16\xa4\x8e\xd6\x31\xf7\x9e\xfc\xe4\xea\x1b\x9c\xe7\x32\x86\x64\x2d\xbd\x9b\xfe\x20\x5b\x27\xfc\xa2\xd2\xcb\x3e\xc6\xd6\x5b\xfd\xb8\x9d\xaa\x96\xc1\x26\xae\x85\xa1\x72\xae\x01\x19\x41\xa7\xd3\x9e\xe4\x2f\x11\xa1\x0d\x1d\xb5\xe0\x0e\xdd\x44\xc4\xf1\x7e\xa7\xc8\xd8\xf9\xda\x51\x3f\xba\x22\xd0\x70\x30\xa3\x92\x48\x05\x14\x9f\xe2\x37\xda\x91\xc9\x2d\xf1\x54\x28\xf3\x7d\x57\x5c\xd7\xd5\x42\xa1\xa2\x8b\xb7\xd1\x8d\x4e\xbc\xaf\xf6\x83\xc0\x9f\xc2\x0a\x65\x98\x71\x8f\x6a\xe2\xd9\x58\x9a\x66\xad\xab\x8b\x99\x3a\xd4\x17\x32\x9e\x98\x78\x6c\x12\x52\x4e\xdc\x91\x63\x63\xa1\x2c\x98\x9d\x5a\x77\x7d\x03\x01\xd7\x50\x50\x07\x0d\x0e\x51\xe7\x07\x94\x3d\x90\x77\x78\x48\x24\xda\x59\x63\x33\x57\xa2\x6d\x32\x83\x38\x86\xf3\xbd\x00\x25\x88\x35\x4a\x18\x8a\x26\x3d\xb7\x83\xfc\x35\x2f\xdc\x15\xa9\x80\xd5\xee\x30\xb4\xd1\x0d\xa7\x47\x4a\xb5\xe1\x6a\x40\xa9\x1a\xa4\xad\xd3\x87\x51\xa9\x43\x5f\x1e\x54\x5c\x4c\xc2\x02\x9a\xb7\xa3\x8d\xa8\xec\x26\x80\x97\x04\x23\x19\xaa\x20\xae\xb8\x05\xae\x79\x8e\x14\x64\xdd\x4b\x9c\x45\x35\xdb\x4c\xb1\xc6\x91\x40\x65\x09\x25\x91\x55\x4c\xf1\x93\xe6\x50\x22\x67\xf4\x0f\xd6\xbd\x2d\xfa\x1e\x91\xb2\x16\x90\x21\xec\x0d\xd3\x16\x46\xc5\x28\x51\xcc\x19\x4e\xe2\xb6\xac\xd0\x73\x36\x6c\xdb\x82\x84\x5a\x12\xb3\x1b\x8f\xbd\xc0\xd5\x2c\xa1\xcb\xdd\xcb\xca\xea\x19\x15\x9d\x8b\x74\x8f\xc5\x0c\x3b\x4e\x58\x17\x20\x9b\xb0\x33\xde\xaf\x07\xf1\x83\x01\xbe\xbf\x1e\xc8\x38\x2b\x49\x57\x05\xac\xc1\x21\x66\xb4\x13\x72\x8c\x41\x5c\x69\x81\x8d\x39\x34\x3d\x4c\xc5\x55\xd0\x59\x5b\x84\x27\x42\x73\xf6\xb4\x60\xc3\xf5\x4c\x89\x97\x08\x83\x15\x1c\xaf\x15\xb4\x54\x02\x11\xaa\x16\x8f\x93\xae\x65\xeb\x8a\xd4\x3f\xda\x67\x20\x45\x8c\x70\xc1\xa4\xef\x4b\x0b\x98\xd7\xc1\xa1\x4b\x05\x15\x13\xee\x02\xf8\x0a\x1e\x87\x37\x73\x01\x16\x07\xb0\x15\x52\x60\xd4\x94\xae\x87\xca\x18\x5f\xff\x9a\x20\x3c\x89\xdb\x86\x03\x12\xe1\xa8\x5a\xcb\x3b\xa2\xe7\x96\xa9\x33\x07\x27\xf3\xed\x6c\xe2\x6f\x69\x43\xa7\x0e\x9f\xbd\x87\x90\xf5\x8e\x7a\xba\xc0\x69\x33\xb0\xe6\x6d\xf6\x8a\x41\x6f\x78\x72\xe0\xd1\xea\xc3\x58\x60\xdf\x8c\xb2\xda\x46\xab\xd8\x3b\xef\x5f\xef\xfc\x6d\xad\x6f\xdf\xed\xb9\x9a\x02\xa4\x14\xc2\x87\xa8\xfe\x61\x61\xd1\x78\x45\x1c\x9a\x74\xb9\xce\x30\xd4\x43\xfd\x13\x85\xfe\x26\x36\xfb\xf3\xec\xab\x7f\x5d\x1c\x7c\xd6\xdb\x42\x5d\x9c\xc7\x23\xde\xb2\xfe\x02\x3a\x7b\x6b\x55\x98\xc3\x03\x4d\x25\xd3\xbb\x84\x39\x49\x2e\x7d\x7d\xbb\x35\x8f\x61\x83\x39\xfe\x01\xc4\x4c\xa6\x73\xa3\xc5\x01\xc4\x73\x77\x65\x6d\xda\x0b\x71\x9e\xf3\x15\x83\xcd\xdd\x87\x99\x92\x61\xee\x25\xd2\x2b\xe5\xda\x15\xc6\xb6\x6e\x9d\x5a\x7d\xc6\x20\xdd\xe9\xa3\x7e\x8f\xff\x6b\xf8\x93\x27\xfe\x0d\x9f\xf4\x34\xb9\xeb\xfa\x61\x5e\xd4\x77\xcf\xf3\xb7\x86\x7c\x2c\x90\xee\x5d\x9f\x16\xdd\xb7\x7a\xeb\xe5\x53\xa3\xf3\xe1\xbf\x3d\x26\x18\x1e\xe0\x7b\x26\x97\x1b\xfd\x67\xfb\xcf\x12\x9b\x97\xcd\x9f\x01\x00\x00\xff\xff\x0c\x18\x50\x44\xa5\x35\x00\x00") +var _dataConfig_schema_v32Json = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xec\x1b\x4d\x73\xdc\xa8\xf2\x3e\xbf\x42\xa5\xe4\x16\x7f\xa4\xde\x4b\xbd\xaa\x97\xdb\x3b\xbe\xd3\xee\x79\x5d\x13\x15\x83\x7a\x34\xc4\x12\x10\x40\x63\x4f\x52\xfe\xef\x5b\xfa\x1c\x40\x20\xd0\x8c\x1c\x67\xb7\xf6\x64\x5b\x74\x37\xf4\x77\x37\x8d\x7f\x6c\x92\x24\x7d\x2f\xf1\x01\x2a\x94\x7e\x4e\xd2\x83\x52\xfc\xf3\xfd\xfd\x57\xc9\xe8\x6d\xf7\xf5\x8e\x89\xe2\x3e\x17\x68\xaf\x6e\x3f\x7e\xba\xef\xbe\xbd\x4b\x6f\x1a\x3c\x92\x37\x28\x98\xd1\x3d\x29\xb2\x6e\x25\x3b\xfe\xfb\xee\x5f\x77\x0d\x7a\x07\xa2\x4e\x1c\x1a\x20\xb6\xfb\x0a\x58\x75\xdf\x04\x7c\xab\x89\x80\x06\xf9\x21\x3d\x82\x90\x84\xd1\x74\x7b\xb3\x69\xd6\xb8\x60\x1c\x84\x22\x20\xd3\xcf\x49\x73\xb8\x24\x19\x41\x86\x0f\x1a\x59\xa9\x04\xa1\x45\xda\x7e\x7e\x69\x29\x24\x49\x2a\x41\x1c\x09\xd6\x28\x8c\x47\x7d\x77\x7f\xa6\x7f\x3f\x82\xdd\xd8\x54\xb5\xc3\xb6\xdf\x39\x52\x0a\x04\xfd\x7d\x7a\xb6\x76\xf9\xcb\x03\xba\xfd\xfe\xbf\xdb\x3f\x3e\xde\xfe\xf7\x2e\xbb\xdd\x7e\x78\x6f\x2c\x37\xf2\x15\xb0\xef\xb6\xcf\x61\x4f\x28\x51\x84\xd1\x71\xff\x74\x84\x7c\xe9\x7f\x7b\x19\x37\x46\x79\xde\x02\xa3\xd2\xd8\x7b\x8f\x4a\x09\x26\xcf\x14\xd4\x13\x13\x8f\x21\x9e\x47\xb0\x37\xe2\xb9\xdf\xdf\xc1\xb3\xc9\xce\x91\x95\x75\x15\xd4\xe0\x00\xf5\x46\xcc\x74\xdb\xaf\xa3\x3f\x09\x58\x80\x0a\x9b\x6c\x07\xf5\x66\x16\xdb\x6c\x7f\x1d\xc3\x9b\x81\xe9\x59\xd8\x0e\x42\xdb\xbb\x3d\xa0\xe1\xde\x2e\x51\xb9\xdc\xcb\x2f\xab\x51\x58\x1e\x29\xe5\xc0\x4b\x76\x6a\xbe\x79\xe4\xd1\x01\x54\x40\x55\x3a\x8a\x20\x49\xd2\x5d\x4d\xca\xdc\x96\x28\xa3\xf0\x5b\x43\xe2\x41\xfb\x98\x24\x3f\xec\x48\xa6\xd1\x69\xd7\x8d\xbf\xfc\x0a\x1f\xd7\x3d\xbc\x8c\xeb\x98\x51\x05\xcf\xaa\x65\x6a\x7e\xeb\x4e\x04\x0c\x3f\x82\xd8\x93\x12\x62\x31\x90\x28\xe4\x8c\xc8\x4a\x22\x55\xc6\x44\x96\x13\xac\x9c\xf8\x25\xda\x41\x79\x15\x05\x8c\xf0\x01\xb2\xbd\x60\x55\x90\xca\x3e\xeb\x38\x91\xe9\x8b\x45\x67\x42\x38\x6c\xda\x23\xaa\xf6\xd7\x76\xe3\x20\x98\x62\xc4\x33\x94\xe7\x86\x48\x91\x10\xe8\x94\xde\x24\x29\x51\x50\x49\xb7\xb4\x93\xb4\xa6\xe4\x5b\x0d\xff\xef\x41\x94\xa8\xc1\xa6\x9b\x0b\xc6\xd7\x27\x5c\x08\x56\xf3\x8c\x23\xd1\xd8\xfa\xbc\x25\xa4\x98\x55\x15\xa2\x6b\x39\xc0\x12\x3e\x22\x24\xcf\xa8\x42\x84\x82\xc8\x28\xaa\x42\x36\xdd\x04\x00\xa0\xb9\xcc\xba\xda\x23\xd6\x92\x0c\x02\x63\x21\xb2\xaa\x3e\x72\x3a\xe7\x21\x1d\x99\xc6\x47\x9a\xb3\xa5\x16\x62\x26\x01\x09\x7c\xb8\x10\x9f\x55\x88\xd0\x18\xd9\x01\x55\xe2\xc4\x19\xe9\xec\xe5\x97\x33\x04\xa0\xc7\x6c\x0c\x6b\x8b\xc5\x00\xf4\x48\x04\xa3\xd5\xe0\x0d\x71\x91\x4a\xc3\x7f\xe6\x4c\x82\x2d\x18\x8b\x41\x7d\x69\x64\xd5\x90\xc9\x80\xf1\x30\x30\x7e\x93\xa4\xb4\xae\x76\x20\x9a\x72\xda\x80\xdc\x33\x51\xa1\xe6\xb0\xc3\xde\xda\xb2\x21\x69\x87\xe5\xe9\x02\xd4\x79\x68\x2a\x0c\x54\x66\x25\xa1\x8f\xeb\x9b\x38\x3c\x2b\x81\xb2\x03\x93\xea\x92\x64\x90\x1e\x00\x95\xea\x80\x0f\x80\x1f\x67\xd0\x75\x28\x03\x9b\x49\x15\x63\xe4\xa4\x42\x45\x18\x88\xe3\x10\xc8\xc5\x49\x2f\x5d\x55\xf8\x1a\x59\x56\x14\x0d\xa8\xcf\xe2\x26\x45\x54\xbf\x1c\x2a\x3f\x72\x41\x8e\x20\x62\x6b\x09\xc6\xcf\xb5\x9f\xbd\x18\xae\x85\x92\x70\x21\x6c\x80\x7e\xb9\xeb\xea\xe0\x19\xaf\x6a\x7f\x2b\xcb\x74\x6b\x97\x0b\x89\x95\xf7\x5d\x5f\x2c\x0e\xe3\x0a\x0a\x43\x2b\x15\xc2\x4d\xdd\x20\x40\x7a\xf4\x7a\x06\xed\x1b\xad\xac\x62\xb9\xcf\x40\x27\xc0\xb6\x6c\xbc\x91\x7a\x71\x22\x4c\x2e\x2a\x65\xa3\x54\x17\xec\x65\x02\xdc\xf8\x8e\x17\x7b\xcc\xf3\x71\xc3\x26\xd6\xc2\xa1\x92\x20\x09\x61\x67\xf7\x0a\xd2\xa0\x46\xf8\xf1\x53\xa4\x4d\xb8\x70\xff\x33\x8b\xeb\x41\xf5\xd2\x8c\xaf\x91\x03\xa4\xce\x47\x69\xdd\xcd\x75\x90\x6d\xc0\xdb\x5e\xb9\x84\xe7\x24\xf7\xc7\x8a\x36\x42\xe8\x0e\xc6\x99\x50\x13\xef\x5a\x9e\xee\x7d\x16\xac\x8b\x6b\x88\x53\xe7\x84\xdf\x6d\x3e\x91\xc6\x44\xdd\x51\x48\x53\xff\x0b\xfa\x47\xd8\x33\xd2\x99\x28\xe5\x80\x56\x48\x14\x60\xb6\x21\x84\x2a\x28\x40\x78\x10\x78\xbd\x2b\x89\x3c\x40\xbe\x04\x47\x30\xc5\x30\x2b\xe3\x1c\xc3\xd9\x09\xc7\x3b\x83\x49\x70\x7b\x75\x6d\xc6\x05\x39\x92\x12\x0a\x8b\xe3\x1d\x63\x25\x20\x6a\x24\x0a\x01\x28\xcf\x18\x2d\x4f\x11\x90\x52\x21\x11\x6c\xff\x24\xe0\x5a\x10\x75\xca\x18\x57\xab\x57\x85\xf2\x50\x65\x92\x7c\x07\xd3\xf7\xce\x56\xdf\x13\xda\x5a\x07\xb2\xae\xd6\x92\xd7\x72\x3f\x9f\xd9\xbe\x92\xdb\x48\x56\x0b\x7c\x9d\xe3\xcc\xc2\xd7\x66\x90\x9b\x07\x2e\x96\x00\x4f\x1c\xbe\x57\x61\xa8\x86\x9a\x75\x15\x67\xa0\x96\x27\x89\xd5\x65\xb5\xb5\x54\x39\xa1\x19\xe3\x40\x83\xbe\x21\x15\xe3\x59\x21\x10\x86\x8c\x83\x20\xcc\x29\x0a\x23\xc0\xe6\xb5\x40\xcd\xfe\x53\x32\x92\x14\x14\xb9\xe3\x8e\x06\xaa\x2a\xbe\xbf\xf0\x12\x40\xa9\xb0\xb3\xd7\x25\xa9\x88\xdf\x69\x1c\x56\x1b\x51\xaf\x75\xb5\x9a\xbb\x44\x9b\x29\xcf\xa2\x42\xf6\x4c\x87\x30\xdf\x20\x44\x74\x06\x07\x24\x16\xa4\x8e\xd6\x31\xf7\x9e\xfc\xe4\xea\x1b\x9c\xe7\x32\x86\x64\x2d\xbd\x9b\xfe\x20\x5b\x27\xfc\xa2\xd2\xcb\x3e\xc6\xd6\x5b\xfd\xb8\x9d\xaa\x96\xc1\x26\xae\x85\xa1\x72\xae\x01\x19\x41\xa7\xd3\x9e\xe4\x2f\x11\xa1\x0d\x1d\xb5\xe0\x0e\xdd\x44\xc4\xf1\x7e\xa7\xc8\xd8\xf9\xda\x51\x3f\xba\x22\xd0\x70\x30\xa3\x92\x48\x05\x14\x9f\xe2\x37\xda\x91\xc9\x2d\xf1\x54\x28\xf3\x7d\x57\x5c\xd7\xd5\x42\xa1\xa2\x8b\xb7\xd1\x8d\x4e\xbc\xaf\xf6\x83\xc0\x9f\xc2\x0a\x65\x98\x71\x8f\x6a\xe2\xd9\x58\x9a\x66\xad\xab\x8b\x99\x3a\xd4\x17\x32\x9e\x98\x78\x6c\x12\x52\x4e\xdc\x91\x63\x63\xa1\x2c\x98\x9d\x5a\x77\x7d\x03\x01\xd7\x50\x50\x07\x0d\x0e\x51\xe7\x07\x94\x3d\x90\x77\x78\x48\x24\xda\x59\x63\x33\x57\xa2\x6d\x32\x83\x38\x86\xf3\xbd\x00\x25\x88\x35\x4a\x18\x8a\x26\x3d\xb7\x83\xfc\x35\x2f\xdc\x15\xa9\x80\xd5\xee\x30\xb4\xd1\x0d\xa7\x47\x4a\xb5\xe1\x6a\x40\xa9\x1a\xa4\xad\xd3\x87\x51\xa9\x43\x5f\x1e\x54\x5c\x4c\xc2\x02\x9a\xb7\xa3\x8d\xa8\xec\x26\x80\x97\x04\x23\x19\xaa\x20\xae\xb8\x05\xae\x79\x8e\x14\x64\xdd\x4b\x9c\x45\x35\xdb\x4c\xb1\xc6\x91\x40\x65\x09\x25\x91\x55\x4c\xf1\x93\xe6\x50\x22\x67\xf4\x0f\xd6\xbd\x2d\xfa\x1e\x91\xb2\x16\x90\x21\xec\x0d\xd3\x16\x46\xc5\x28\x51\xcc\x19\x4e\xe2\xb6\xac\xd0\x73\x36\x6c\xdb\x82\x84\x5a\x12\xb3\x1b\x8f\xbd\xc0\xd5\x2c\xa1\xcb\xdd\xcb\xca\xea\x19\x15\x9d\x8b\x74\x8f\xc5\x0c\x3b\x4e\x58\x17\x20\x9b\xb0\x33\xde\xaf\x07\xf1\x83\x01\xbe\xbf\x1e\xc8\x38\x2b\x49\x57\x05\xac\xc1\x21\x66\xb4\x13\x72\x8c\x41\x5c\x69\x81\x8d\x39\x34\x3d\x4c\xc5\x55\xd0\x59\x5b\x84\x27\x42\x73\xf6\xb4\x60\xc3\xf5\x4c\x89\x97\x08\x83\x15\x1c\xaf\x15\xb4\x54\x02\x11\xaa\x16\x8f\x93\xae\x65\xeb\x8a\xd4\x3f\xda\x67\x20\x45\x8c\x70\xc1\xa4\xef\x4b\x0b\x98\xd7\xc1\xa1\x4b\x05\x15\x13\xee\x02\xf8\x0a\x1e\x87\x37\x73\x01\x16\x07\xb0\x15\x52\x60\xd4\x94\xae\x87\xca\x18\x5f\xff\x9a\x20\x3c\x89\xdb\x86\x03\x12\xe1\xa8\x5a\xcb\x3b\xa2\xe7\x96\xa9\x33\x07\x27\xf3\xed\x6c\xe2\x6f\x69\x43\xa7\x0e\x9f\xbd\x87\x90\xf5\x8e\x7a\xba\xc0\x69\x33\xb0\xe6\x6d\xf6\x8a\x41\x6f\x78\x72\xe0\xd1\xea\xc3\x58\x60\xdf\x8c\xb2\xda\x46\xab\xd8\x3b\xef\x5f\xef\xfc\x6d\xad\x6f\xdf\xed\xb9\x9a\x02\xa4\x14\xc2\x87\xa8\xfe\x61\x61\xd1\x78\x45\x1c\x9a\x74\xb9\xce\x30\xd4\x43\xfd\x13\x85\xfe\x26\x36\xfb\xf3\xec\xab\x7f\x5d\x1c\x7c\xd6\xdb\x42\x5d\x9c\xc7\x23\xde\xb2\xfe\x02\x3a\x7b\x6b\x55\x98\xc3\x03\x4d\x25\xd3\xbb\x84\x39\x49\x2e\x7d\x7d\xbb\x35\x8f\x61\x83\x39\xfe\x01\xc4\x4c\xa6\x73\xa3\xc5\x01\xc4\x73\x77\x65\x6d\xda\x0b\x71\x9e\xf3\x15\x83\xcd\xdd\x87\x99\x92\x61\xee\x25\xd2\x2b\xe5\xda\x15\xc6\xb6\x6e\x9d\x5a\x7d\xc6\x20\xdd\xe9\xa3\x7e\x8f\xff\x6b\xf8\x93\x27\xfe\x0d\x9f\xf4\x34\xb9\xeb\xfa\x61\x5e\xd4\x77\xcf\xf3\xb7\x86\x7c\x2c\x90\xee\x5d\x9f\x16\xdd\xb7\x7a\xeb\xe5\x53\xa3\xf3\xe1\xbf\x3d\x26\x18\x1e\xe0\x7b\x26\x97\x1b\xfd\x67\xfb\xcf\x12\x9b\x97\xcd\x9f\x01\x00\x00\xff\xff\x0c\x18\x50\x44\xa5\x35\x00\x00") func dataConfig_schema_v32JsonBytes() ([]byte, error) { return bindataRead( From a1b7969bce18b56f0299a286d872f14e9346272d Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Wed, 12 Apr 2017 12:42:35 -0400 Subject: [PATCH 07/18] Set Composefile WorkingDir to dirname of the composefile. Signed-off-by: Daniel Nephin --- cli/command/stack/deploy_composefile.go | 11 ++++---- cli/command/stack/deploy_composefile_test.go | 28 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 cli/command/stack/deploy_composefile_test.go diff --git a/cli/command/stack/deploy_composefile.go b/cli/command/stack/deploy_composefile.go index f241310bf2..e3f59de14c 100644 --- a/cli/command/stack/deploy_composefile.go +++ b/cli/command/stack/deploy_composefile.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "os" + "path/filepath" "sort" "strings" @@ -20,7 +21,7 @@ import ( ) func deployCompose(ctx context.Context, dockerCli *command.DockerCli, opts deployOptions) error { - configDetails, err := getConfigDetails(opts) + configDetails, err := getConfigDetails(opts.composefile) if err != nil { return err } @@ -108,16 +109,16 @@ func propertyWarnings(properties map[string]string) string { return strings.Join(msgs, "\n\n") } -func getConfigDetails(opts deployOptions) (composetypes.ConfigDetails, error) { +func getConfigDetails(composefile string) (composetypes.ConfigDetails, error) { var details composetypes.ConfigDetails - var err error - details.WorkingDir, err = os.Getwd() + absPath, err := filepath.Abs(composefile) if err != nil { return details, err } + details.WorkingDir = filepath.Dir(absPath) - configFile, err := getConfigFile(opts.composefile) + configFile, err := getConfigFile(composefile) if err != nil { return details, err } diff --git a/cli/command/stack/deploy_composefile_test.go b/cli/command/stack/deploy_composefile_test.go new file mode 100644 index 0000000000..d5ef5463ff --- /dev/null +++ b/cli/command/stack/deploy_composefile_test.go @@ -0,0 +1,28 @@ +package stack + +import ( + "os" + "path/filepath" + "testing" + + "github.com/docker/docker/pkg/testutil/tempfile" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetConfigDetails(t *testing.T) { + content := ` +version: "3.0" +services: + foo: + image: alpine:3.5 +` + file := tempfile.NewTempFile(t, "test-get-config-details", content) + defer file.Remove() + + details, err := getConfigDetails(file.Name()) + require.NoError(t, err) + assert.Equal(t, filepath.Dir(file.Name()), details.WorkingDir) + assert.Len(t, details.ConfigFiles, 1) + assert.Len(t, details.Environment, len(os.Environ())) +} From 2b31a4bf8d6b80ca2ca51c96e822dcd5832592f9 Mon Sep 17 00:00:00 2001 From: Dave Henderson Date: Wed, 19 Apr 2017 21:35:00 -0500 Subject: [PATCH 08/18] Output `docker swarm join` on a single line This avoids issues when copy/pasting between different shells on different OSes, which may not all support `\` as a continuation character. Fixes #32725 Signed-off-by: Dave Henderson --- cli/command/swarm/join_token.go | 4 ++-- cli/command/swarm/testdata/jointoken-manager-rotate.golden | 5 +---- cli/command/swarm/testdata/jointoken-manager.golden | 5 +---- cli/command/swarm/testdata/jointoken-worker.golden | 5 +---- 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/cli/command/swarm/join_token.go b/cli/command/swarm/join_token.go index 0a905dfc26..b35efad8a9 100644 --- a/cli/command/swarm/join_token.go +++ b/cli/command/swarm/join_token.go @@ -108,10 +108,10 @@ func printJoinCommand(ctx context.Context, dockerCli command.Cli, nodeID string, if node.ManagerStatus != nil { if worker { - fmt.Fprintf(dockerCli.Out(), "To add a worker to this swarm, run the following command:\n\n docker swarm join \\\n --token %s \\\n %s\n\n", sw.JoinTokens.Worker, node.ManagerStatus.Addr) + fmt.Fprintf(dockerCli.Out(), "To add a worker to this swarm, run the following command:\n\n docker swarm join --token %s %s\n\n", sw.JoinTokens.Worker, node.ManagerStatus.Addr) } if manager { - fmt.Fprintf(dockerCli.Out(), "To add a manager to this swarm, run the following command:\n\n docker swarm join \\\n --token %s \\\n %s\n\n", sw.JoinTokens.Manager, node.ManagerStatus.Addr) + fmt.Fprintf(dockerCli.Out(), "To add a manager to this swarm, run the following command:\n\n docker swarm join --token %s %s\n\n", sw.JoinTokens.Manager, node.ManagerStatus.Addr) } } diff --git a/cli/command/swarm/testdata/jointoken-manager-rotate.golden b/cli/command/swarm/testdata/jointoken-manager-rotate.golden index 7ee455bec8..b4d0a48f66 100644 --- a/cli/command/swarm/testdata/jointoken-manager-rotate.golden +++ b/cli/command/swarm/testdata/jointoken-manager-rotate.golden @@ -2,7 +2,4 @@ Successfully rotated manager join token. To add a manager to this swarm, run the following command: - docker swarm join \ - --token manager-join-token \ - 127.0.0.1 - + docker swarm join --token manager-join-token 127.0.0.1 diff --git a/cli/command/swarm/testdata/jointoken-manager.golden b/cli/command/swarm/testdata/jointoken-manager.golden index d56527aa55..522b2968fe 100644 --- a/cli/command/swarm/testdata/jointoken-manager.golden +++ b/cli/command/swarm/testdata/jointoken-manager.golden @@ -1,6 +1,3 @@ To add a manager to this swarm, run the following command: - docker swarm join \ - --token manager-join-token \ - 127.0.0.1 - + docker swarm join --token manager-join-token 127.0.0.1 diff --git a/cli/command/swarm/testdata/jointoken-worker.golden b/cli/command/swarm/testdata/jointoken-worker.golden index 5d44f3daee..899df703fd 100644 --- a/cli/command/swarm/testdata/jointoken-worker.golden +++ b/cli/command/swarm/testdata/jointoken-worker.golden @@ -1,6 +1,3 @@ To add a worker to this swarm, run the following command: - docker swarm join \ - --token worker-join-token \ - 127.0.0.1 - + docker swarm join --token worker-join-token 127.0.0.1 From e7793092a24a38df7a6f76381eab6ac1c2f5c0b0 Mon Sep 17 00:00:00 2001 From: Ignacio Capurro Date: Thu, 30 Mar 2017 21:21:14 -0300 Subject: [PATCH 09/18] Unit tests for cli/commands/image (except build and tag) Signed-off-by: Ignacio Capurro --- cli/command/cli.go | 7 ++ cli/command/image/client_test.go | 116 ++++++++++++++++++ cli/command/image/history.go | 4 +- cli/command/image/history_test.go | 108 ++++++++++++++++ cli/command/image/import.go | 4 +- cli/command/image/import_test.go | 100 +++++++++++++++ cli/command/image/inspect.go | 4 +- cli/command/image/inspect_test.go | 92 ++++++++++++++ cli/command/image/list.go | 6 +- cli/command/image/list_test.go | 102 +++++++++++++++ cli/command/image/load.go | 4 +- cli/command/image/load_test.go | 106 ++++++++++++++++ cli/command/image/prune.go | 6 +- cli/command/image/prune_test.go | 100 +++++++++++++++ cli/command/image/pull.go | 4 +- cli/command/image/pull_test.go | 85 +++++++++++++ cli/command/image/push.go | 4 +- cli/command/image/push_test.go | 84 +++++++++++++ cli/command/image/remove.go | 6 +- cli/command/image/remove_test.go | 103 ++++++++++++++++ cli/command/image/save.go | 4 +- cli/command/image/save_test.go | 98 +++++++++++++++ cli/command/image/tag.go | 4 +- cli/command/image/tag_test.go | 43 +++++++ ...tory-command-success.quiet-no-trunc.golden | 1 + .../history-command-success.quiet.golden | 1 + .../history-command-success.simple.golden | 2 + .../testdata/import-command-success.input.txt | 1 + .../inspect-command-success.format.golden | 1 + ...inspect-command-success.simple-many.golden | 50 ++++++++ .../inspect-command-success.simple.golden | 26 ++++ .../list-command-success.filters.golden | 1 + .../list-command-success.format.golden | 0 .../list-command-success.match-name.golden | 1 + .../list-command-success.quiet-format.golden | 0 .../list-command-success.simple.golden | 1 + .../load-command-success.input-file.golden | 1 + .../testdata/load-command-success.input.txt | 1 + .../testdata/load-command-success.json.golden | 1 + .../load-command-success.simple.golden | 1 + .../testdata/prune-command-success.all.golden | 2 + ...prune-command-success.force-deleted.golden | 4 + ...rune-command-success.force-untagged.golden | 4 + .../pull-command-success.simple-no-tag.golden | 1 + .../pull-command-success.simple.golden | 0 ...-success.Image Deleted and Untagged.golden | 4 + ...emove-command-success.Image Deleted.golden | 2 + ...move-command-success.Image Untagged.golden | 2 + cli/command/image/trust.go | 14 +-- cli/command/in.go | 42 +------ cli/command/out.go | 38 +----- cli/command/registry.go | 14 +-- cli/command/stream.go | 44 +++++++ cli/command/swarm/unlock_test.go | 3 +- cli/command/volume/prune_test.go | 5 +- cli/internal/test/cli.go | 24 ++-- cli/internal/test/store.go | 74 +++++++++++ 57 files changed, 1441 insertions(+), 119 deletions(-) create mode 100644 cli/command/image/client_test.go create mode 100644 cli/command/image/history_test.go create mode 100644 cli/command/image/import_test.go create mode 100644 cli/command/image/inspect_test.go create mode 100644 cli/command/image/list_test.go create mode 100644 cli/command/image/load_test.go create mode 100644 cli/command/image/prune_test.go create mode 100644 cli/command/image/pull_test.go create mode 100644 cli/command/image/push_test.go create mode 100644 cli/command/image/remove_test.go create mode 100644 cli/command/image/save_test.go create mode 100644 cli/command/image/tag_test.go create mode 100644 cli/command/image/testdata/history-command-success.quiet-no-trunc.golden create mode 100644 cli/command/image/testdata/history-command-success.quiet.golden create mode 100644 cli/command/image/testdata/history-command-success.simple.golden create mode 100644 cli/command/image/testdata/import-command-success.input.txt create mode 100644 cli/command/image/testdata/inspect-command-success.format.golden create mode 100644 cli/command/image/testdata/inspect-command-success.simple-many.golden create mode 100644 cli/command/image/testdata/inspect-command-success.simple.golden create mode 100644 cli/command/image/testdata/list-command-success.filters.golden create mode 100644 cli/command/image/testdata/list-command-success.format.golden create mode 100644 cli/command/image/testdata/list-command-success.match-name.golden create mode 100644 cli/command/image/testdata/list-command-success.quiet-format.golden create mode 100644 cli/command/image/testdata/list-command-success.simple.golden create mode 100644 cli/command/image/testdata/load-command-success.input-file.golden create mode 100644 cli/command/image/testdata/load-command-success.input.txt create mode 100644 cli/command/image/testdata/load-command-success.json.golden create mode 100644 cli/command/image/testdata/load-command-success.simple.golden create mode 100644 cli/command/image/testdata/prune-command-success.all.golden create mode 100644 cli/command/image/testdata/prune-command-success.force-deleted.golden create mode 100644 cli/command/image/testdata/prune-command-success.force-untagged.golden create mode 100644 cli/command/image/testdata/pull-command-success.simple-no-tag.golden create mode 100644 cli/command/image/testdata/pull-command-success.simple.golden create mode 100644 cli/command/image/testdata/remove-command-success.Image Deleted and Untagged.golden create mode 100644 cli/command/image/testdata/remove-command-success.Image Deleted.golden create mode 100644 cli/command/image/testdata/remove-command-success.Image Untagged.golden create mode 100644 cli/command/stream.go create mode 100644 cli/internal/test/store.go diff --git a/cli/command/cli.go b/cli/command/cli.go index c5541978c2..6cad5a10c7 100644 --- a/cli/command/cli.go +++ b/cli/command/cli.go @@ -39,7 +39,9 @@ type Cli interface { Out() *OutStream Err() io.Writer In() *InStream + SetIn(in *InStream) ConfigFile() *configfile.ConfigFile + CredentialsStore(serverAddress string) credentials.Store } // DockerCli is an instance the docker command line client. @@ -75,6 +77,11 @@ func (cli *DockerCli) Err() io.Writer { return cli.err } +// SetIn sets the reader used for stdin +func (cli *DockerCli) SetIn(in *InStream) { + cli.in = in +} + // In returns the reader used for stdin func (cli *DockerCli) In() *InStream { return cli.in diff --git a/cli/command/image/client_test.go b/cli/command/image/client_test.go new file mode 100644 index 0000000000..949b09388b --- /dev/null +++ b/cli/command/image/client_test.go @@ -0,0 +1,116 @@ +package image + +import ( + "io" + "io/ioutil" + "strings" + "time" + + "github.com/docker/cli/client" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/image" + "golang.org/x/net/context" +) + +type fakeClient struct { + client.Client + imageTagFunc func(string, string) error + imageSaveFunc func(images []string) (io.ReadCloser, error) + imageRemoveFunc func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) + imagePushFunc func(ref string, options types.ImagePushOptions) (io.ReadCloser, error) + infoFunc func() (types.Info, error) + imagePullFunc func(ref string, options types.ImagePullOptions) (io.ReadCloser, error) + imagesPruneFunc func(pruneFilter filters.Args) (types.ImagesPruneReport, error) + imageLoadFunc func(input io.Reader, quiet bool) (types.ImageLoadResponse, error) + imageListFunc func(options types.ImageListOptions) ([]types.ImageSummary, error) + imageInspectFunc func(image string) (types.ImageInspect, []byte, error) + imageImportFunc func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) + imageHistoryFunc func(image string) ([]image.HistoryResponseItem, error) +} + +func (cli *fakeClient) ImageTag(_ context.Context, image, ref string) error { + if cli.imageTagFunc != nil { + return cli.imageTagFunc(image, ref) + } + return nil +} + +func (cli *fakeClient) ImageSave(_ context.Context, images []string) (io.ReadCloser, error) { + if cli.imageSaveFunc != nil { + return cli.imageSaveFunc(images) + } + return ioutil.NopCloser(strings.NewReader("")), nil +} + +func (cli *fakeClient) ImageRemove(_ context.Context, image string, + options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { + if cli.imageRemoveFunc != nil { + return cli.imageRemoveFunc(image, options) + } + return []types.ImageDeleteResponseItem{}, nil +} + +func (cli *fakeClient) ImagePush(_ context.Context, ref string, options types.ImagePushOptions) (io.ReadCloser, error) { + if cli.imagePushFunc != nil { + return cli.imagePushFunc(ref, options) + } + return ioutil.NopCloser(strings.NewReader("")), nil +} + +func (cli *fakeClient) Info(_ context.Context) (types.Info, error) { + if cli.infoFunc != nil { + return cli.infoFunc() + } + return types.Info{}, nil +} + +func (cli *fakeClient) ImagePull(_ context.Context, ref string, options types.ImagePullOptions) (io.ReadCloser, error) { + if cli.imagePullFunc != nil { + cli.imagePullFunc(ref, options) + } + return ioutil.NopCloser(strings.NewReader("")), nil +} + +func (cli *fakeClient) ImagesPrune(_ context.Context, pruneFilter filters.Args) (types.ImagesPruneReport, error) { + if cli.imagesPruneFunc != nil { + return cli.imagesPruneFunc(pruneFilter) + } + return types.ImagesPruneReport{}, nil +} + +func (cli *fakeClient) ImageLoad(_ context.Context, input io.Reader, quiet bool) (types.ImageLoadResponse, error) { + if cli.imageLoadFunc != nil { + return cli.imageLoadFunc(input, quiet) + } + return types.ImageLoadResponse{}, nil +} + +func (cli *fakeClient) ImageList(ctx context.Context, options types.ImageListOptions) ([]types.ImageSummary, error) { + if cli.imageListFunc != nil { + return cli.imageListFunc(options) + } + return []types.ImageSummary{{}}, nil +} + +func (cli *fakeClient) ImageInspectWithRaw(_ context.Context, image string) (types.ImageInspect, []byte, error) { + if cli.imageInspectFunc != nil { + return cli.imageInspectFunc(image) + } + return types.ImageInspect{}, nil, nil +} + +func (cli *fakeClient) ImageImport(_ context.Context, source types.ImageImportSource, ref string, + options types.ImageImportOptions) (io.ReadCloser, error) { + if cli.imageImportFunc != nil { + return cli.imageImportFunc(source, ref, options) + } + return ioutil.NopCloser(strings.NewReader("")), nil +} + +func (cli *fakeClient) ImageHistory(_ context.Context, img string) ([]image.HistoryResponseItem, error) { + if cli.imageHistoryFunc != nil { + return cli.imageHistoryFunc(img) + } + return []image.HistoryResponseItem{{ID: img, Created: time.Now().Unix()}}, nil +} diff --git a/cli/command/image/history.go b/cli/command/image/history.go index f4a7009f7c..27782d107a 100644 --- a/cli/command/image/history.go +++ b/cli/command/image/history.go @@ -19,7 +19,7 @@ type historyOptions struct { } // NewHistoryCommand creates a new `docker history` command -func NewHistoryCommand(dockerCli *command.DockerCli) *cobra.Command { +func NewHistoryCommand(dockerCli command.Cli) *cobra.Command { var opts historyOptions cmd := &cobra.Command{ @@ -42,7 +42,7 @@ func NewHistoryCommand(dockerCli *command.DockerCli) *cobra.Command { return cmd } -func runHistory(dockerCli *command.DockerCli, opts historyOptions) error { +func runHistory(dockerCli command.Cli, opts historyOptions) error { ctx := context.Background() history, err := dockerCli.Client().ImageHistory(ctx, opts.image) diff --git a/cli/command/image/history_test.go b/cli/command/image/history_test.go new file mode 100644 index 0000000000..71605e4fc8 --- /dev/null +++ b/cli/command/image/history_test.go @@ -0,0 +1,108 @@ +package image + +import ( + "bytes" + "fmt" + "io/ioutil" + "regexp" + "testing" + "time" + + "github.com/docker/cli/cli/internal/test" + "github.com/docker/docker/api/types/image" + "github.com/docker/docker/pkg/testutil" + "github.com/docker/docker/pkg/testutil/golden" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" +) + +func TestNewHistoryCommandErrors(t *testing.T) { + testCases := []struct { + name string + args []string + expectedError string + imageHistoryFunc func(img string) ([]image.HistoryResponseItem, error) + }{ + { + name: "wrong-args", + args: []string{}, + expectedError: "requires exactly 1 argument(s).", + }, + { + name: "client-error", + args: []string{"image:tag"}, + expectedError: "something went wrong", + imageHistoryFunc: func(img string) ([]image.HistoryResponseItem, error) { + return []image.HistoryResponseItem{{}}, errors.Errorf("something went wrong") + }, + }, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + cmd := NewHistoryCommand(test.NewFakeCli(&fakeClient{imageHistoryFunc: tc.imageHistoryFunc}, buf)) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + } +} + +func TestNewHistoryCommandSuccess(t *testing.T) { + testCases := []struct { + name string + args []string + outputRegex string + imageHistoryFunc func(img string) ([]image.HistoryResponseItem, error) + }{ + { + name: "simple", + args: []string{"image:tag"}, + imageHistoryFunc: func(img string) ([]image.HistoryResponseItem, error) { + return []image.HistoryResponseItem{{ + ID: "1234567890123456789", + Created: time.Now().Unix(), + }}, nil + }, + }, + { + name: "quiet", + args: []string{"--quiet", "image:tag"}, + }, + // TODO: This test is failing since the output does not contain an RFC3339 date + //{ + // name: "non-human", + // args: []string{"--human=false", "image:tag"}, + // outputRegex: "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}", // RFC3339 date format match + //}, + { + name: "non-human-header", + args: []string{"--human=false", "image:tag"}, + outputRegex: "CREATED\\sAT", + }, + { + name: "quiet-no-trunc", + args: []string{"--quiet", "--no-trunc", "image:tag"}, + imageHistoryFunc: func(img string) ([]image.HistoryResponseItem, error) { + return []image.HistoryResponseItem{{ + ID: "1234567890123456789", + Created: time.Now().Unix(), + }}, nil + }, + }, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + cmd := NewHistoryCommand(test.NewFakeCli(&fakeClient{imageHistoryFunc: tc.imageHistoryFunc}, buf)) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + err := cmd.Execute() + assert.NoError(t, err) + actual := buf.String() + if tc.outputRegex == "" { + expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("history-command-success.%s.golden", tc.name))[:]) + testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected) + } else { + match, _ := regexp.MatchString(tc.outputRegex, actual) + assert.Equal(t, match, true) + } + } +} diff --git a/cli/command/image/import.go b/cli/command/image/import.go index 5284b66e26..4748122273 100644 --- a/cli/command/image/import.go +++ b/cli/command/image/import.go @@ -23,7 +23,7 @@ type importOptions struct { } // NewImportCommand creates a new `docker import` command -func NewImportCommand(dockerCli *command.DockerCli) *cobra.Command { +func NewImportCommand(dockerCli command.Cli) *cobra.Command { var opts importOptions cmd := &cobra.Command{ @@ -48,7 +48,7 @@ func NewImportCommand(dockerCli *command.DockerCli) *cobra.Command { return cmd } -func runImport(dockerCli *command.DockerCli, opts importOptions) error { +func runImport(dockerCli command.Cli, opts importOptions) error { var ( in io.Reader srcName = opts.source diff --git a/cli/command/image/import_test.go b/cli/command/image/import_test.go new file mode 100644 index 0000000000..8e0facf168 --- /dev/null +++ b/cli/command/image/import_test.go @@ -0,0 +1,100 @@ +package image + +import ( + "bytes" + "io" + "io/ioutil" + "strings" + "testing" + + "github.com/docker/cli/cli/internal/test" + "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/testutil" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" +) + +func TestNewImportCommandErrors(t *testing.T) { + testCases := []struct { + name string + args []string + expectedError string + imageImportFunc func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) + }{ + { + name: "wrong-args", + args: []string{}, + expectedError: "requires at least 1 argument(s).", + }, + { + name: "import-failed", + args: []string{"testdata/import-command-success.input.txt"}, + expectedError: "something went wrong", + imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) { + return nil, errors.Errorf("something went wrong") + }, + }, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + cmd := NewImportCommand(test.NewFakeCli(&fakeClient{imageImportFunc: tc.imageImportFunc}, buf)) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + } +} + +func TestNewImportCommandInvalidFile(t *testing.T) { + cmd := NewImportCommand(test.NewFakeCli(&fakeClient{}, new(bytes.Buffer))) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs([]string{"testdata/import-command-success.unexistent-file"}) + testutil.ErrorContains(t, cmd.Execute(), "testdata/import-command-success.unexistent-file") +} + +func TestNewImportCommandSuccess(t *testing.T) { + testCases := []struct { + name string + args []string + imageImportFunc func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) + }{ + { + name: "simple", + args: []string{"testdata/import-command-success.input.txt"}, + }, + { + name: "terminal-source", + args: []string{"-"}, + }, + { + name: "double", + args: []string{"-", "image:local"}, + imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) { + assert.Equal(t, ref, "image:local") + return ioutil.NopCloser(strings.NewReader("")), nil + }, + }, + { + name: "message", + args: []string{"--message", "test message", "-"}, + imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) { + assert.Equal(t, options.Message, "test message") + return ioutil.NopCloser(strings.NewReader("")), nil + }, + }, + { + name: "change", + args: []string{"--change", "ENV DEBUG true", "-"}, + imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) { + assert.Equal(t, options.Changes[0], "ENV DEBUG true") + return ioutil.NopCloser(strings.NewReader("")), nil + }, + }, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + cmd := NewImportCommand(test.NewFakeCli(&fakeClient{imageImportFunc: tc.imageImportFunc}, buf)) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + assert.NoError(t, cmd.Execute()) + } +} diff --git a/cli/command/image/inspect.go b/cli/command/image/inspect.go index 5d805fcfd5..a510e30764 100644 --- a/cli/command/image/inspect.go +++ b/cli/command/image/inspect.go @@ -15,7 +15,7 @@ type inspectOptions struct { } // newInspectCommand creates a new cobra.Command for `docker image inspect` -func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command { +func newInspectCommand(dockerCli command.Cli) *cobra.Command { var opts inspectOptions cmd := &cobra.Command{ @@ -33,7 +33,7 @@ func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command { return cmd } -func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error { +func runInspect(dockerCli command.Cli, opts inspectOptions) error { client := dockerCli.Client() ctx := context.Background() diff --git a/cli/command/image/inspect_test.go b/cli/command/image/inspect_test.go new file mode 100644 index 0000000000..ffe77dd26b --- /dev/null +++ b/cli/command/image/inspect_test.go @@ -0,0 +1,92 @@ +package image + +import ( + "bytes" + "fmt" + "io/ioutil" + "testing" + + "github.com/docker/cli/cli/internal/test" + "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/testutil" + "github.com/docker/docker/pkg/testutil/golden" + "github.com/stretchr/testify/assert" +) + +func TestNewInspectCommandErrors(t *testing.T) { + testCases := []struct { + name string + args []string + expectedError string + }{ + { + name: "wrong-args", + args: []string{}, + expectedError: "requires at least 1 argument(s).", + }, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + cmd := newInspectCommand(test.NewFakeCli(&fakeClient{}, buf)) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + } +} + +func TestNewInspectCommandSuccess(t *testing.T) { + imageInspectInvocationCount := 0 + testCases := []struct { + name string + args []string + imageCount int + imageInspectFunc func(image string) (types.ImageInspect, []byte, error) + }{ + { + name: "simple", + args: []string{"image"}, + imageCount: 1, + imageInspectFunc: func(image string) (types.ImageInspect, []byte, error) { + imageInspectInvocationCount++ + assert.Equal(t, image, "image") + return types.ImageInspect{}, nil, nil + }, + }, + { + name: "format", + imageCount: 1, + args: []string{"--format='{{.ID}}'", "image"}, + imageInspectFunc: func(image string) (types.ImageInspect, []byte, error) { + imageInspectInvocationCount++ + return types.ImageInspect{ID: image}, nil, nil + }, + }, + { + name: "simple-many", + args: []string{"image1", "image2"}, + imageCount: 2, + imageInspectFunc: func(image string) (types.ImageInspect, []byte, error) { + imageInspectInvocationCount++ + if imageInspectInvocationCount == 1 { + assert.Equal(t, image, "image1") + } else { + assert.Equal(t, image, "image2") + } + return types.ImageInspect{}, nil, nil + }, + }, + } + for _, tc := range testCases { + imageInspectInvocationCount = 0 + buf := new(bytes.Buffer) + cmd := newInspectCommand(test.NewFakeCli(&fakeClient{imageInspectFunc: tc.imageInspectFunc}, buf)) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + err := cmd.Execute() + assert.NoError(t, err) + actual := buf.String() + expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("inspect-command-success.%s.golden", tc.name))[:]) + testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected) + assert.Equal(t, tc.imageCount, imageInspectInvocationCount) + } +} diff --git a/cli/command/image/list.go b/cli/command/image/list.go index 86364489eb..93edaa91e2 100644 --- a/cli/command/image/list.go +++ b/cli/command/image/list.go @@ -23,7 +23,7 @@ type imagesOptions struct { } // NewImagesCommand creates a new `docker images` command -func NewImagesCommand(dockerCli *command.DockerCli) *cobra.Command { +func NewImagesCommand(dockerCli command.Cli) *cobra.Command { opts := imagesOptions{filter: opts.NewFilterOpt()} cmd := &cobra.Command{ @@ -50,14 +50,14 @@ func NewImagesCommand(dockerCli *command.DockerCli) *cobra.Command { return cmd } -func newListCommand(dockerCli *command.DockerCli) *cobra.Command { +func newListCommand(dockerCli command.Cli) *cobra.Command { cmd := *NewImagesCommand(dockerCli) cmd.Aliases = []string{"images", "list"} cmd.Use = "ls [OPTIONS] [REPOSITORY[:TAG]]" return &cmd } -func runImages(dockerCli *command.DockerCli, opts imagesOptions) error { +func runImages(dockerCli command.Cli, opts imagesOptions) error { ctx := context.Background() filters := opts.filter.Value() diff --git a/cli/command/image/list_test.go b/cli/command/image/list_test.go new file mode 100644 index 0000000000..8b1ba374b6 --- /dev/null +++ b/cli/command/image/list_test.go @@ -0,0 +1,102 @@ +package image + +import ( + "bytes" + "fmt" + "io/ioutil" + "testing" + + "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/cli/internal/test" + "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/testutil" + "github.com/docker/docker/pkg/testutil/golden" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" +) + +func TestNewImagesCommandErrors(t *testing.T) { + testCases := []struct { + name string + args []string + expectedError string + imageListFunc func(options types.ImageListOptions) ([]types.ImageSummary, error) + }{ + { + name: "wrong-args", + args: []string{"arg1", "arg2"}, + expectedError: "requires at most 1 argument(s).", + }, + { + name: "failed-list", + expectedError: "something went wrong", + imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) { + return []types.ImageSummary{{}}, errors.Errorf("something went wrong") + }, + }, + } + for _, tc := range testCases { + cmd := NewImagesCommand(test.NewFakeCli(&fakeClient{imageListFunc: tc.imageListFunc}, new(bytes.Buffer))) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + assert.Error(t, cmd.Execute(), tc.expectedError) + } +} + +func TestNewImagesCommandSuccess(t *testing.T) { + testCases := []struct { + name string + args []string + imageFormat string + imageListFunc func(options types.ImageListOptions) ([]types.ImageSummary, error) + }{ + { + name: "simple", + }, + { + name: "format", + imageFormat: "raw", + }, + { + name: "quiet-format", + args: []string{"-q"}, + imageFormat: "table", + }, + { + name: "match-name", + args: []string{"image"}, + imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) { + assert.Equal(t, options.Filters.Get("reference")[0], "image") + return []types.ImageSummary{{}}, nil + }, + }, + { + name: "filters", + args: []string{"--filter", "name=value"}, + imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) { + assert.Equal(t, options.Filters.Get("name")[0], "value") + return []types.ImageSummary{{}}, nil + }, + }, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + cli := test.NewFakeCli(&fakeClient{imageListFunc: tc.imageListFunc}, buf) + cli.SetConfigfile(&configfile.ConfigFile{ImagesFormat: tc.imageFormat}) + cmd := NewImagesCommand(cli) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + err := cmd.Execute() + assert.NoError(t, err) + actual := buf.String() + expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("list-command-success.%s.golden", tc.name))[:]) + testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected) + } +} + +func TestNewListCommandAlias(t *testing.T) { + cmd := newListCommand(test.NewFakeCli(&fakeClient{}, new(bytes.Buffer))) + assert.Equal(t, cmd.HasAlias("images"), true) + assert.Equal(t, cmd.HasAlias("list"), true) + assert.Equal(t, cmd.HasAlias("other"), false) +} diff --git a/cli/command/image/load.go b/cli/command/image/load.go index f4b6b4490e..6708599fd7 100644 --- a/cli/command/image/load.go +++ b/cli/command/image/load.go @@ -19,7 +19,7 @@ type loadOptions struct { } // NewLoadCommand creates a new `docker load` command -func NewLoadCommand(dockerCli *command.DockerCli) *cobra.Command { +func NewLoadCommand(dockerCli command.Cli) *cobra.Command { var opts loadOptions cmd := &cobra.Command{ @@ -39,7 +39,7 @@ func NewLoadCommand(dockerCli *command.DockerCli) *cobra.Command { return cmd } -func runLoad(dockerCli *command.DockerCli, opts loadOptions) error { +func runLoad(dockerCli command.Cli, opts loadOptions) error { var input io.Reader = dockerCli.In() if opts.input != "" { diff --git a/cli/command/image/load_test.go b/cli/command/image/load_test.go new file mode 100644 index 0000000000..3434d150d4 --- /dev/null +++ b/cli/command/image/load_test.go @@ -0,0 +1,106 @@ +package image + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "strings" + "testing" + + "github.com/docker/cli/cli/internal/test" + "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/testutil" + "github.com/docker/docker/pkg/testutil/golden" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" +) + +func TestNewLoadCommandErrors(t *testing.T) { + testCases := []struct { + name string + args []string + isTerminalIn bool + expectedError string + imageLoadFunc func(input io.Reader, quiet bool) (types.ImageLoadResponse, error) + }{ + { + name: "wrong-args", + args: []string{"arg"}, + expectedError: "accepts no argument(s).", + }, + { + name: "input-to-terminal", + isTerminalIn: true, + expectedError: "requested load from stdin, but stdin is empty", + }, + { + name: "pull-error", + expectedError: "something went wrong", + imageLoadFunc: func(input io.Reader, quiet bool) (types.ImageLoadResponse, error) { + return types.ImageLoadResponse{}, errors.Errorf("something went wrong") + }, + }, + } + for _, tc := range testCases { + cli := test.NewFakeCli(&fakeClient{imageLoadFunc: tc.imageLoadFunc}, new(bytes.Buffer)) + cli.In().SetIsTerminal(tc.isTerminalIn) + cmd := NewLoadCommand(cli) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + assert.Error(t, cmd.Execute(), tc.expectedError) + } +} + +func TestNewLoadCommandInvalidInput(t *testing.T) { + expectedError := "open *" + cmd := NewLoadCommand(test.NewFakeCli(&fakeClient{}, new(bytes.Buffer))) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs([]string{"--input", "*"}) + err := cmd.Execute() + assert.NotNil(t, err) + assert.Contains(t, err.Error(), expectedError) +} + +func TestNewLoadCommandSuccess(t *testing.T) { + testCases := []struct { + name string + args []string + imageLoadFunc func(input io.Reader, quiet bool) (types.ImageLoadResponse, error) + }{ + { + name: "simple", + imageLoadFunc: func(input io.Reader, quiet bool) (types.ImageLoadResponse, error) { + return types.ImageLoadResponse{Body: ioutil.NopCloser(strings.NewReader("Success"))}, nil + }, + }, + { + name: "json", + imageLoadFunc: func(input io.Reader, quiet bool) (types.ImageLoadResponse, error) { + json := "{\"ID\": \"1\"}" + return types.ImageLoadResponse{ + Body: ioutil.NopCloser(strings.NewReader(json)), + JSON: true, + }, nil + }, + }, + { + name: "input-file", + args: []string{"--input", "testdata/load-command-success.input.txt"}, + imageLoadFunc: func(input io.Reader, quiet bool) (types.ImageLoadResponse, error) { + return types.ImageLoadResponse{Body: ioutil.NopCloser(strings.NewReader("Success"))}, nil + }, + }, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + cmd := NewLoadCommand(test.NewFakeCli(&fakeClient{imageLoadFunc: tc.imageLoadFunc}, buf)) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + err := cmd.Execute() + assert.NoError(t, err) + actual := buf.String() + expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("load-command-success.%s.golden", tc.name))[:]) + testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected) + } +} diff --git a/cli/command/image/prune.go b/cli/command/image/prune.go index a60ce2080a..3d67b0939e 100644 --- a/cli/command/image/prune.go +++ b/cli/command/image/prune.go @@ -19,7 +19,7 @@ type pruneOptions struct { } // NewPruneCommand returns a new cobra prune command for images -func NewPruneCommand(dockerCli *command.DockerCli) *cobra.Command { +func NewPruneCommand(dockerCli command.Cli) *cobra.Command { opts := pruneOptions{filter: opts.NewFilterOpt()} cmd := &cobra.Command{ @@ -55,7 +55,7 @@ Are you sure you want to continue?` Are you sure you want to continue?` ) -func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (spaceReclaimed uint64, output string, err error) { +func runPrune(dockerCli command.Cli, opts pruneOptions) (spaceReclaimed uint64, output string, err error) { pruneFilters := opts.filter.Value() pruneFilters.Add("dangling", fmt.Sprintf("%v", !opts.all)) pruneFilters = command.PruneFilters(dockerCli, pruneFilters) @@ -90,6 +90,6 @@ func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (spaceReclaimed u // RunPrune calls the Image Prune API // This returns the amount of space reclaimed and a detailed output string -func RunPrune(dockerCli *command.DockerCli, all bool, filter opts.FilterOpt) (uint64, string, error) { +func RunPrune(dockerCli command.Cli, all bool, filter opts.FilterOpt) (uint64, string, error) { return runPrune(dockerCli, pruneOptions{force: true, all: all, filter: filter}) } diff --git a/cli/command/image/prune_test.go b/cli/command/image/prune_test.go new file mode 100644 index 0000000000..f118158cdf --- /dev/null +++ b/cli/command/image/prune_test.go @@ -0,0 +1,100 @@ +package image + +import ( + "bytes" + "fmt" + "io/ioutil" + "testing" + + "github.com/docker/cli/cli/internal/test" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/pkg/testutil" + "github.com/docker/docker/pkg/testutil/golden" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" +) + +func TestNewPruneCommandErrors(t *testing.T) { + testCases := []struct { + name string + args []string + expectedError string + imagesPruneFunc func(pruneFilter filters.Args) (types.ImagesPruneReport, error) + }{ + { + name: "wrong-args", + args: []string{"something"}, + expectedError: "accepts no argument(s).", + }, + { + name: "prune-error", + args: []string{"--force"}, + expectedError: "something went wrong", + imagesPruneFunc: func(pruneFilter filters.Args) (types.ImagesPruneReport, error) { + return types.ImagesPruneReport{}, errors.Errorf("something went wrong") + }, + }, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + cmd := NewPruneCommand(test.NewFakeCli(&fakeClient{ + imagesPruneFunc: tc.imagesPruneFunc, + }, buf)) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + assert.Error(t, cmd.Execute(), tc.expectedError) + } +} + +func TestNewPruneCommandSuccess(t *testing.T) { + testCases := []struct { + name string + args []string + imagesPruneFunc func(pruneFilter filters.Args) (types.ImagesPruneReport, error) + }{ + { + name: "all", + args: []string{"--all"}, + imagesPruneFunc: func(pruneFilter filters.Args) (types.ImagesPruneReport, error) { + assert.Equal(t, pruneFilter.Get("dangling")[0], "false") + return types.ImagesPruneReport{}, nil + }, + }, + { + name: "force-deleted", + args: []string{"--force"}, + imagesPruneFunc: func(pruneFilter filters.Args) (types.ImagesPruneReport, error) { + assert.Equal(t, pruneFilter.Get("dangling")[0], "true") + return types.ImagesPruneReport{ + ImagesDeleted: []types.ImageDeleteResponseItem{{Deleted: "image1"}}, + SpaceReclaimed: 1, + }, nil + }, + }, + { + name: "force-untagged", + args: []string{"--force"}, + imagesPruneFunc: func(pruneFilter filters.Args) (types.ImagesPruneReport, error) { + assert.Equal(t, pruneFilter.Get("dangling")[0], "true") + return types.ImagesPruneReport{ + ImagesDeleted: []types.ImageDeleteResponseItem{{Untagged: "image1"}}, + SpaceReclaimed: 2, + }, nil + }, + }, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + cmd := NewPruneCommand(test.NewFakeCli(&fakeClient{ + imagesPruneFunc: tc.imagesPruneFunc, + }, buf)) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + err := cmd.Execute() + assert.NoError(t, err) + actual := buf.String() + expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("prune-command-success.%s.golden", tc.name))[:]) + testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected) + } +} diff --git a/cli/command/image/pull.go b/cli/command/image/pull.go index c4be52fe8a..e60e5a4348 100644 --- a/cli/command/image/pull.go +++ b/cli/command/image/pull.go @@ -19,7 +19,7 @@ type pullOptions struct { } // NewPullCommand creates a new `docker pull` command -func NewPullCommand(dockerCli *command.DockerCli) *cobra.Command { +func NewPullCommand(dockerCli command.Cli) *cobra.Command { var opts pullOptions cmd := &cobra.Command{ @@ -40,7 +40,7 @@ func NewPullCommand(dockerCli *command.DockerCli) *cobra.Command { return cmd } -func runPull(dockerCli *command.DockerCli, opts pullOptions) error { +func runPull(dockerCli command.Cli, opts pullOptions) error { distributionRef, err := reference.ParseNormalizedNamed(opts.remote) if err != nil { return err diff --git a/cli/command/image/pull_test.go b/cli/command/image/pull_test.go new file mode 100644 index 0000000000..c51a8746fc --- /dev/null +++ b/cli/command/image/pull_test.go @@ -0,0 +1,85 @@ +package image + +import ( + "bytes" + "fmt" + "io/ioutil" + "testing" + + "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/internal/test" + "github.com/docker/distribution/reference" + "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/testutil" + "github.com/docker/docker/pkg/testutil/golden" + "github.com/docker/docker/registry" + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" +) + +func TestNewPullCommandErrors(t *testing.T) { + testCases := []struct { + name string + args []string + expectedError string + trustedPullFunc func(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, + authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error + }{ + { + name: "wrong-args", + expectedError: "requires exactly 1 argument(s).", + args: []string{}, + }, + { + name: "invalid-name", + expectedError: "invalid reference format: repository name must be lowercase", + args: []string{"UPPERCASE_REPO"}, + }, + { + name: "all-tags-with-tag", + expectedError: "tag can't be used with --all-tags/-a", + args: []string{"--all-tags", "image:tag"}, + }, + { + name: "pull-error", + args: []string{"--disable-content-trust=false", "image:tag"}, + expectedError: "you are not authorized to perform this operation: server returned 401.", + }, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + cmd := NewPullCommand(test.NewFakeCli(&fakeClient{}, buf)) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + assert.Error(t, cmd.Execute(), tc.expectedError) + } +} + +func TestNewPullCommandSuccess(t *testing.T) { + testCases := []struct { + name string + args []string + trustedPullFunc func(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, + authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error + }{ + { + name: "simple", + args: []string{"image:tag"}, + }, + { + name: "simple-no-tag", + args: []string{"image"}, + }, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + cmd := NewPullCommand(test.NewFakeCli(&fakeClient{}, buf)) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + err := cmd.Execute() + assert.NoError(t, err) + actual := buf.String() + expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("pull-command-success.%s.golden", tc.name))[:]) + testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected) + } +} diff --git a/cli/command/image/push.go b/cli/command/image/push.go index 00b3d96f04..cc95897bd0 100644 --- a/cli/command/image/push.go +++ b/cli/command/image/push.go @@ -12,7 +12,7 @@ import ( ) // NewPushCommand creates a new `docker push` command -func NewPushCommand(dockerCli *command.DockerCli) *cobra.Command { +func NewPushCommand(dockerCli command.Cli) *cobra.Command { cmd := &cobra.Command{ Use: "push [OPTIONS] NAME[:TAG]", Short: "Push an image or a repository to a registry", @@ -29,7 +29,7 @@ func NewPushCommand(dockerCli *command.DockerCli) *cobra.Command { return cmd } -func runPush(dockerCli *command.DockerCli, remote string) error { +func runPush(dockerCli command.Cli, remote string) error { ref, err := reference.ParseNormalizedNamed(remote) if err != nil { return err diff --git a/cli/command/image/push_test.go b/cli/command/image/push_test.go new file mode 100644 index 0000000000..d34941bd42 --- /dev/null +++ b/cli/command/image/push_test.go @@ -0,0 +1,84 @@ +package image + +import ( + "bytes" + "io" + "io/ioutil" + "strings" + "testing" + + "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/internal/test" + "github.com/docker/distribution/reference" + "github.com/docker/docker/api/types" + "github.com/docker/docker/registry" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "golang.org/x/net/context" +) + +func TestNewPushCommandErrors(t *testing.T) { + testCases := []struct { + name string + args []string + expectedError string + imagePushFunc func(ref string, options types.ImagePushOptions) (io.ReadCloser, error) + }{ + { + name: "wrong-args", + args: []string{}, + expectedError: "requires exactly 1 argument(s).", + }, + { + name: "invalid-name", + args: []string{"UPPERCASE_REPO"}, + expectedError: "invalid reference format: repository name must be lowercase", + }, + { + name: "push-failed", + args: []string{"image:repo"}, + expectedError: "Failed to push", + imagePushFunc: func(ref string, options types.ImagePushOptions) (io.ReadCloser, error) { + return ioutil.NopCloser(strings.NewReader("")), errors.Errorf("Failed to push") + }, + }, + { + name: "trust-error", + args: []string{"--disable-content-trust=false", "image:repo"}, + expectedError: "you are not authorized to perform this operation: server returned 401.", + }, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + cmd := NewPushCommand(test.NewFakeCli(&fakeClient{imagePushFunc: tc.imagePushFunc}, buf)) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + assert.Error(t, cmd.Execute(), tc.expectedError) + } +} + +func TestNewPushCommandSuccess(t *testing.T) { + testCases := []struct { + name string + args []string + trustedPushFunc func(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, + ref reference.Named, authConfig types.AuthConfig, + requestPrivilege types.RequestPrivilegeFunc) error + }{ + { + name: "simple", + args: []string{"image:tag"}, + }, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + cmd := NewPushCommand(test.NewFakeCli(&fakeClient{ + imagePushFunc: func(ref string, options types.ImagePushOptions) (io.ReadCloser, error) { + return ioutil.NopCloser(strings.NewReader("")), nil + }, + }, buf)) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + assert.NoError(t, cmd.Execute()) + } +} diff --git a/cli/command/image/remove.go b/cli/command/image/remove.go index 0948bb7bef..91bf2f8786 100644 --- a/cli/command/image/remove.go +++ b/cli/command/image/remove.go @@ -19,7 +19,7 @@ type removeOptions struct { } // NewRemoveCommand creates a new `docker remove` command -func NewRemoveCommand(dockerCli *command.DockerCli) *cobra.Command { +func NewRemoveCommand(dockerCli command.Cli) *cobra.Command { var opts removeOptions cmd := &cobra.Command{ @@ -39,14 +39,14 @@ func NewRemoveCommand(dockerCli *command.DockerCli) *cobra.Command { return cmd } -func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command { +func newRemoveCommand(dockerCli command.Cli) *cobra.Command { cmd := *NewRemoveCommand(dockerCli) cmd.Aliases = []string{"rmi", "remove"} cmd.Use = "rm [OPTIONS] IMAGE [IMAGE...]" return &cmd } -func runRemove(dockerCli *command.DockerCli, opts removeOptions, images []string) error { +func runRemove(dockerCli command.Cli, opts removeOptions, images []string) error { client := dockerCli.Client() ctx := context.Background() diff --git a/cli/command/image/remove_test.go b/cli/command/image/remove_test.go new file mode 100644 index 0000000000..9b6508d4ad --- /dev/null +++ b/cli/command/image/remove_test.go @@ -0,0 +1,103 @@ +package image + +import ( + "bytes" + "fmt" + "io/ioutil" + "testing" + + "github.com/docker/cli/cli/internal/test" + "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/testutil" + "github.com/docker/docker/pkg/testutil/golden" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" +) + +func TestNewRemoveCommandAlias(t *testing.T) { + cmd := newRemoveCommand(test.NewFakeCli(&fakeClient{}, new(bytes.Buffer))) + assert.Equal(t, cmd.HasAlias("rmi"), true) + assert.Equal(t, cmd.HasAlias("remove"), true) + assert.Equal(t, cmd.HasAlias("other"), false) +} + +func TestNewRemoveCommandErrors(t *testing.T) { + testCases := []struct { + name string + args []string + expectedError string + imageRemoveFunc func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) + }{ + { + name: "wrong args", + expectedError: "requires at least 1 argument(s).", + }, + { + name: "ImageRemove fail", + args: []string{"arg1"}, + expectedError: "error removing image", + imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { + assert.Equal(t, options.Force, false) + assert.Equal(t, options.PruneChildren, true) + return []types.ImageDeleteResponseItem{}, errors.Errorf("error removing image") + }, + }, + } + for _, tc := range testCases { + cmd := NewRemoveCommand(test.NewFakeCli(&fakeClient{ + imageRemoveFunc: tc.imageRemoveFunc, + }, new(bytes.Buffer))) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + assert.Error(t, cmd.Execute(), tc.expectedError) + } +} + +func TestNewRemoveCommandSuccess(t *testing.T) { + testCases := []struct { + name string + args []string + imageRemoveFunc func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) + }{ + { + name: "Image Deleted", + args: []string{"image1"}, + imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { + assert.Equal(t, image, "image1") + return []types.ImageDeleteResponseItem{{Deleted: image}}, nil + }, + }, + { + name: "Image Untagged", + args: []string{"image1"}, + imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { + assert.Equal(t, image, "image1") + return []types.ImageDeleteResponseItem{{Untagged: image}}, nil + }, + }, + { + name: "Image Deleted and Untagged", + args: []string{"image1", "image2"}, + imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { + if image == "image1" { + return []types.ImageDeleteResponseItem{{Untagged: image}}, nil + } + return []types.ImageDeleteResponseItem{{Deleted: image}}, nil + }, + }, + } + for _, tc := range testCases { + buf := new(bytes.Buffer) + cmd := NewRemoveCommand(test.NewFakeCli(&fakeClient{ + imageRemoveFunc: tc.imageRemoveFunc, + }, buf)) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + assert.NoError(t, cmd.Execute()) + err := cmd.Execute() + assert.NoError(t, err) + actual := buf.String() + expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("remove-command-success.%s.golden", tc.name))[:]) + testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected) + } +} diff --git a/cli/command/image/save.go b/cli/command/image/save.go index cb049d5eaf..ba666d2740 100644 --- a/cli/command/image/save.go +++ b/cli/command/image/save.go @@ -16,7 +16,7 @@ type saveOptions struct { } // NewSaveCommand creates a new `docker save` command -func NewSaveCommand(dockerCli *command.DockerCli) *cobra.Command { +func NewSaveCommand(dockerCli command.Cli) *cobra.Command { var opts saveOptions cmd := &cobra.Command{ @@ -36,7 +36,7 @@ func NewSaveCommand(dockerCli *command.DockerCli) *cobra.Command { return cmd } -func runSave(dockerCli *command.DockerCli, opts saveOptions) error { +func runSave(dockerCli command.Cli, opts saveOptions) error { if opts.output == "" && dockerCli.Out().IsTerminal() { return errors.New("cowardly refusing to save to a terminal. Use the -o flag or redirect") } diff --git a/cli/command/image/save_test.go b/cli/command/image/save_test.go new file mode 100644 index 0000000000..fe8a04bf6f --- /dev/null +++ b/cli/command/image/save_test.go @@ -0,0 +1,98 @@ +package image + +import ( + "bytes" + "io" + "io/ioutil" + "os" + "strings" + "testing" + + "github.com/docker/cli/cli/internal/test" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" +) + +func TestNewSaveCommandErrors(t *testing.T) { + testCases := []struct { + name string + args []string + isTerminal bool + expectedError string + imageSaveFunc func(images []string) (io.ReadCloser, error) + }{ + { + name: "wrong args", + args: []string{}, + expectedError: "requires at least 1 argument(s).", + }, + { + name: "output to terminal", + args: []string{"output", "file", "arg1"}, + isTerminal: true, + expectedError: "Cowardly refusing to save to a terminal. Use the -o flag or redirect.", + }, + { + name: "ImageSave fail", + args: []string{"arg1"}, + isTerminal: false, + expectedError: "error saving image", + imageSaveFunc: func(images []string) (io.ReadCloser, error) { + return ioutil.NopCloser(strings.NewReader("")), errors.Errorf("error saving image") + }, + }, + } + for _, tc := range testCases { + cli := test.NewFakeCli(&fakeClient{imageSaveFunc: tc.imageSaveFunc}, new(bytes.Buffer)) + cli.Out().SetIsTerminal(tc.isTerminal) + cmd := NewSaveCommand(cli) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + assert.Error(t, cmd.Execute(), tc.expectedError) + } +} + +func TestNewSaveCommandSuccess(t *testing.T) { + testCases := []struct { + args []string + isTerminal bool + imageSaveFunc func(images []string) (io.ReadCloser, error) + deferredFunc func() + }{ + { + args: []string{"-o", "save_tmp_file", "arg1"}, + isTerminal: true, + imageSaveFunc: func(images []string) (io.ReadCloser, error) { + assert.Equal(t, len(images), 1) + assert.Equal(t, images[0], "arg1") + return ioutil.NopCloser(strings.NewReader("")), nil + }, + deferredFunc: func() { + os.Remove("save_tmp_file") + }, + }, + { + args: []string{"arg1", "arg2"}, + isTerminal: false, + imageSaveFunc: func(images []string) (io.ReadCloser, error) { + assert.Equal(t, len(images), 2) + assert.Equal(t, images[0], "arg1") + assert.Equal(t, images[1], "arg2") + return ioutil.NopCloser(strings.NewReader("")), nil + }, + }, + } + for _, tc := range testCases { + cmd := NewSaveCommand(test.NewFakeCli(&fakeClient{ + imageSaveFunc: func(images []string) (io.ReadCloser, error) { + return ioutil.NopCloser(strings.NewReader("")), nil + }, + }, new(bytes.Buffer))) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + assert.NoError(t, cmd.Execute()) + if tc.deferredFunc != nil { + tc.deferredFunc() + } + } +} diff --git a/cli/command/image/tag.go b/cli/command/image/tag.go index ab8dc72498..2a50c127c4 100644 --- a/cli/command/image/tag.go +++ b/cli/command/image/tag.go @@ -14,7 +14,7 @@ type tagOptions struct { } // NewTagCommand creates a new `docker tag` command -func NewTagCommand(dockerCli *command.DockerCli) *cobra.Command { +func NewTagCommand(dockerCli command.Cli) *cobra.Command { var opts tagOptions cmd := &cobra.Command{ @@ -34,7 +34,7 @@ func NewTagCommand(dockerCli *command.DockerCli) *cobra.Command { return cmd } -func runTag(dockerCli *command.DockerCli, opts tagOptions) error { +func runTag(dockerCli command.Cli, opts tagOptions) error { ctx := context.Background() return dockerCli.Client().ImageTag(ctx, opts.image, opts.name) diff --git a/cli/command/image/tag_test.go b/cli/command/image/tag_test.go new file mode 100644 index 0000000000..40f5b46b32 --- /dev/null +++ b/cli/command/image/tag_test.go @@ -0,0 +1,43 @@ +package image + +import ( + "bytes" + "io/ioutil" + "testing" + + "github.com/docker/cli/cli/internal/test" + "github.com/stretchr/testify/assert" +) + +func TestCliNewTagCommandErrors(t *testing.T) { + testCases := [][]string{ + {}, + {"image1"}, + {"image1", "image2", "image3"}, + } + expectedError := "\"tag\" requires exactly 2 argument(s)." + buf := new(bytes.Buffer) + for _, args := range testCases { + cmd := NewTagCommand(test.NewFakeCli(&fakeClient{}, buf)) + cmd.SetArgs(args) + cmd.SetOutput(ioutil.Discard) + assert.Error(t, cmd.Execute(), expectedError) + } +} + +func TestCliNewTagCommand(t *testing.T) { + buf := new(bytes.Buffer) + cmd := NewTagCommand( + test.NewFakeCli(&fakeClient{ + imageTagFunc: func(image string, ref string) error { + assert.Equal(t, image, "image1") + assert.Equal(t, ref, "image2") + return nil + }, + }, buf)) + cmd.SetArgs([]string{"image1", "image2"}) + cmd.SetOutput(ioutil.Discard) + assert.NoError(t, cmd.Execute()) + value, _ := cmd.Flags().GetBool("interspersed") + assert.Equal(t, value, false) +} diff --git a/cli/command/image/testdata/history-command-success.quiet-no-trunc.golden b/cli/command/image/testdata/history-command-success.quiet-no-trunc.golden new file mode 100644 index 0000000000..65103f6354 --- /dev/null +++ b/cli/command/image/testdata/history-command-success.quiet-no-trunc.golden @@ -0,0 +1 @@ +1234567890123456789 diff --git a/cli/command/image/testdata/history-command-success.quiet.golden b/cli/command/image/testdata/history-command-success.quiet.golden new file mode 100644 index 0000000000..42c7c82cc8 --- /dev/null +++ b/cli/command/image/testdata/history-command-success.quiet.golden @@ -0,0 +1 @@ +tag diff --git a/cli/command/image/testdata/history-command-success.simple.golden b/cli/command/image/testdata/history-command-success.simple.golden new file mode 100644 index 0000000000..8aa590526f --- /dev/null +++ b/cli/command/image/testdata/history-command-success.simple.golden @@ -0,0 +1,2 @@ +IMAGE CREATED CREATED BY SIZE COMMENT +123456789012 Less than a second ago 0B diff --git a/cli/command/image/testdata/import-command-success.input.txt b/cli/command/image/testdata/import-command-success.input.txt new file mode 100644 index 0000000000..7ab5949b13 --- /dev/null +++ b/cli/command/image/testdata/import-command-success.input.txt @@ -0,0 +1 @@ +file input test \ No newline at end of file diff --git a/cli/command/image/testdata/inspect-command-success.format.golden b/cli/command/image/testdata/inspect-command-success.format.golden new file mode 100644 index 0000000000..f934996b07 --- /dev/null +++ b/cli/command/image/testdata/inspect-command-success.format.golden @@ -0,0 +1 @@ +'image' diff --git a/cli/command/image/testdata/inspect-command-success.simple-many.golden b/cli/command/image/testdata/inspect-command-success.simple-many.golden new file mode 100644 index 0000000000..d4042589f8 --- /dev/null +++ b/cli/command/image/testdata/inspect-command-success.simple-many.golden @@ -0,0 +1,50 @@ +[ + { + "Id": "", + "RepoTags": null, + "RepoDigests": null, + "Parent": "", + "Comment": "", + "Created": "", + "Container": "", + "ContainerConfig": null, + "DockerVersion": "", + "Author": "", + "Config": null, + "Architecture": "", + "Os": "", + "Size": 0, + "VirtualSize": 0, + "GraphDriver": { + "Data": null, + "Name": "" + }, + "RootFS": { + "Type": "" + } + }, + { + "Id": "", + "RepoTags": null, + "RepoDigests": null, + "Parent": "", + "Comment": "", + "Created": "", + "Container": "", + "ContainerConfig": null, + "DockerVersion": "", + "Author": "", + "Config": null, + "Architecture": "", + "Os": "", + "Size": 0, + "VirtualSize": 0, + "GraphDriver": { + "Data": null, + "Name": "" + }, + "RootFS": { + "Type": "" + } + } +] diff --git a/cli/command/image/testdata/inspect-command-success.simple.golden b/cli/command/image/testdata/inspect-command-success.simple.golden new file mode 100644 index 0000000000..802c52469b --- /dev/null +++ b/cli/command/image/testdata/inspect-command-success.simple.golden @@ -0,0 +1,26 @@ +[ + { + "Id": "", + "RepoTags": null, + "RepoDigests": null, + "Parent": "", + "Comment": "", + "Created": "", + "Container": "", + "ContainerConfig": null, + "DockerVersion": "", + "Author": "", + "Config": null, + "Architecture": "", + "Os": "", + "Size": 0, + "VirtualSize": 0, + "GraphDriver": { + "Data": null, + "Name": "" + }, + "RootFS": { + "Type": "" + } + } +] diff --git a/cli/command/image/testdata/list-command-success.filters.golden b/cli/command/image/testdata/list-command-success.filters.golden new file mode 100644 index 0000000000..e3b8109bcf --- /dev/null +++ b/cli/command/image/testdata/list-command-success.filters.golden @@ -0,0 +1 @@ +REPOSITORY TAG IMAGE ID CREATED SIZE diff --git a/cli/command/image/testdata/list-command-success.format.golden b/cli/command/image/testdata/list-command-success.format.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cli/command/image/testdata/list-command-success.match-name.golden b/cli/command/image/testdata/list-command-success.match-name.golden new file mode 100644 index 0000000000..e3b8109bcf --- /dev/null +++ b/cli/command/image/testdata/list-command-success.match-name.golden @@ -0,0 +1 @@ +REPOSITORY TAG IMAGE ID CREATED SIZE diff --git a/cli/command/image/testdata/list-command-success.quiet-format.golden b/cli/command/image/testdata/list-command-success.quiet-format.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cli/command/image/testdata/list-command-success.simple.golden b/cli/command/image/testdata/list-command-success.simple.golden new file mode 100644 index 0000000000..e3b8109bcf --- /dev/null +++ b/cli/command/image/testdata/list-command-success.simple.golden @@ -0,0 +1 @@ +REPOSITORY TAG IMAGE ID CREATED SIZE diff --git a/cli/command/image/testdata/load-command-success.input-file.golden b/cli/command/image/testdata/load-command-success.input-file.golden new file mode 100644 index 0000000000..51da4200ab --- /dev/null +++ b/cli/command/image/testdata/load-command-success.input-file.golden @@ -0,0 +1 @@ +Success \ No newline at end of file diff --git a/cli/command/image/testdata/load-command-success.input.txt b/cli/command/image/testdata/load-command-success.input.txt new file mode 100644 index 0000000000..7ab5949b13 --- /dev/null +++ b/cli/command/image/testdata/load-command-success.input.txt @@ -0,0 +1 @@ +file input test \ No newline at end of file diff --git a/cli/command/image/testdata/load-command-success.json.golden b/cli/command/image/testdata/load-command-success.json.golden new file mode 100644 index 0000000000..c17f16ecd7 --- /dev/null +++ b/cli/command/image/testdata/load-command-success.json.golden @@ -0,0 +1 @@ +1: diff --git a/cli/command/image/testdata/load-command-success.simple.golden b/cli/command/image/testdata/load-command-success.simple.golden new file mode 100644 index 0000000000..51da4200ab --- /dev/null +++ b/cli/command/image/testdata/load-command-success.simple.golden @@ -0,0 +1 @@ +Success \ No newline at end of file diff --git a/cli/command/image/testdata/prune-command-success.all.golden b/cli/command/image/testdata/prune-command-success.all.golden new file mode 100644 index 0000000000..4d1445280c --- /dev/null +++ b/cli/command/image/testdata/prune-command-success.all.golden @@ -0,0 +1,2 @@ +WARNING! This will remove all images without at least one container associated to them. +Are you sure you want to continue? [y/N] Total reclaimed space: 0B diff --git a/cli/command/image/testdata/prune-command-success.force-deleted.golden b/cli/command/image/testdata/prune-command-success.force-deleted.golden new file mode 100644 index 0000000000..1b6efd4a99 --- /dev/null +++ b/cli/command/image/testdata/prune-command-success.force-deleted.golden @@ -0,0 +1,4 @@ +Deleted Images: +deleted: image1 + +Total reclaimed space: 1B diff --git a/cli/command/image/testdata/prune-command-success.force-untagged.golden b/cli/command/image/testdata/prune-command-success.force-untagged.golden new file mode 100644 index 0000000000..725468fe56 --- /dev/null +++ b/cli/command/image/testdata/prune-command-success.force-untagged.golden @@ -0,0 +1,4 @@ +Deleted Images: +untagged: image1 + +Total reclaimed space: 2B diff --git a/cli/command/image/testdata/pull-command-success.simple-no-tag.golden b/cli/command/image/testdata/pull-command-success.simple-no-tag.golden new file mode 100644 index 0000000000..946de409a4 --- /dev/null +++ b/cli/command/image/testdata/pull-command-success.simple-no-tag.golden @@ -0,0 +1 @@ +Using default tag: latest diff --git a/cli/command/image/testdata/pull-command-success.simple.golden b/cli/command/image/testdata/pull-command-success.simple.golden new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cli/command/image/testdata/remove-command-success.Image Deleted and Untagged.golden b/cli/command/image/testdata/remove-command-success.Image Deleted and Untagged.golden new file mode 100644 index 0000000000..4efc53719d --- /dev/null +++ b/cli/command/image/testdata/remove-command-success.Image Deleted and Untagged.golden @@ -0,0 +1,4 @@ +Untagged: image1 +Deleted: image2 +Untagged: image1 +Deleted: image2 diff --git a/cli/command/image/testdata/remove-command-success.Image Deleted.golden b/cli/command/image/testdata/remove-command-success.Image Deleted.golden new file mode 100644 index 0000000000..382724d39f --- /dev/null +++ b/cli/command/image/testdata/remove-command-success.Image Deleted.golden @@ -0,0 +1,2 @@ +Deleted: image1 +Deleted: image1 diff --git a/cli/command/image/testdata/remove-command-success.Image Untagged.golden b/cli/command/image/testdata/remove-command-success.Image Untagged.golden new file mode 100644 index 0000000000..c795dac19f --- /dev/null +++ b/cli/command/image/testdata/remove-command-success.Image Untagged.golden @@ -0,0 +1,2 @@ +Untagged: image1 +Untagged: image1 diff --git a/cli/command/image/trust.go b/cli/command/image/trust.go index 282fcffa3d..c5b50ba460 100644 --- a/cli/command/image/trust.go +++ b/cli/command/image/trust.go @@ -29,7 +29,7 @@ type target struct { } // trustedPush handles content trust pushing of an image -func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error { +func trustedPush(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error { responseBody, err := imagePushPrivileged(ctx, cli, authConfig, ref, requestPrivilege) if err != nil { return err @@ -42,7 +42,7 @@ func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry // PushTrustedReference pushes a canonical reference to the trust server. // nolint: gocyclo -func PushTrustedReference(cli *command.DockerCli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, in io.Reader) error { +func PushTrustedReference(cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, in io.Reader) error { // If it is a trusted push we would like to find the target entry which match the // tag provided in the function and then do an AddTarget later. target := &client.Target{} @@ -203,7 +203,7 @@ func addTargetToAllSignableRoles(repo *client.NotaryRepository, target *client.T } // imagePushPrivileged push the image -func imagePushPrivileged(ctx context.Context, cli *command.DockerCli, authConfig types.AuthConfig, ref reference.Named, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) { +func imagePushPrivileged(ctx context.Context, cli command.Cli, authConfig types.AuthConfig, ref reference.Named, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) { encodedAuth, err := command.EncodeAuthToBase64(authConfig) if err != nil { return nil, err @@ -217,7 +217,7 @@ func imagePushPrivileged(ctx context.Context, cli *command.DockerCli, authConfig } // trustedPull handles content trust pulling of an image -func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error { +func trustedPull(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, requestPrivilege types.RequestPrivilegeFunc) error { var refs []target notaryRepo, err := trust.GetNotaryRepository(cli, repoInfo, authConfig, "pull") @@ -296,7 +296,7 @@ func trustedPull(ctx context.Context, cli *command.DockerCli, repoInfo *registry } // imagePullPrivileged pulls the image and displays it to the output -func imagePullPrivileged(ctx context.Context, cli *command.DockerCli, authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc, all bool) error { +func imagePullPrivileged(ctx context.Context, cli command.Cli, authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc, all bool) error { encodedAuth, err := command.EncodeAuthToBase64(authConfig) if err != nil { @@ -318,7 +318,7 @@ func imagePullPrivileged(ctx context.Context, cli *command.DockerCli, authConfig } // TrustedReference returns the canonical trusted reference for an image reference -func TrustedReference(ctx context.Context, cli *command.DockerCli, ref reference.NamedTagged, rs registry.Service) (reference.Canonical, error) { +func TrustedReference(ctx context.Context, cli command.Cli, ref reference.NamedTagged, rs registry.Service) (reference.Canonical, error) { var ( repoInfo *registry.RepositoryInfo err error @@ -372,7 +372,7 @@ func convertTarget(t client.Target) (target, error) { } // TagTrusted tags a trusted ref -func TagTrusted(ctx context.Context, cli *command.DockerCli, trustedRef reference.Canonical, ref reference.NamedTagged) error { +func TagTrusted(ctx context.Context, cli command.Cli, trustedRef reference.Canonical, ref reference.NamedTagged) error { // Use familiar references when interacting with client and output familiarRef := reference.FamiliarString(ref) trustedFamiliarRef := reference.FamiliarString(trustedRef) diff --git a/cli/command/in.go b/cli/command/in.go index 50de77ee9b..815d2a2028 100644 --- a/cli/command/in.go +++ b/cli/command/in.go @@ -1,20 +1,16 @@ package command import ( - "io" - "os" - "runtime" - + "errors" "github.com/docker/docker/pkg/term" - "github.com/pkg/errors" + "io" + "runtime" ) // InStream is an input stream used by the DockerCli to read user input type InStream struct { - in io.ReadCloser - fd uintptr - isTerminal bool - state *term.State + CommonStream + in io.ReadCloser } func (i *InStream) Read(p []byte) (int, error) { @@ -26,32 +22,6 @@ func (i *InStream) Close() error { return i.in.Close() } -// FD returns the file descriptor number for this stream -func (i *InStream) FD() uintptr { - return i.fd -} - -// IsTerminal returns true if this stream is connected to a terminal -func (i *InStream) IsTerminal() bool { - return i.isTerminal -} - -// SetRawTerminal sets raw mode on the input terminal -func (i *InStream) SetRawTerminal() (err error) { - if os.Getenv("NORAW") != "" || !i.isTerminal { - return nil - } - i.state, err = term.SetRawTerminal(i.fd) - return err -} - -// RestoreTerminal restores normal mode to the terminal -func (i *InStream) RestoreTerminal() { - if i.state != nil { - term.RestoreTerminal(i.fd, i.state) - } -} - // CheckTty checks if we are trying to attach to a container tty // from a non-tty client input stream, and if so, returns an error. func (i *InStream) CheckTty(attachStdin, ttyMode bool) error { @@ -71,5 +41,5 @@ func (i *InStream) CheckTty(attachStdin, ttyMode bool) error { // NewInStream returns a new InStream object from a ReadCloser func NewInStream(in io.ReadCloser) *InStream { fd, isTerminal := term.GetFdInfo(in) - return &InStream{in: in, fd: fd, isTerminal: isTerminal} + return &InStream{CommonStream: CommonStream{fd: fd, isTerminal: isTerminal}, in: in} } diff --git a/cli/command/out.go b/cli/command/out.go index 85718d7acd..622e758cc4 100644 --- a/cli/command/out.go +++ b/cli/command/out.go @@ -1,52 +1,22 @@ package command import ( - "io" - "os" - "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/term" + "io" ) // OutStream is an output stream used by the DockerCli to write normal program // output. type OutStream struct { - out io.Writer - fd uintptr - isTerminal bool - state *term.State + CommonStream + out io.Writer } func (o *OutStream) Write(p []byte) (int, error) { return o.out.Write(p) } -// FD returns the file descriptor number for this stream -func (o *OutStream) FD() uintptr { - return o.fd -} - -// IsTerminal returns true if this stream is connected to a terminal -func (o *OutStream) IsTerminal() bool { - return o.isTerminal -} - -// SetRawTerminal sets raw mode on the output terminal -func (o *OutStream) SetRawTerminal() (err error) { - if os.Getenv("NORAW") != "" || !o.isTerminal { - return nil - } - o.state, err = term.SetRawTerminalOutput(o.fd) - return err -} - -// RestoreTerminal restores normal mode to the terminal -func (o *OutStream) RestoreTerminal() { - if o.state != nil { - term.RestoreTerminal(o.fd, o.state) - } -} - // GetTtySize returns the height and width in characters of the tty func (o *OutStream) GetTtySize() (uint, uint) { if !o.isTerminal { @@ -65,5 +35,5 @@ func (o *OutStream) GetTtySize() (uint, uint) { // NewOutStream returns a new OutStream object from a Writer func NewOutStream(out io.Writer) *OutStream { fd, isTerminal := term.GetFdInfo(out) - return &OutStream{out: out, fd: fd, isTerminal: isTerminal} + return &OutStream{CommonStream: CommonStream{fd: fd, isTerminal: isTerminal}, out: out} } diff --git a/cli/command/registry.go b/cli/command/registry.go index e13bba775d..884fa6ec40 100644 --- a/cli/command/registry.go +++ b/cli/command/registry.go @@ -21,7 +21,7 @@ import ( ) // ElectAuthServer returns the default registry to use (by asking the daemon) -func ElectAuthServer(ctx context.Context, cli *DockerCli) string { +func ElectAuthServer(ctx context.Context, cli Cli) string { // The daemon `/info` endpoint informs us of the default registry being // used. This is essential in cross-platforms environment, where for // example a Linux client might be interacting with a Windows daemon, hence @@ -46,7 +46,7 @@ func EncodeAuthToBase64(authConfig types.AuthConfig) (string, error) { // RegistryAuthenticationPrivilegedFunc returns a RequestPrivilegeFunc from the specified registry index info // for the given command. -func RegistryAuthenticationPrivilegedFunc(cli *DockerCli, index *registrytypes.IndexInfo, cmdName string) types.RequestPrivilegeFunc { +func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInfo, cmdName string) types.RequestPrivilegeFunc { return func() (string, error) { fmt.Fprintf(cli.Out(), "\nPlease login prior to %s:\n", cmdName) indexServer := registry.GetAuthConfigKey(index) @@ -62,7 +62,7 @@ func RegistryAuthenticationPrivilegedFunc(cli *DockerCli, index *registrytypes.I // ResolveAuthConfig is like registry.ResolveAuthConfig, but if using the // default index, it uses the default index name for the daemon's platform, // not the client's platform. -func ResolveAuthConfig(ctx context.Context, cli *DockerCli, index *registrytypes.IndexInfo) types.AuthConfig { +func ResolveAuthConfig(ctx context.Context, cli Cli, index *registrytypes.IndexInfo) types.AuthConfig { configKey := index.Name if index.Official { configKey = ElectAuthServer(ctx, cli) @@ -73,10 +73,10 @@ func ResolveAuthConfig(ctx context.Context, cli *DockerCli, index *registrytypes } // ConfigureAuth returns an AuthConfig from the specified user, password and server. -func ConfigureAuth(cli *DockerCli, flUser, flPassword, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) { +func ConfigureAuth(cli Cli, flUser, flPassword, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) { // On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210 if runtime.GOOS == "windows" { - cli.in = NewInStream(os.Stdin) + cli.SetIn(NewInStream(os.Stdin)) } if !isDefaultRegistry { @@ -160,7 +160,7 @@ func promptWithDefault(out io.Writer, prompt string, configDefault string) { } // RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete image -func RetrieveAuthTokenFromImage(ctx context.Context, cli *DockerCli, image string) (string, error) { +func RetrieveAuthTokenFromImage(ctx context.Context, cli Cli, image string) (string, error) { // Retrieve encoded auth token from the image reference authConfig, err := resolveAuthConfigFromImage(ctx, cli, image) if err != nil { @@ -174,7 +174,7 @@ func RetrieveAuthTokenFromImage(ctx context.Context, cli *DockerCli, image strin } // resolveAuthConfigFromImage retrieves that AuthConfig using the image string -func resolveAuthConfigFromImage(ctx context.Context, cli *DockerCli, image string) (types.AuthConfig, error) { +func resolveAuthConfigFromImage(ctx context.Context, cli Cli, image string) (types.AuthConfig, error) { registryRef, err := reference.ParseNormalizedNamed(image) if err != nil { return types.AuthConfig{}, err diff --git a/cli/command/stream.go b/cli/command/stream.go new file mode 100644 index 0000000000..13a54cc672 --- /dev/null +++ b/cli/command/stream.go @@ -0,0 +1,44 @@ +package command + +import ( + "github.com/docker/docker/pkg/term" + "os" +) + +// CommonStream is an input stream used by the DockerCli to read user input +type CommonStream struct { + fd uintptr + isTerminal bool + state *term.State +} + +// FD returns the file descriptor number for this stream +func (s *CommonStream) FD() uintptr { + return s.fd +} + +// IsTerminal returns true if this stream is connected to a terminal +func (s *CommonStream) IsTerminal() bool { + return s.isTerminal +} + +// SetRawTerminal sets raw mode on the input terminal +func (s *CommonStream) SetRawTerminal() (err error) { + if os.Getenv("NORAW") != "" || !s.isTerminal { + return nil + } + s.state, err = term.SetRawTerminal(s.fd) + return err +} + +// RestoreTerminal restores normal mode to the terminal +func (s *CommonStream) RestoreTerminal() { + if s.state != nil { + term.RestoreTerminal(s.fd, s.state) + } +} + +// SetIsTerminal sets the boolean used for isTerminal +func (s *CommonStream) SetIsTerminal(isTerminal bool) { + s.isTerminal = isTerminal +} diff --git a/cli/command/swarm/unlock_test.go b/cli/command/swarm/unlock_test.go index 0bd433671d..991365f873 100644 --- a/cli/command/swarm/unlock_test.go +++ b/cli/command/swarm/unlock_test.go @@ -6,6 +6,7 @@ import ( "strings" "testing" + "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/internal/test" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" @@ -96,7 +97,7 @@ func TestSwarmUnlock(t *testing.T) { return nil }, }, buf) - dockerCli.SetIn(ioutil.NopCloser(strings.NewReader(input))) + dockerCli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input)))) cmd := newUnlockCommand(dockerCli) assert.NoError(t, cmd.Execute()) } diff --git a/cli/command/volume/prune_test.go b/cli/command/volume/prune_test.go index 2381a41458..33a8d5dc19 100644 --- a/cli/command/volume/prune_test.go +++ b/cli/command/volume/prune_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/internal/test" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" @@ -91,7 +92,7 @@ func TestVolumePrunePromptYes(t *testing.T) { volumePruneFunc: simplePruneFunc, }, buf) - cli.SetIn(ioutil.NopCloser(strings.NewReader(input))) + cli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input)))) cmd := NewPruneCommand( cli, ) @@ -113,7 +114,7 @@ func TestVolumePrunePromptNo(t *testing.T) { volumePruneFunc: simplePruneFunc, }, buf) - cli.SetIn(ioutil.NopCloser(strings.NewReader(input))) + cli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input)))) cmd := NewPruneCommand( cli, ) diff --git a/cli/internal/test/cli.go b/cli/internal/test/cli.go index 081588b0fd..0a0eae3841 100644 --- a/cli/internal/test/cli.go +++ b/cli/internal/test/cli.go @@ -7,6 +7,7 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/cli/config/credentials" "github.com/docker/cli/client" ) @@ -15,23 +16,24 @@ type FakeCli struct { command.DockerCli client client.APIClient configfile *configfile.ConfigFile - out io.Writer + out *command.OutStream err io.Writer - in io.ReadCloser + in *command.InStream + store credentials.Store } // NewFakeCli returns a Cli backed by the fakeCli func NewFakeCli(client client.APIClient, out io.Writer) *FakeCli { return &FakeCli{ client: client, - out: out, + out: command.NewOutStream(out), err: ioutil.Discard, - in: ioutil.NopCloser(strings.NewReader("")), + in: command.NewInStream(ioutil.NopCloser(strings.NewReader(""))), } } // SetIn sets the input of the cli to the specified ReadCloser -func (c *FakeCli) SetIn(in io.ReadCloser) { +func (c *FakeCli) SetIn(in *command.InStream) { c.in = in } @@ -52,7 +54,7 @@ func (c *FakeCli) Client() client.APIClient { // Out returns the output stream (stdout) the cli should write on func (c *FakeCli) Out() *command.OutStream { - return command.NewOutStream(c.out) + return c.out } // Err returns the output stream (stderr) the cli should write on @@ -62,10 +64,18 @@ func (c *FakeCli) Err() io.Writer { // In returns the input stream the cli will use func (c *FakeCli) In() *command.InStream { - return command.NewInStream(c.in) + return c.in } // ConfigFile returns the cli configfile object (to get client configuration) func (c *FakeCli) ConfigFile() *configfile.ConfigFile { return c.configfile } + +// CredentialsStore returns the fake store the cli will use +func (c *FakeCli) CredentialsStore(serverAddress string) credentials.Store { + if c.store == nil { + c.store = NewFakeStore() + } + return c.store +} diff --git a/cli/internal/test/store.go b/cli/internal/test/store.go new file mode 100644 index 0000000000..28e52bab05 --- /dev/null +++ b/cli/internal/test/store.go @@ -0,0 +1,74 @@ +package test + +import ( + "github.com/docker/cli/cli/config/credentials" + "github.com/docker/docker/api/types" +) + +// fake store implements a credentials.Store that only acts as an in memory map +type fakeStore struct { + store map[string]types.AuthConfig + eraseFunc func(serverAddress string) error + getFunc func(serverAddress string) (types.AuthConfig, error) + getAllFunc func() (map[string]types.AuthConfig, error) + storeFunc func(authConfig types.AuthConfig) error +} + +// NewFakeStore creates a new file credentials store. +func NewFakeStore() credentials.Store { + return &fakeStore{store: map[string]types.AuthConfig{}} +} + +func (c *fakeStore) SetStore(store map[string]types.AuthConfig) { + c.store = store +} + +func (c *fakeStore) SetEraseFunc(eraseFunc func(string) error) { + c.eraseFunc = eraseFunc +} + +func (c *fakeStore) SetGetFunc(getFunc func(string) (types.AuthConfig, error)) { + c.getFunc = getFunc +} + +func (c *fakeStore) SetGetAllFunc(getAllFunc func() (map[string]types.AuthConfig, error)) { + c.getAllFunc = getAllFunc +} + +func (c *fakeStore) SetStoreFunc(storeFunc func(types.AuthConfig) error) { + c.storeFunc = storeFunc +} + +// Erase removes the given credentials from the map store +func (c *fakeStore) Erase(serverAddress string) error { + if c.eraseFunc != nil { + return c.eraseFunc(serverAddress) + } + delete(c.store, serverAddress) + return nil +} + +// Get retrieves credentials for a specific server from the map store. +func (c *fakeStore) Get(serverAddress string) (types.AuthConfig, error) { + if c.getFunc != nil { + return c.getFunc(serverAddress) + } + authConfig, _ := c.store[serverAddress] + return authConfig, nil +} + +func (c *fakeStore) GetAll() (map[string]types.AuthConfig, error) { + if c.getAllFunc != nil { + return c.getAllFunc() + } + return c.store, nil +} + +// Store saves the given credentials in the map store. +func (c *fakeStore) Store(authConfig types.AuthConfig) error { + if c.storeFunc != nil { + return c.storeFunc(authConfig) + } + c.store[authConfig.ServerAddress] = authConfig + return nil +} From 5b6bd9286284113261f6acce08120fe01a7cb54b Mon Sep 17 00:00:00 2001 From: Boaz Shuster Date: Sun, 5 Mar 2017 20:02:03 +0200 Subject: [PATCH 10/18] Add format to docker stack ls Signed-off-by: Boaz Shuster --- cli/command/formatter/stack.go | 67 +++++++++++++++++++++++++++++ cli/command/formatter/stack_test.go | 64 +++++++++++++++++++++++++++ cli/command/stack/list.go | 65 ++++++++-------------------- 3 files changed, 150 insertions(+), 46 deletions(-) create mode 100644 cli/command/formatter/stack.go create mode 100644 cli/command/formatter/stack_test.go diff --git a/cli/command/formatter/stack.go b/cli/command/formatter/stack.go new file mode 100644 index 0000000000..676bcc05fe --- /dev/null +++ b/cli/command/formatter/stack.go @@ -0,0 +1,67 @@ +package formatter + +import ( + "strconv" +) + +const ( + defaultStackTableFormat = "table {{.Name}}\t{{.Services}}" + + stackServicesHeader = "SERVICES" +) + +// Stack contains deployed stack information. +type Stack struct { + // Name is the name of the stack + Name string + // Services is the number of the services + Services int +} + +// NewStackFormat returns a format for use with a stack Context +func NewStackFormat(source string) Format { + switch source { + case TableFormatKey: + return defaultStackTableFormat + } + return Format(source) +} + +// StackWrite writes formatted stacks using the Context +func StackWrite(ctx Context, stacks []*Stack) error { + render := func(format func(subContext subContext) error) error { + for _, stack := range stacks { + if err := format(&stackContext{s: stack}); err != nil { + return err + } + } + return nil + } + return ctx.Write(newStackContext(), render) +} + +type stackContext struct { + HeaderContext + s *Stack +} + +func newStackContext() *stackContext { + stackCtx := stackContext{} + stackCtx.header = map[string]string{ + "Name": nameHeader, + "Services": stackServicesHeader, + } + return &stackCtx +} + +func (s *stackContext) MarshalJSON() ([]byte, error) { + return marshalJSON(s) +} + +func (s *stackContext) Name() string { + return s.s.Name +} + +func (s *stackContext) Services() string { + return strconv.Itoa(s.s.Services) +} diff --git a/cli/command/formatter/stack_test.go b/cli/command/formatter/stack_test.go new file mode 100644 index 0000000000..b18ae7f083 --- /dev/null +++ b/cli/command/formatter/stack_test.go @@ -0,0 +1,64 @@ +package formatter + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStackContextWrite(t *testing.T) { + cases := []struct { + context Context + expected string + }{ + // Errors + { + Context{Format: "{{InvalidFunction}}"}, + `Template parsing error: template: :1: function "InvalidFunction" not defined +`, + }, + { + Context{Format: "{{nil}}"}, + `Template parsing error: template: :1:2: executing "" at : nil is not a command +`, + }, + // Table format + { + Context{Format: NewStackFormat("table")}, + `NAME SERVICES +baz 2 +bar 1 +`, + }, + { + Context{Format: NewStackFormat("table {{.Name}}")}, + `NAME +baz +bar +`, + }, + // Custom Format + { + Context{Format: NewStackFormat("{{.Name}}")}, + `baz +bar +`, + }, + } + + stacks := []*Stack{ + {Name: "baz", Services: 2}, + {Name: "bar", Services: 1}, + } + for _, testcase := range cases { + out := bytes.NewBufferString("") + testcase.context.Output = out + err := StackWrite(testcase.context, stacks) + if err != nil { + assert.Error(t, err, testcase.expected) + } else { + assert.Equal(t, out.String(), testcase.expected) + } + } +} diff --git a/cli/command/stack/list.go b/cli/command/stack/list.go index 7b1f8e3559..c1402b3005 100644 --- a/cli/command/stack/list.go +++ b/cli/command/stack/list.go @@ -1,14 +1,11 @@ package stack import ( - "fmt" - "io" "sort" - "strconv" - "text/tabwriter" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/command/formatter" "github.com/docker/cli/cli/compose/convert" "github.com/docker/cli/client" "github.com/docker/docker/api/types" @@ -17,11 +14,8 @@ import ( "golang.org/x/net/context" ) -const ( - listItemFmt = "%s\t%s\n" -) - type listOptions struct { + format string } func newListCommand(dockerCli *command.DockerCli) *cobra.Command { @@ -37,6 +31,8 @@ func newListCommand(dockerCli *command.DockerCli) *cobra.Command { }, } + flags := cmd.Flags() + flags.StringVar(&opts.format, "format", "", "Pretty-print stacks using a Go template") return cmd } @@ -48,55 +44,32 @@ func runList(dockerCli *command.DockerCli, opts listOptions) error { if err != nil { return err } - - out := dockerCli.Out() - printTable(out, stacks) - return nil + format := opts.format + if len(format) == 0 { + format = formatter.TableFormatKey + } + stackCtx := formatter.Context{ + Output: dockerCli.Out(), + Format: formatter.NewStackFormat(format), + } + sort.Sort(byName(stacks)) + return formatter.StackWrite(stackCtx, stacks) } -type byName []*stack +type byName []*formatter.Stack func (n byName) Len() int { return len(n) } func (n byName) Swap(i, j int) { n[i], n[j] = n[j], n[i] } func (n byName) Less(i, j int) bool { return n[i].Name < n[j].Name } -func printTable(out io.Writer, stacks []*stack) { - writer := tabwriter.NewWriter(out, 0, 4, 2, ' ', 0) - - // Ignore flushing errors - defer writer.Flush() - - sort.Sort(byName(stacks)) - - fmt.Fprintf(writer, listItemFmt, "NAME", "SERVICES") - for _, stack := range stacks { - fmt.Fprintf( - writer, - listItemFmt, - stack.Name, - strconv.Itoa(stack.Services), - ) - } -} - -type stack struct { - // Name is the name of the stack - Name string - // Services is the number of the services - Services int -} - -func getStacks( - ctx context.Context, - apiclient client.APIClient, -) ([]*stack, error) { +func getStacks(ctx context.Context, apiclient client.APIClient) ([]*formatter.Stack, error) { services, err := apiclient.ServiceList( ctx, types.ServiceListOptions{Filters: getAllStacksFilter()}) if err != nil { return nil, err } - m := make(map[string]*stack, 0) + m := make(map[string]*formatter.Stack, 0) for _, service := range services { labels := service.Spec.Labels name, ok := labels[convert.LabelNamespace] @@ -106,7 +79,7 @@ func getStacks( } ztack, ok := m[name] if !ok { - m[name] = &stack{ + m[name] = &formatter.Stack{ Name: name, Services: 1, } @@ -114,7 +87,7 @@ func getStacks( ztack.Services++ } } - var stacks []*stack + var stacks []*formatter.Stack for _, stack := range m { stacks = append(stacks, stack) } From c67589a52cedc11d53d632b9af32d36eea74a80c Mon Sep 17 00:00:00 2001 From: Vincent Demeester Date: Tue, 25 Apr 2017 18:25:05 +0200 Subject: [PATCH 11/18] Fix docker run -it on windows Signed-off-by: Vincent Demeester --- cli/command/in.go | 13 ++++++++++++- cli/command/out.go | 13 ++++++++++++- cli/command/stream.go | 10 ---------- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/cli/command/in.go b/cli/command/in.go index 815d2a2028..54855c6dc2 100644 --- a/cli/command/in.go +++ b/cli/command/in.go @@ -2,9 +2,11 @@ package command import ( "errors" - "github.com/docker/docker/pkg/term" "io" + "os" "runtime" + + "github.com/docker/docker/pkg/term" ) // InStream is an input stream used by the DockerCli to read user input @@ -22,6 +24,15 @@ func (i *InStream) Close() error { return i.in.Close() } +// SetRawTerminal sets raw mode on the input terminal +func (i *InStream) SetRawTerminal() (err error) { + if os.Getenv("NORAW") != "" || !i.CommonStream.isTerminal { + return nil + } + i.CommonStream.state, err = term.SetRawTerminal(i.CommonStream.fd) + return err +} + // CheckTty checks if we are trying to attach to a container tty // from a non-tty client input stream, and if so, returns an error. func (i *InStream) CheckTty(attachStdin, ttyMode bool) error { diff --git a/cli/command/out.go b/cli/command/out.go index 622e758cc4..27b44c235d 100644 --- a/cli/command/out.go +++ b/cli/command/out.go @@ -1,9 +1,11 @@ package command import ( + "io" + "os" + "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/term" - "io" ) // OutStream is an output stream used by the DockerCli to write normal program @@ -17,6 +19,15 @@ func (o *OutStream) Write(p []byte) (int, error) { return o.out.Write(p) } +// SetRawTerminal sets raw mode on the input terminal +func (o *OutStream) SetRawTerminal() (err error) { + if os.Getenv("NORAW") != "" || !o.CommonStream.isTerminal { + return nil + } + o.CommonStream.state, err = term.SetRawTerminalOutput(o.CommonStream.fd) + return err +} + // GetTtySize returns the height and width in characters of the tty func (o *OutStream) GetTtySize() (uint, uint) { if !o.isTerminal { diff --git a/cli/command/stream.go b/cli/command/stream.go index 13a54cc672..71a43fa2e9 100644 --- a/cli/command/stream.go +++ b/cli/command/stream.go @@ -2,7 +2,6 @@ package command import ( "github.com/docker/docker/pkg/term" - "os" ) // CommonStream is an input stream used by the DockerCli to read user input @@ -22,15 +21,6 @@ func (s *CommonStream) IsTerminal() bool { return s.isTerminal } -// SetRawTerminal sets raw mode on the input terminal -func (s *CommonStream) SetRawTerminal() (err error) { - if os.Getenv("NORAW") != "" || !s.isTerminal { - return nil - } - s.state, err = term.SetRawTerminal(s.fd) - return err -} - // RestoreTerminal restores normal mode to the terminal func (s *CommonStream) RestoreTerminal() { if s.state != nil { From 25809f899182da274c380b8f3de9a09e8407e324 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Tue, 25 Apr 2017 11:57:04 -0400 Subject: [PATCH 12/18] move service runtime filter to server Signed-off-by: Evan Hazlett --- cli/command/service/list.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/command/service/list.go b/cli/command/service/list.go index d02c691a9b..338ac9164b 100644 --- a/cli/command/service/list.go +++ b/cli/command/service/list.go @@ -45,7 +45,8 @@ func runList(dockerCli *command.DockerCli, opts listOptions) error { ctx := context.Background() client := dockerCli.Client() - services, err := client.ServiceList(ctx, types.ServiceListOptions{}) + serviceFilters := opts.filter.Value() + services, err := client.ServiceList(ctx, types.ServiceListOptions{Filters: serviceFilters}) if err != nil { return err } From 6f94ab98f5e6c62cfd6306b0f34ccbbdd8390db1 Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Tue, 25 Apr 2017 16:24:07 -0700 Subject: [PATCH 13/18] cli: Correct command/image tests for testify These tests were caught in the crossfire of the transition to testify. testify has a few subtle differences from the similar custom framework it replaced: - Error behaves differently - Equal takes its arguments in a different order This PR also takes the opportunity to use a few shorthands from testify, such as Len, True, and False. Signed-off-by: Aaron Lehmann --- cli/command/image/history_test.go | 2 +- cli/command/image/import_test.go | 6 +++--- cli/command/image/inspect_test.go | 8 ++++---- cli/command/image/list_test.go | 12 ++++++------ cli/command/image/load_test.go | 5 ++--- cli/command/image/prune_test.go | 8 ++++---- cli/command/image/pull_test.go | 2 +- cli/command/image/push_test.go | 3 ++- cli/command/image/remove_test.go | 16 ++++++++-------- cli/command/image/save_test.go | 14 ++++++++------ cli/command/image/tag_test.go | 9 +++++---- 11 files changed, 44 insertions(+), 41 deletions(-) diff --git a/cli/command/image/history_test.go b/cli/command/image/history_test.go index 71605e4fc8..8582c09b62 100644 --- a/cli/command/image/history_test.go +++ b/cli/command/image/history_test.go @@ -102,7 +102,7 @@ func TestNewHistoryCommandSuccess(t *testing.T) { testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected) } else { match, _ := regexp.MatchString(tc.outputRegex, actual) - assert.Equal(t, match, true) + assert.True(t, match) } } } diff --git a/cli/command/image/import_test.go b/cli/command/image/import_test.go index 8e0facf168..134722f0f1 100644 --- a/cli/command/image/import_test.go +++ b/cli/command/image/import_test.go @@ -69,7 +69,7 @@ func TestNewImportCommandSuccess(t *testing.T) { name: "double", args: []string{"-", "image:local"}, imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) { - assert.Equal(t, ref, "image:local") + assert.Equal(t, "image:local", ref) return ioutil.NopCloser(strings.NewReader("")), nil }, }, @@ -77,7 +77,7 @@ func TestNewImportCommandSuccess(t *testing.T) { name: "message", args: []string{"--message", "test message", "-"}, imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) { - assert.Equal(t, options.Message, "test message") + assert.Equal(t, "test message", options.Message) return ioutil.NopCloser(strings.NewReader("")), nil }, }, @@ -85,7 +85,7 @@ func TestNewImportCommandSuccess(t *testing.T) { name: "change", args: []string{"--change", "ENV DEBUG true", "-"}, imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) { - assert.Equal(t, options.Changes[0], "ENV DEBUG true") + assert.Equal(t, "ENV DEBUG true", options.Changes[0]) return ioutil.NopCloser(strings.NewReader("")), nil }, }, diff --git a/cli/command/image/inspect_test.go b/cli/command/image/inspect_test.go index ffe77dd26b..b3a2c2f5f5 100644 --- a/cli/command/image/inspect_test.go +++ b/cli/command/image/inspect_test.go @@ -48,7 +48,7 @@ func TestNewInspectCommandSuccess(t *testing.T) { imageCount: 1, imageInspectFunc: func(image string) (types.ImageInspect, []byte, error) { imageInspectInvocationCount++ - assert.Equal(t, image, "image") + assert.Equal(t, "image", image) return types.ImageInspect{}, nil, nil }, }, @@ -68,9 +68,9 @@ func TestNewInspectCommandSuccess(t *testing.T) { imageInspectFunc: func(image string) (types.ImageInspect, []byte, error) { imageInspectInvocationCount++ if imageInspectInvocationCount == 1 { - assert.Equal(t, image, "image1") + assert.Equal(t, "image1", image) } else { - assert.Equal(t, image, "image2") + assert.Equal(t, "image2", image) } return types.ImageInspect{}, nil, nil }, @@ -87,6 +87,6 @@ func TestNewInspectCommandSuccess(t *testing.T) { actual := buf.String() expected := string(golden.Get(t, []byte(actual), fmt.Sprintf("inspect-command-success.%s.golden", tc.name))[:]) testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, expected) - assert.Equal(t, tc.imageCount, imageInspectInvocationCount) + assert.Equal(t, imageInspectInvocationCount, tc.imageCount) } } diff --git a/cli/command/image/list_test.go b/cli/command/image/list_test.go index 8b1ba374b6..070b19efb1 100644 --- a/cli/command/image/list_test.go +++ b/cli/command/image/list_test.go @@ -39,7 +39,7 @@ func TestNewImagesCommandErrors(t *testing.T) { cmd := NewImagesCommand(test.NewFakeCli(&fakeClient{imageListFunc: tc.imageListFunc}, new(bytes.Buffer))) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - assert.Error(t, cmd.Execute(), tc.expectedError) + testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -66,7 +66,7 @@ func TestNewImagesCommandSuccess(t *testing.T) { name: "match-name", args: []string{"image"}, imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) { - assert.Equal(t, options.Filters.Get("reference")[0], "image") + assert.Equal(t, "image", options.Filters.Get("reference")[0]) return []types.ImageSummary{{}}, nil }, }, @@ -74,7 +74,7 @@ func TestNewImagesCommandSuccess(t *testing.T) { name: "filters", args: []string{"--filter", "name=value"}, imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) { - assert.Equal(t, options.Filters.Get("name")[0], "value") + assert.Equal(t, "value", options.Filters.Get("name")[0]) return []types.ImageSummary{{}}, nil }, }, @@ -96,7 +96,7 @@ func TestNewImagesCommandSuccess(t *testing.T) { func TestNewListCommandAlias(t *testing.T) { cmd := newListCommand(test.NewFakeCli(&fakeClient{}, new(bytes.Buffer))) - assert.Equal(t, cmd.HasAlias("images"), true) - assert.Equal(t, cmd.HasAlias("list"), true) - assert.Equal(t, cmd.HasAlias("other"), false) + assert.True(t, cmd.HasAlias("images")) + assert.True(t, cmd.HasAlias("list")) + assert.False(t, cmd.HasAlias("other")) } diff --git a/cli/command/image/load_test.go b/cli/command/image/load_test.go index 3434d150d4..ebe1a0c7e3 100644 --- a/cli/command/image/load_test.go +++ b/cli/command/image/load_test.go @@ -48,7 +48,7 @@ func TestNewLoadCommandErrors(t *testing.T) { cmd := NewLoadCommand(cli) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - assert.Error(t, cmd.Execute(), tc.expectedError) + testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -58,8 +58,7 @@ func TestNewLoadCommandInvalidInput(t *testing.T) { cmd.SetOutput(ioutil.Discard) cmd.SetArgs([]string{"--input", "*"}) err := cmd.Execute() - assert.NotNil(t, err) - assert.Contains(t, err.Error(), expectedError) + testutil.ErrorContains(t, err, expectedError) } func TestNewLoadCommandSuccess(t *testing.T) { diff --git a/cli/command/image/prune_test.go b/cli/command/image/prune_test.go index f118158cdf..c17a75d224 100644 --- a/cli/command/image/prune_test.go +++ b/cli/command/image/prune_test.go @@ -43,7 +43,7 @@ func TestNewPruneCommandErrors(t *testing.T) { }, buf)) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - assert.Error(t, cmd.Execute(), tc.expectedError) + testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -57,7 +57,7 @@ func TestNewPruneCommandSuccess(t *testing.T) { name: "all", args: []string{"--all"}, imagesPruneFunc: func(pruneFilter filters.Args) (types.ImagesPruneReport, error) { - assert.Equal(t, pruneFilter.Get("dangling")[0], "false") + assert.Equal(t, "false", pruneFilter.Get("dangling")[0]) return types.ImagesPruneReport{}, nil }, }, @@ -65,7 +65,7 @@ func TestNewPruneCommandSuccess(t *testing.T) { name: "force-deleted", args: []string{"--force"}, imagesPruneFunc: func(pruneFilter filters.Args) (types.ImagesPruneReport, error) { - assert.Equal(t, pruneFilter.Get("dangling")[0], "true") + assert.Equal(t, "true", pruneFilter.Get("dangling")[0]) return types.ImagesPruneReport{ ImagesDeleted: []types.ImageDeleteResponseItem{{Deleted: "image1"}}, SpaceReclaimed: 1, @@ -76,7 +76,7 @@ func TestNewPruneCommandSuccess(t *testing.T) { name: "force-untagged", args: []string{"--force"}, imagesPruneFunc: func(pruneFilter filters.Args) (types.ImagesPruneReport, error) { - assert.Equal(t, pruneFilter.Get("dangling")[0], "true") + assert.Equal(t, "true", pruneFilter.Get("dangling")[0]) return types.ImagesPruneReport{ ImagesDeleted: []types.ImageDeleteResponseItem{{Untagged: "image1"}}, SpaceReclaimed: 2, diff --git a/cli/command/image/pull_test.go b/cli/command/image/pull_test.go index c51a8746fc..b4b57e2abc 100644 --- a/cli/command/image/pull_test.go +++ b/cli/command/image/pull_test.go @@ -51,7 +51,7 @@ func TestNewPullCommandErrors(t *testing.T) { cmd := NewPullCommand(test.NewFakeCli(&fakeClient{}, buf)) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - assert.Error(t, cmd.Execute(), tc.expectedError) + testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) } } diff --git a/cli/command/image/push_test.go b/cli/command/image/push_test.go index d34941bd42..b382ad7ee1 100644 --- a/cli/command/image/push_test.go +++ b/cli/command/image/push_test.go @@ -11,6 +11,7 @@ import ( "github.com/docker/cli/cli/internal/test" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/testutil" "github.com/docker/docker/registry" "github.com/pkg/errors" "github.com/stretchr/testify/assert" @@ -53,7 +54,7 @@ func TestNewPushCommandErrors(t *testing.T) { cmd := NewPushCommand(test.NewFakeCli(&fakeClient{imagePushFunc: tc.imagePushFunc}, buf)) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - assert.Error(t, cmd.Execute(), tc.expectedError) + testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) } } diff --git a/cli/command/image/remove_test.go b/cli/command/image/remove_test.go index 9b6508d4ad..0915729b20 100644 --- a/cli/command/image/remove_test.go +++ b/cli/command/image/remove_test.go @@ -16,9 +16,9 @@ import ( func TestNewRemoveCommandAlias(t *testing.T) { cmd := newRemoveCommand(test.NewFakeCli(&fakeClient{}, new(bytes.Buffer))) - assert.Equal(t, cmd.HasAlias("rmi"), true) - assert.Equal(t, cmd.HasAlias("remove"), true) - assert.Equal(t, cmd.HasAlias("other"), false) + assert.True(t, cmd.HasAlias("rmi")) + assert.True(t, cmd.HasAlias("remove")) + assert.False(t, cmd.HasAlias("other")) } func TestNewRemoveCommandErrors(t *testing.T) { @@ -37,8 +37,8 @@ func TestNewRemoveCommandErrors(t *testing.T) { args: []string{"arg1"}, expectedError: "error removing image", imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { - assert.Equal(t, options.Force, false) - assert.Equal(t, options.PruneChildren, true) + assert.False(t, options.Force) + assert.True(t, options.PruneChildren) return []types.ImageDeleteResponseItem{}, errors.Errorf("error removing image") }, }, @@ -49,7 +49,7 @@ func TestNewRemoveCommandErrors(t *testing.T) { }, new(bytes.Buffer))) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - assert.Error(t, cmd.Execute(), tc.expectedError) + testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -63,7 +63,7 @@ func TestNewRemoveCommandSuccess(t *testing.T) { name: "Image Deleted", args: []string{"image1"}, imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { - assert.Equal(t, image, "image1") + assert.Equal(t, "image1", image) return []types.ImageDeleteResponseItem{{Deleted: image}}, nil }, }, @@ -71,7 +71,7 @@ func TestNewRemoveCommandSuccess(t *testing.T) { name: "Image Untagged", args: []string{"image1"}, imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { - assert.Equal(t, image, "image1") + assert.Equal(t, "image1", image) return []types.ImageDeleteResponseItem{{Untagged: image}}, nil }, }, diff --git a/cli/command/image/save_test.go b/cli/command/image/save_test.go index fe8a04bf6f..df34b3591d 100644 --- a/cli/command/image/save_test.go +++ b/cli/command/image/save_test.go @@ -9,8 +9,10 @@ import ( "testing" "github.com/docker/cli/cli/internal/test" + "github.com/docker/docker/pkg/testutil" "github.com/pkg/errors" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestNewSaveCommandErrors(t *testing.T) { @@ -48,7 +50,7 @@ func TestNewSaveCommandErrors(t *testing.T) { cmd := NewSaveCommand(cli) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - assert.Error(t, cmd.Execute(), tc.expectedError) + testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -63,8 +65,8 @@ func TestNewSaveCommandSuccess(t *testing.T) { args: []string{"-o", "save_tmp_file", "arg1"}, isTerminal: true, imageSaveFunc: func(images []string) (io.ReadCloser, error) { - assert.Equal(t, len(images), 1) - assert.Equal(t, images[0], "arg1") + require.Len(t, images, 1) + assert.Equal(t, "arg1", images[0]) return ioutil.NopCloser(strings.NewReader("")), nil }, deferredFunc: func() { @@ -75,9 +77,9 @@ func TestNewSaveCommandSuccess(t *testing.T) { args: []string{"arg1", "arg2"}, isTerminal: false, imageSaveFunc: func(images []string) (io.ReadCloser, error) { - assert.Equal(t, len(images), 2) - assert.Equal(t, images[0], "arg1") - assert.Equal(t, images[1], "arg2") + require.Len(t, images, 2) + assert.Equal(t, "arg1", images[0]) + assert.Equal(t, "arg2", images[1]) return ioutil.NopCloser(strings.NewReader("")), nil }, }, diff --git a/cli/command/image/tag_test.go b/cli/command/image/tag_test.go index 40f5b46b32..8cf0c534b8 100644 --- a/cli/command/image/tag_test.go +++ b/cli/command/image/tag_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/docker/cli/cli/internal/test" + "github.com/docker/docker/pkg/testutil" "github.com/stretchr/testify/assert" ) @@ -21,7 +22,7 @@ func TestCliNewTagCommandErrors(t *testing.T) { cmd := NewTagCommand(test.NewFakeCli(&fakeClient{}, buf)) cmd.SetArgs(args) cmd.SetOutput(ioutil.Discard) - assert.Error(t, cmd.Execute(), expectedError) + testutil.ErrorContains(t, cmd.Execute(), expectedError) } } @@ -30,8 +31,8 @@ func TestCliNewTagCommand(t *testing.T) { cmd := NewTagCommand( test.NewFakeCli(&fakeClient{ imageTagFunc: func(image string, ref string) error { - assert.Equal(t, image, "image1") - assert.Equal(t, ref, "image2") + assert.Equal(t, "image1", image) + assert.Equal(t, "image2", ref) return nil }, }, buf)) @@ -39,5 +40,5 @@ func TestCliNewTagCommand(t *testing.T) { cmd.SetOutput(ioutil.Discard) assert.NoError(t, cmd.Execute()) value, _ := cmd.Flags().GetBool("interspersed") - assert.Equal(t, value, false) + assert.False(t, value) } From 6665c9c74781e6d860d4fbb9956debeae05edc19 Mon Sep 17 00:00:00 2001 From: Evan Hazlett Date: Wed, 26 Apr 2017 09:09:54 -0400 Subject: [PATCH 14/18] remove service runtime filter in stack command Signed-off-by: Evan Hazlett --- cli/command/stack/common.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cli/command/stack/common.go b/cli/command/stack/common.go index 443ca3297d..3e6b57bd8b 100644 --- a/cli/command/stack/common.go +++ b/cli/command/stack/common.go @@ -18,9 +18,7 @@ func getStackFilter(namespace string) filters.Args { } func getServiceFilter(namespace string) filters.Args { - filter := getStackFilter(namespace) - filter.Add("runtime", string(swarm.RuntimeContainer)) - return filter + return getStackFilter(namespace) } func getStackFilterFromOpt(namespace string, opt opts.FilterOpt) filters.Args { From 81f87595fe3f41ab9eb1216bfb09b10ff0ea80af Mon Sep 17 00:00:00 2001 From: Ying Li Date: Tue, 25 Apr 2017 15:40:46 -0700 Subject: [PATCH 15/18] Add the CACert parameter to the ExternalCA object in order to match swarmkit's API type. Make sure this parameter gets propagated to swarmkit, and also add an extra option to the CLI when providing external CAs to parse the CA cert from a file. Signed-off-by: Ying Li --- cli/command/swarm/opts.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cli/command/swarm/opts.go b/cli/command/swarm/opts.go index 225d38d110..a28dd4aad4 100644 --- a/cli/command/swarm/opts.go +++ b/cli/command/swarm/opts.go @@ -2,7 +2,9 @@ package swarm import ( "encoding/csv" + "encoding/pem" "fmt" + "io/ioutil" "strings" "time" @@ -154,6 +156,15 @@ func parseExternalCA(caSpec string) (*swarm.ExternalCA, error) { case "url": hasURL = true externalCA.URL = value + case "cacert": + cacontents, err := ioutil.ReadFile(value) + if err != nil { + return nil, errors.Wrap(err, "unable to read CA cert for external CA") + } + if pemBlock, _ := pem.Decode(cacontents); pemBlock == nil { + return nil, errors.New("CA cert for external CA must be in PEM format") + } + externalCA.CACert = string(cacontents) default: externalCA.Options[key] = value } From 169160ba63b081c946679fa4cb95a74199165bea Mon Sep 17 00:00:00 2001 From: Flavio Crisciani Date: Fri, 14 Apr 2017 16:54:17 -0700 Subject: [PATCH 16/18] Inroduce SWARM --data-path-addr flag This new flag will allow the configuration of an interface that can be used for data path traffic to be isolated from control plane traffic. This flag is simply percolated down to libnetwork and will be used by all the global scope drivers (today overlay) Negative test added for invalid flag arguments Signed-off-by: Flavio Crisciani --- cli/command/swarm/init.go | 3 +++ cli/command/swarm/join.go | 3 +++ cli/command/swarm/opts.go | 1 + 3 files changed, 7 insertions(+) diff --git a/cli/command/swarm/init.go b/cli/command/swarm/init.go index e64a2c5ae6..ea3189a0c7 100644 --- a/cli/command/swarm/init.go +++ b/cli/command/swarm/init.go @@ -19,6 +19,7 @@ type initOptions struct { listenAddr NodeAddrOption // Not a NodeAddrOption because it has no default port. advertiseAddr string + dataPathAddr string forceNewCluster bool availability string } @@ -40,6 +41,7 @@ func newInitCommand(dockerCli command.Cli) *cobra.Command { flags := cmd.Flags() flags.Var(&opts.listenAddr, flagListenAddr, "Listen address (format: [:port])") flags.StringVar(&opts.advertiseAddr, flagAdvertiseAddr, "", "Advertised address (format: [:port])") + flags.StringVar(&opts.dataPathAddr, flagDataPathAddr, "", "Address or interface to use for data path traffic (format: )") flags.BoolVar(&opts.forceNewCluster, "force-new-cluster", false, "Force create a new cluster from current state") flags.BoolVar(&opts.autolock, flagAutolock, false, "Enable manager autolocking (requiring an unlock key to start a stopped manager)") flags.StringVar(&opts.availability, flagAvailability, "active", `Availability of the node ("active"|"pause"|"drain")`) @@ -54,6 +56,7 @@ func runInit(dockerCli command.Cli, flags *pflag.FlagSet, opts initOptions) erro req := swarm.InitRequest{ ListenAddr: opts.listenAddr.String(), AdvertiseAddr: opts.advertiseAddr, + DataPathAddr: opts.dataPathAddr, ForceNewCluster: opts.forceNewCluster, Spec: opts.swarmOptions.ToSpec(flags), AutoLockManagers: opts.swarmOptions.autolock, diff --git a/cli/command/swarm/join.go b/cli/command/swarm/join.go index ff4351c52e..0f09527d07 100644 --- a/cli/command/swarm/join.go +++ b/cli/command/swarm/join.go @@ -19,6 +19,7 @@ type joinOptions struct { listenAddr NodeAddrOption // Not a NodeAddrOption because it has no default port. advertiseAddr string + dataPathAddr string token string availability string } @@ -41,6 +42,7 @@ func newJoinCommand(dockerCli command.Cli) *cobra.Command { flags := cmd.Flags() flags.Var(&opts.listenAddr, flagListenAddr, "Listen address (format: [:port])") flags.StringVar(&opts.advertiseAddr, flagAdvertiseAddr, "", "Advertised address (format: [:port])") + flags.StringVar(&opts.dataPathAddr, flagDataPathAddr, "", "Address or interface to use for data path traffic (format: )") flags.StringVar(&opts.token, flagToken, "", "Token for entry into the swarm") flags.StringVar(&opts.availability, flagAvailability, "active", `Availability of the node ("active"|"pause"|"drain")`) return cmd @@ -54,6 +56,7 @@ func runJoin(dockerCli command.Cli, flags *pflag.FlagSet, opts joinOptions) erro JoinToken: opts.token, ListenAddr: opts.listenAddr.String(), AdvertiseAddr: opts.advertiseAddr, + DataPathAddr: opts.dataPathAddr, RemoteAddrs: []string{opts.remote}, } if flags.Changed(flagAvailability) { diff --git a/cli/command/swarm/opts.go b/cli/command/swarm/opts.go index a28dd4aad4..d2097050af 100644 --- a/cli/command/swarm/opts.go +++ b/cli/command/swarm/opts.go @@ -21,6 +21,7 @@ const ( flagDispatcherHeartbeat = "dispatcher-heartbeat" flagListenAddr = "listen-addr" flagAdvertiseAddr = "advertise-addr" + flagDataPathAddr = "data-path-addr" flagQuiet = "quiet" flagRotate = "rotate" flagToken = "token" From 9ca78094b58d0e5228926c6e95034cb44ff5697b Mon Sep 17 00:00:00 2001 From: yupengzte Date: Thu, 27 Apr 2017 11:31:01 +0800 Subject: [PATCH 17/18] delete ineffectual assignment Signed-off-by: yupengzte --- cli/command/service/logs.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/cli/command/service/logs.go b/cli/command/service/logs.go index c4ac557021..5db7c42bc3 100644 --- a/cli/command/service/logs.go +++ b/cli/command/service/logs.go @@ -92,7 +92,15 @@ func runLogs(dockerCli *command.DockerCli, opts *logsOptions) error { if !client.IsErrServiceNotFound(err) { return err } - task, _, _ := cli.TaskInspectWithRaw(ctx, opts.target) + task, _, err := cli.TaskInspectWithRaw(ctx, opts.target) + if err != nil { + if client.IsErrTaskNotFound(err) { + // if the task isn't found, rewrite the error to be clear + // that we looked for services AND tasks and found none + err = fmt.Errorf("no such task or service") + } + return err + } tty = task.Spec.ContainerSpec.TTY // TODO(dperny) hot fix until we get a nice details system squared away, // ignores details (including task context) if we have a TTY log @@ -104,15 +112,10 @@ func runLogs(dockerCli *command.DockerCli, opts *logsOptions) error { responseBody, err = cli.TaskLogs(ctx, opts.target, options) if err != nil { - if client.IsErrTaskNotFound(err) { - // if the task ALSO isn't found, rewrite the error to be clear - // that we looked for services AND tasks - err = fmt.Errorf("No such task or service") - } return err } + maxLength = getMaxLength(task.Slot) - responseBody, _ = cli.TaskLogs(ctx, opts.target, options) } else { tty = service.Spec.TaskTemplate.ContainerSpec.TTY // TODO(dperny) hot fix until we get a nice details system squared away, From 779012af69f8e3f46bb42877440149c4ddfe54af Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Mon, 1 May 2017 14:54:56 -0400 Subject: [PATCH 18/18] Refacator pkg/streamformatter StreamFormatter suffered was two distinct structs mixed into a single struct without any overlap. Signed-off-by: Daniel Nephin --- cli/command/image/build.go | 2 +- cli/command/image/build/context.go | 2 +- cli/command/service/progress/progress.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/command/image/build.go b/cli/command/image/build.go index 5d4c2557c7..5ab3829bea 100644 --- a/cli/command/image/build.go +++ b/cli/command/image/build.go @@ -254,7 +254,7 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error { } // Setup an upload progress bar - progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(progBuff, true) + progressOutput := streamformatter.NewProgressOutput(progBuff) if !dockerCli.Out().IsTerminal() { progressOutput = &lastProgressOutput{output: progressOutput} } diff --git a/cli/command/image/build/context.go b/cli/command/image/build/context.go index da8cf11e9e..e6165aa975 100644 --- a/cli/command/image/build/context.go +++ b/cli/command/image/build/context.go @@ -165,7 +165,7 @@ func GetContextFromURL(out io.Writer, remoteURL, dockerfileName string) (io.Read if err != nil { return nil, "", errors.Errorf("unable to download remote context %s: %v", remoteURL, err) } - progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(out, true) + progressOutput := streamformatter.NewProgressOutput(out) // Pass the response body through a progress reader. progReader := progress.NewProgressReader(response.Body, progressOutput, response.ContentLength, "", fmt.Sprintf("Downloading build context from remote url: %s", remoteURL)) diff --git a/cli/command/service/progress/progress.go b/cli/command/service/progress/progress.go index 61c040df5e..266a949814 100644 --- a/cli/command/service/progress/progress.go +++ b/cli/command/service/progress/progress.go @@ -63,7 +63,7 @@ func stateToProgress(state swarm.TaskState, rollback bool) int64 { func ServiceProgress(ctx context.Context, client client.APIClient, serviceID string, progressWriter io.WriteCloser) error { defer progressWriter.Close() - progressOut := streamformatter.NewJSONStreamFormatter().NewProgressOutput(progressWriter, false) + progressOut := streamformatter.NewJSONProgressOutput(progressWriter, false) sigint := make(chan os.Signal, 1) signal.Notify(sigint, os.Interrupt)