From dea809813ecddab1cbca25210b2c1fa1ccc279bc Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Mon, 10 Apr 2017 13:11:58 +0200 Subject: [PATCH 01/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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/21] 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) From 03a36f9d50365c37dfee4c8110636ed31f1f5ec1 Mon Sep 17 00:00:00 2001 From: Tibor Vass Date: Wed, 3 May 2017 19:26:45 -0700 Subject: [PATCH 19/21] Fix expected output in test due to linter-induced change on error string Signed-off-by: Tibor Vass --- cli/command/image/save_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/command/image/save_test.go b/cli/command/image/save_test.go index df34b3591d..d137dc92f9 100644 --- a/cli/command/image/save_test.go +++ b/cli/command/image/save_test.go @@ -32,7 +32,7 @@ func TestNewSaveCommandErrors(t *testing.T) { 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.", + expectedError: "cowardly refusing to save to a terminal. Use the -o flag or redirect", }, { name: "ImageSave fail", From b141fa3799d3b52a16647dedf7b22dd36b528e55 Mon Sep 17 00:00:00 2001 From: Tibor Vass Date: Wed, 3 May 2017 19:27:10 -0700 Subject: [PATCH 20/21] update vendor.conf Signed-off-by: Tibor Vass --- vendor.conf | 5 +- vendor/github.com/docker/docker/README.md | 316 +----- vendor/github.com/docker/docker/api/README.md | 4 +- .../docker/api/types/container/config.go | 6 + .../docker/api/types/container/host_config.go | 3 - .../docker/docker/api/types/swarm/swarm.go | 6 + .../docker/docker/api/types/types.go | 2 + .../pkg/streamformatter/streamformatter.go | 174 ++-- .../pkg/streamformatter/streamwriter.go | 47 + .../docker/docker/pkg/testutil/cmd/command.go | 307 ------ .../docker/docker/pkg/testutil/utils.go | 15 - vendor/github.com/docker/docker/vendor.conf | 13 +- .../github.com/docker/docker/volume/volume.go | 12 +- vendor/github.com/go-check/check/LICENSE | 25 - vendor/github.com/go-check/check/README.md | 10 - vendor/github.com/go-check/check/benchmark.go | 187 ---- vendor/github.com/go-check/check/check.go | 939 ------------------ vendor/github.com/go-check/check/checkers.go | 458 --------- vendor/github.com/go-check/check/helpers.go | 231 ----- vendor/github.com/go-check/check/printer.go | 168 ---- vendor/github.com/go-check/check/reporter.go | 88 -- vendor/github.com/go-check/check/run.go | 183 ---- .../github.com/opencontainers/selinux/LICENSE | 201 ++++ .../opencontainers/selinux/README.md | 7 + .../go-selinux}/label/label.go | 2 +- .../go-selinux}/label/label_selinux.go | 48 +- .../selinux => selinux/go-selinux}/selinux.go | 266 +++-- .../selinux/go-selinux/xattrs.go | 78 ++ 28 files changed, 685 insertions(+), 3116 deletions(-) create mode 100644 vendor/github.com/docker/docker/pkg/streamformatter/streamwriter.go delete mode 100644 vendor/github.com/docker/docker/pkg/testutil/cmd/command.go delete mode 100644 vendor/github.com/go-check/check/LICENSE delete mode 100644 vendor/github.com/go-check/check/README.md delete mode 100644 vendor/github.com/go-check/check/benchmark.go delete mode 100644 vendor/github.com/go-check/check/check.go delete mode 100644 vendor/github.com/go-check/check/checkers.go delete mode 100644 vendor/github.com/go-check/check/helpers.go delete mode 100644 vendor/github.com/go-check/check/printer.go delete mode 100644 vendor/github.com/go-check/check/reporter.go delete mode 100644 vendor/github.com/go-check/check/run.go create mode 100644 vendor/github.com/opencontainers/selinux/LICENSE create mode 100644 vendor/github.com/opencontainers/selinux/README.md rename vendor/github.com/opencontainers/{runc/libcontainer => selinux/go-selinux}/label/label.go (97%) rename vendor/github.com/opencontainers/{runc/libcontainer => selinux/go-selinux}/label/label_selinux.go (82%) rename vendor/github.com/opencontainers/{runc/libcontainer/selinux => selinux/go-selinux}/selinux.go (60%) create mode 100644 vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go diff --git a/vendor.conf b/vendor.conf index c853de045c..b478cd8023 100644 --- a/vendor.conf +++ b/vendor.conf @@ -6,7 +6,7 @@ github.com/agl/ed25519 d2b94fd789ea21d12fac1a4443dd3a3f79cda72c github.com/coreos/etcd 824277cb3a577a0e8c829ca9ec557b973fe06d20 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621 -github.com/docker/docker f33f2578881af36dc403a2063a00b19a4844fcbe +github.com/docker/docker bf5cf84534f1eac9805c6f1bffff341358806b57 github.com/docker/docker-credential-helpers v0.5.0 github.com/docker/go d30aec9fd63c35133f8f79c3412ad91a3b08be06 github.com/docker/go-connections e15c02316c12de00874640cd76311849de2aeed5 @@ -17,7 +17,6 @@ github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a github.com/docker/notary v0.4.2 github.com/docker/swarmkit b19d028de0a6e9ca281afeb76cea2544b9edd839 github.com/flynn-archive/go-shlex 3f9db97f856818214da2e1057f8ad84803971cff -github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git github.com/gogo/protobuf 7efa791bd276fd4db00867cbd982b552627c24cb github.com/golang/protobuf 8ee79997227bf9b34611aee7946ae64735e6fd93 github.com/gorilla/context v1.1 @@ -28,6 +27,7 @@ github.com/miekg/pkcs11 df8ae6ca730422dba20c768ff38ef7d79077a59f github.com/mitchellh/mapstructure f3009df150dadf309fdee4a54ed65c124afad715 github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb github.com/opencontainers/runc 9c2d8d184e5da67c95d601382adf14862e4f2228 https://github.com/docker/runc.git +github.com/opencontainers/selinux ba1aefe8057f1d0cfb8e88d0ec1dc85925ef987d github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9 github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2 github.com/spf13/cobra v1.5.1 https://github.com/dnephin/cobra.git @@ -43,3 +43,4 @@ golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756 golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb google.golang.org/grpc v1.0.4 gopkg.in/yaml.v2 4c78c975fe7c825c6d1466c42be594d1d6f3aba6 + diff --git a/vendor/github.com/docker/docker/README.md b/vendor/github.com/docker/docker/README.md index 2e91559c59..f768ff8449 100644 --- a/vendor/github.com/docker/docker/README.md +++ b/vendor/github.com/docker/docker/README.md @@ -1,270 +1,80 @@ -Docker: the container engine [![Release](https://img.shields.io/github/release/docker/docker.svg)](https://github.com/docker/docker/releases/latest) -============================ +### Docker users, see [Moby and Docker](https://mobyproject.org/#moby-and-docker) to clarify the relationship between the projects -Docker is an open source project to pack, ship and run any application -as a lightweight container. +### Docker maintainers and contributors, see [Transitioning to Moby](#transitioning-to-moby) for more details -Docker containers are both *hardware-agnostic* and *platform-agnostic*. -This means they can run anywhere, from your laptop to the largest -cloud compute instance and everything in between - and they don't require -you to use a particular language, framework or packaging system. That -makes them great building blocks for deploying and scaling web apps, -databases, and backend services without depending on a particular stack -or provider. +The Moby Project +================ -Docker began as an open-source implementation of the deployment engine which -powered [dotCloud](http://web.archive.org/web/20130530031104/https://www.dotcloud.com/), -a popular Platform-as-a-Service. It benefits directly from the experience -accumulated over several years of large-scale operation and support of hundreds -of thousands of applications and databases. +![Moby Project logo](docs/static_files/moby-project-logo.png "The Moby Project") -![Docker logo](docs/static_files/docker-logo-compressed.png "Docker") +Moby is an open-source project created by Docker to advance the software containerization movement. +It provides a “Lego set” of dozens of components, the framework for assembling them into custom container-based systems, and a place for all container enthusiasts to experiment and exchange ideas. -## Security Disclosure +# Moby -Security is very important to us. If you have any issue regarding security, -please disclose the information responsibly by sending an email to -security@docker.com and not by creating a GitHub issue. +## Overview -## Better than VMs +At the core of Moby is a framework to assemble specialized container systems. +It provides: -A common method for distributing applications and sandboxing their -execution is to use virtual machines, or VMs. Typical VM formats are -VMware's vmdk, Oracle VirtualBox's vdi, and Amazon EC2's ami. In theory -these formats should allow every developer to automatically package -their application into a "machine" for easy distribution and deployment. -In practice, that almost never happens, for a few reasons: +- A library of containerized components for all vital aspects of a container system: OS, container runtime, orchestration, infrastructure management, networking, storage, security, build, image distribution, etc. +- Tools to assemble the components into runnable artifacts for a variety of platforms and architectures: bare metal (both x86 and Arm); executables for Linux, Mac and Windows; VM images for popular cloud and virtualization providers. +- A set of reference assemblies which can be used as-is, modified, or used as inspiration to create your own. - * *Size*: VMs are very large which makes them impractical to store - and transfer. - * *Performance*: running VMs consumes significant CPU and memory, - which makes them impractical in many scenarios, for example local - development of multi-tier applications, and large-scale deployment - of cpu and memory-intensive applications on large numbers of - machines. - * *Portability*: competing VM environments don't play well with each - other. Although conversion tools do exist, they are limited and - add even more overhead. - * *Hardware-centric*: VMs were designed with machine operators in - mind, not software developers. As a result, they offer very - limited tooling for what developers need most: building, testing - and running their software. For example, VMs offer no facilities - for application versioning, monitoring, configuration, logging or - service discovery. +All Moby components are containers, so creating new components is as easy as building a new OCI-compatible container. -By contrast, Docker relies on a different sandboxing method known as -*containerization*. Unlike traditional virtualization, containerization -takes place at the kernel level. Most modern operating system kernels -now support the primitives necessary for containerization, including -Linux with [openvz](https://openvz.org), -[vserver](http://linux-vserver.org) and more recently -[lxc](https://linuxcontainers.org/), Solaris with -[zones](https://docs.oracle.com/cd/E26502_01/html/E29024/preface-1.html#scrolltoc), -and FreeBSD with -[Jails](https://www.freebsd.org/doc/handbook/jails.html). +## Principles -Docker builds on top of these low-level primitives to offer developers a -portable format and runtime environment that solves all four problems. -Docker containers are small (and their transfer can be optimized with -layers), they have basically zero memory and cpu overhead, they are -completely portable, and are designed from the ground up with an -application-centric design. +Moby is an open project guided by strong principles, but modular, flexible and without too strong an opinion on user experience, so it is open to the community to help set its direction. +The guiding principles are: -Perhaps best of all, because Docker operates at the OS level, it can still be -run inside a VM! +- Batteries included but swappable: Moby includes enough components to build fully featured container system, but its modular architecture ensures that most of the components can be swapped by different implementations. +- Usable security: Moby will provide secure defaults without compromising usability. +- Container centric: Moby is built with containers, for running containers. -## Plays well with others +With Moby, you should be able to describe all the components of your distributed application, from the high-level configuration files down to the kernel you would like to use and build and deploy it easily. -Docker does not require you to buy into a particular programming -language, framework, packaging system, or configuration language. +Moby uses [containerd](https://github.com/containerd/containerd) as the default container runtime. -Is your application a Unix process? Does it use files, tcp connections, -environment variables, standard Unix streams and command-line arguments -as inputs and outputs? Then Docker can run it. +## Audience -Can your application's build be expressed as a sequence of such -commands? Then Docker can build it. +Moby is recommended for anyone who wants to assemble a container-based system. This includes: -## Escape dependency hell +- Hackers who want to customize or patch their Docker build +- System engineers or integrators building a container system +- Infrastructure providers looking to adapt existing container systems to their environment +- Container enthusiasts who want to experiment with the latest container tech +- Open-source developers looking to test their project in a variety of different systems +- Anyone curious about Docker internals and how it’s built -A common problem for developers is the difficulty of managing all -their application's dependencies in a simple and automated way. +Moby is NOT recommended for: -This is usually difficult for several reasons: +- Application developers looking for an easy way to run their applications in containers. We recommend Docker CE instead. +- Enterprise IT and development teams looking for a ready-to-use, commercially supported container platform. We recommend Docker EE instead. +- Anyone curious about containers and looking for an easy way to learn. We recommend the docker.com website instead. - * *Cross-platform dependencies*. Modern applications often depend on - a combination of system libraries and binaries, language-specific - packages, framework-specific modules, internal components - developed for another project, etc. These dependencies live in - different "worlds" and require different tools - these tools - typically don't work well with each other, requiring awkward - custom integrations. +# Transitioning to Moby - * *Conflicting dependencies*. Different applications may depend on - different versions of the same dependency. Packaging tools handle - these situations with various degrees of ease - but they all - handle them in different and incompatible ways, which again forces - the developer to do extra work. +Docker is transitioning all of its open source collaborations to the Moby project going forward. +During the transition, all open source activity should continue as usual. - * *Custom dependencies*. A developer may need to prepare a custom - version of their application's dependency. Some packaging systems - can handle custom versions of a dependency, others can't - and all - of them handle it differently. +We are proposing the following list of changes: +- splitting up the engine into more open components +- removing the docker UI, SDK etc to keep them in the Docker org +- clarifying that the project is not limited to the engine, but to the assembly of all the individual components of the Docker platform +- open-source new tools & components which we currently use to assemble the Docker product, but could benefit the community +- defining an open, community-centric governance inspired by the Fedora project (a very successful example of balancing the needs of the community with the constraints of the primary corporate sponsor) -Docker solves the problem of dependency hell by giving developers a simple -way to express *all* their application's dependencies in one place, while -streamlining the process of assembling them. If this makes you think of -[XKCD 927](https://xkcd.com/927/), don't worry. Docker doesn't -*replace* your favorite packaging systems. It simply orchestrates -their use in a simple and repeatable way. How does it do that? With -layers. +----- -Docker defines a build as running a sequence of Unix commands, one -after the other, in the same container. Build commands modify the -contents of the container (usually by installing new files on the -filesystem), the next command modifies it some more, etc. Since each -build command inherits the result of the previous commands, the -*order* in which the commands are executed expresses *dependencies*. - -Here's a typical Docker build process: - -```bash -FROM ubuntu:12.04 -RUN apt-get update && apt-get install -y python python-pip curl -RUN curl -sSL https://github.com/shykes/helloflask/archive/master.tar.gz | tar -xzv -RUN cd helloflask-master && pip install -r requirements.txt -``` - -Note that Docker doesn't care *how* dependencies are built - as long -as they can be built by running a Unix command in a container. - - -Getting started -=============== - -Docker can be installed either on your computer for building applications or -on servers for running them. To get started, [check out the installation -instructions in the -documentation](https://docs.docker.com/engine/installation/). - -Usage examples -============== - -Docker can be used to run short-lived commands, long-running daemons -(app servers, databases, etc.), interactive shell sessions, etc. - -You can find a [list of real-world -examples](https://docs.docker.com/engine/examples/) in the -documentation. - -Under the hood --------------- - -Under the hood, Docker is built on the following components: - -* The - [cgroups](https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt) - and - [namespaces](http://man7.org/linux/man-pages/man7/namespaces.7.html) - capabilities of the Linux kernel -* The [Go](https://golang.org) programming language -* The [Docker Image Specification](https://github.com/docker/docker/blob/master/image/spec/v1.md) -* The [Libcontainer Specification](https://github.com/opencontainers/runc/blob/master/libcontainer/SPEC.md) - -Contributing to Docker [![GoDoc](https://godoc.org/github.com/docker/docker?status.svg)](https://godoc.org/github.com/docker/docker) -====================== - -| **Master** (Linux) | **Experimental** (Linux) | **Windows** | **FreeBSD** | -|------------------|----------------------|---------|---------| -| [![Jenkins Build Status](https://jenkins.dockerproject.org/view/Docker/job/Docker%20Master/badge/icon)](https://jenkins.dockerproject.org/view/Docker/job/Docker%20Master/) | [![Jenkins Build Status](https://jenkins.dockerproject.org/view/Docker/job/Docker%20Master%20%28experimental%29/badge/icon)](https://jenkins.dockerproject.org/view/Docker/job/Docker%20Master%20%28experimental%29/) | [![Build Status](http://jenkins.dockerproject.org/job/Docker%20Master%20(windows)/badge/icon)](http://jenkins.dockerproject.org/job/Docker%20Master%20(windows)/) | [![Build Status](http://jenkins.dockerproject.org/job/Docker%20Master%20(freebsd)/badge/icon)](http://jenkins.dockerproject.org/job/Docker%20Master%20(freebsd)/) | - -Want to hack on Docker? Awesome! We have [instructions to help you get -started contributing code or documentation](https://docs.docker.com/opensource/project/who-written-for/). - -These instructions are probably not perfect, please let us know if anything -feels wrong or incomplete. Better yet, submit a PR and improve them yourself. - -Getting the development builds -============================== - -Want to run Docker from a master build? You can download -master builds at [master.dockerproject.org](https://master.dockerproject.org). -They are updated with each commit merged into the master branch. - -Don't know how to use that super cool new feature in the master build? Check -out the master docs at -[docs.master.dockerproject.org](http://docs.master.dockerproject.org). - -How the project is run -====================== - -Docker is a very, very active project. If you want to learn more about how it is run, -or want to get more involved, the best place to start is [the project directory](https://github.com/docker/docker/tree/master/project). - -We are always open to suggestions on process improvements, and are always looking for more maintainers. - -### Talking to other Docker users and contributors - - - - - - - - - - - - - - - - - - - - - - - - -
Internet Relay Chat (IRC) -

- IRC is a direct line to our most knowledgeable Docker users; we have - both the #docker and #docker-dev group on - irc.freenode.net. - IRC is a rich chat protocol but it can overwhelm new users. You can search - our chat archives. -

- Read our IRC quickstart guide for an easy way to get started. -
Docker Community Forums - The Docker Engine - group is for users of the Docker Engine project. -
Google Groups - The docker-dev group is for contributors and other people - contributing to the Docker project. You can join this group without a - Google account by sending an email to docker-dev+subscribe@googlegroups.com. - You'll receive a join-request message; simply reply to the message to - confirm your subscription. -
Twitter - You can follow Docker's Twitter feed - to get updates on our products. You can also tweet us questions or just - share blogs or stories. -
Stack Overflow - Stack Overflow has thousands of Docker questions listed. We regularly - monitor Docker questions - and so do many other knowledgeable Docker users. -
- -### Legal +Legal +===== *Brought to you courtesy of our legal counsel. For more context, -please see the [NOTICE](https://github.com/docker/docker/blob/master/NOTICE) document in this repo.* +please see the [NOTICE](https://github.com/moby/moby/blob/master/NOTICE) document in this repo.* -Use and transfer of Docker may be subject to certain restrictions by the +Use and transfer of Moby may be subject to certain restrictions by the United States and other governments. It is your responsibility to ensure that your use and/or transfer does not @@ -275,30 +85,6 @@ For more information, please see https://www.bis.doc.gov Licensing ========= -Docker is licensed under the Apache License, Version 2.0. See -[LICENSE](https://github.com/docker/docker/blob/master/LICENSE) for the full +Moby is licensed under the Apache License, Version 2.0. See +[LICENSE](https://github.com/moby/moby/blob/master/LICENSE) for the full license text. - -Other Docker Related Projects -============================= -There are a number of projects under development that are based on Docker's -core technology. These projects expand the tooling built around the -Docker platform to broaden its application and utility. - -* [Docker Registry](https://github.com/docker/distribution): Registry -server for Docker (hosting/delivery of repositories and images) -* [Docker Machine](https://github.com/docker/machine): Machine management -for a container-centric world -* [Docker Swarm](https://github.com/docker/swarm): A Docker-native clustering -system -* [Docker Compose](https://github.com/docker/compose) (formerly Fig): -Define and run multi-container apps -* [Kitematic](https://github.com/docker/kitematic): The easiest way to use -Docker on Mac and Windows - -If you know of another project underway that should be listed here, please help -us keep this list up-to-date by submitting a PR. - -Awesome-Docker -============== -You can find more projects, tools and articles related to Docker on the [awesome-docker list](https://github.com/veggiemonk/awesome-docker). Add your project there. diff --git a/vendor/github.com/docker/docker/api/README.md b/vendor/github.com/docker/docker/api/README.md index 8954ed0174..bb88132524 100644 --- a/vendor/github.com/docker/docker/api/README.md +++ b/vendor/github.com/docker/docker/api/README.md @@ -14,8 +14,8 @@ It consists of various components in this repository: The API is defined by the [Swagger](http://swagger.io/specification/) definition in `api/swagger.yaml`. This definition can be used to: -1. To automatically generate documentation. -2. To automatically generate the Go server and client. (A work-in-progress.) +1. Automatically generate documentation. +2. Automatically generate the Go server and client. (A work-in-progress.) 3. Provide a machine readable version of the API for introspecting what it can do, automatically generating clients for other languages, etc. ## Updating the API documentation diff --git a/vendor/github.com/docker/docker/api/types/container/config.go b/vendor/github.com/docker/docker/api/types/container/config.go index 02e1b87a78..55a03fc981 100644 --- a/vendor/github.com/docker/docker/api/types/container/config.go +++ b/vendor/github.com/docker/docker/api/types/container/config.go @@ -7,6 +7,12 @@ import ( "github.com/docker/go-connections/nat" ) +// MinimumDuration puts a minimum on user configured duration. +// This is to prevent API error on time unit. For example, API may +// set 3 as healthcheck interval with intention of 3 seconds, but +// Docker interprets it as 3 nanoseconds. +const MinimumDuration = 1 * time.Millisecond + // HealthConfig holds configuration settings for the HEALTHCHECK feature. type HealthConfig struct { // Test is the test to perform to check that the container is healthy. diff --git a/vendor/github.com/docker/docker/api/types/container/host_config.go b/vendor/github.com/docker/docker/api/types/container/host_config.go index c8f72f8c89..9fea9eb04b 100644 --- a/vendor/github.com/docker/docker/api/types/container/host_config.go +++ b/vendor/github.com/docker/docker/api/types/container/host_config.go @@ -377,7 +377,4 @@ type HostConfig struct { // Run a custom init inside the container, if null, use the daemon's configured settings Init *bool `json:",omitempty"` - - // Custom init path - InitPath string `json:",omitempty"` } diff --git a/vendor/github.com/docker/docker/api/types/swarm/swarm.go b/vendor/github.com/docker/docker/api/types/swarm/swarm.go index c513274750..9fc5c30961 100644 --- a/vendor/github.com/docker/docker/api/types/swarm/swarm.go +++ b/vendor/github.com/docker/docker/api/types/swarm/swarm.go @@ -126,12 +126,17 @@ type ExternalCA struct { // Options is a set of additional key/value pairs whose interpretation // depends on the specified CA type. Options map[string]string `json:",omitempty"` + + // CACert specifies which root CA is used by this external CA. This certificate must + // be in PEM format. + CACert string } // InitRequest is the request used to init a swarm. type InitRequest struct { ListenAddr string AdvertiseAddr string + DataPathAddr string ForceNewCluster bool Spec Spec AutoLockManagers bool @@ -142,6 +147,7 @@ type InitRequest struct { type JoinRequest struct { ListenAddr string AdvertiseAddr string + DataPathAddr string RemoteAddrs []string JoinToken string // accept by secret Availability NodeAvailability diff --git a/vendor/github.com/docker/docker/api/types/types.go b/vendor/github.com/docker/docker/api/types/types.go index bbaf2c5531..9493bd95e2 100644 --- a/vendor/github.com/docker/docker/api/types/types.go +++ b/vendor/github.com/docker/docker/api/types/types.go @@ -238,6 +238,8 @@ type PluginsInfo struct { Network []string // List of Authorization plugins registered Authorization []string + // List of Log plugins registered + Log []string } // ExecStartCheck is a temp struct used by execStart diff --git a/vendor/github.com/docker/docker/pkg/streamformatter/streamformatter.go b/vendor/github.com/docker/docker/pkg/streamformatter/streamformatter.go index f2868441ee..fa79828172 100644 --- a/vendor/github.com/docker/docker/pkg/streamformatter/streamformatter.go +++ b/vendor/github.com/docker/docker/pkg/streamformatter/streamformatter.go @@ -10,91 +10,76 @@ import ( "github.com/docker/docker/pkg/progress" ) -// StreamFormatter formats a stream, optionally using JSON. -type StreamFormatter struct { - json bool -} - -// NewStreamFormatter returns a simple StreamFormatter -func NewStreamFormatter() *StreamFormatter { - return &StreamFormatter{} -} - -// NewJSONStreamFormatter returns a StreamFormatter configured to stream json -func NewJSONStreamFormatter() *StreamFormatter { - return &StreamFormatter{true} -} - const streamNewline = "\r\n" -var streamNewlineBytes = []byte(streamNewline) +type jsonProgressFormatter struct{} -// FormatStream formats the specified stream. -func (sf *StreamFormatter) FormatStream(str string) []byte { - if sf.json { - b, err := json.Marshal(&jsonmessage.JSONMessage{Stream: str}) - if err != nil { - return sf.FormatError(err) - } - return append(b, streamNewlineBytes...) - } - return []byte(str + "\r") +func appendNewline(source []byte) []byte { + return append(source, []byte(streamNewline)...) } // FormatStatus formats the specified objects according to the specified format (and id). -func (sf *StreamFormatter) FormatStatus(id, format string, a ...interface{}) []byte { +func FormatStatus(id, format string, a ...interface{}) []byte { str := fmt.Sprintf(format, a...) - if sf.json { - b, err := json.Marshal(&jsonmessage.JSONMessage{ID: id, Status: str}) - if err != nil { - return sf.FormatError(err) - } - return append(b, streamNewlineBytes...) + b, err := json.Marshal(&jsonmessage.JSONMessage{ID: id, Status: str}) + if err != nil { + return FormatError(err) } - return []byte(str + streamNewline) + return appendNewline(b) } -// FormatError formats the specified error. -func (sf *StreamFormatter) FormatError(err error) []byte { - if sf.json { - jsonError, ok := err.(*jsonmessage.JSONError) - if !ok { - jsonError = &jsonmessage.JSONError{Message: err.Error()} - } - if b, err := json.Marshal(&jsonmessage.JSONMessage{Error: jsonError, ErrorMessage: err.Error()}); err == nil { - return append(b, streamNewlineBytes...) - } - return []byte("{\"error\":\"format error\"}" + streamNewline) +// FormatError formats the error as a JSON object +func FormatError(err error) []byte { + jsonError, ok := err.(*jsonmessage.JSONError) + if !ok { + jsonError = &jsonmessage.JSONError{Message: err.Error()} } - return []byte("Error: " + err.Error() + streamNewline) + if b, err := json.Marshal(&jsonmessage.JSONMessage{Error: jsonError, ErrorMessage: err.Error()}); err == nil { + return appendNewline(b) + } + return []byte(`{"error":"format error"}` + streamNewline) } -// FormatProgress formats the progress information for a specified action. -func (sf *StreamFormatter) FormatProgress(id, action string, progress *jsonmessage.JSONProgress, aux interface{}) []byte { +func (sf *jsonProgressFormatter) formatStatus(id, format string, a ...interface{}) []byte { + return FormatStatus(id, format, a...) +} + +// formatProgress formats the progress information for a specified action. +func (sf *jsonProgressFormatter) formatProgress(id, action string, progress *jsonmessage.JSONProgress, aux interface{}) []byte { if progress == nil { progress = &jsonmessage.JSONProgress{} } - if sf.json { - var auxJSON *json.RawMessage - if aux != nil { - auxJSONBytes, err := json.Marshal(aux) - if err != nil { - return nil - } - auxJSON = new(json.RawMessage) - *auxJSON = auxJSONBytes - } - b, err := json.Marshal(&jsonmessage.JSONMessage{ - Status: action, - ProgressMessage: progress.String(), - Progress: progress, - ID: id, - Aux: auxJSON, - }) + var auxJSON *json.RawMessage + if aux != nil { + auxJSONBytes, err := json.Marshal(aux) if err != nil { return nil } - return append(b, streamNewlineBytes...) + auxJSON = new(json.RawMessage) + *auxJSON = auxJSONBytes + } + b, err := json.Marshal(&jsonmessage.JSONMessage{ + Status: action, + ProgressMessage: progress.String(), + Progress: progress, + ID: id, + Aux: auxJSON, + }) + if err != nil { + return nil + } + return appendNewline(b) +} + +type rawProgressFormatter struct{} + +func (sf *rawProgressFormatter) formatStatus(id, format string, a ...interface{}) []byte { + return []byte(fmt.Sprintf(format, a...) + streamNewline) +} + +func (sf *rawProgressFormatter) formatProgress(id, action string, progress *jsonmessage.JSONProgress, aux interface{}) []byte { + if progress == nil { + progress = &jsonmessage.JSONProgress{} } endl := "\r" if progress.String() == "" { @@ -105,16 +90,23 @@ func (sf *StreamFormatter) FormatProgress(id, action string, progress *jsonmessa // NewProgressOutput returns a progress.Output object that can be passed to // progress.NewProgressReader. -func (sf *StreamFormatter) NewProgressOutput(out io.Writer, newLines bool) progress.Output { - return &progressOutput{ - sf: sf, - out: out, - newLines: newLines, - } +func NewProgressOutput(out io.Writer) progress.Output { + return &progressOutput{sf: &rawProgressFormatter{}, out: out, newLines: true} +} + +// NewJSONProgressOutput returns a progress.Output that that formats output +// using JSON objects +func NewJSONProgressOutput(out io.Writer, newLines bool) progress.Output { + return &progressOutput{sf: &jsonProgressFormatter{}, out: out, newLines: newLines} +} + +type formatProgress interface { + formatStatus(id, format string, a ...interface{}) []byte + formatProgress(id, action string, progress *jsonmessage.JSONProgress, aux interface{}) []byte } type progressOutput struct { - sf *StreamFormatter + sf formatProgress out io.Writer newLines bool } @@ -123,10 +115,10 @@ type progressOutput struct { func (out *progressOutput) WriteProgress(prog progress.Progress) error { var formatted []byte if prog.Message != "" { - formatted = out.sf.FormatStatus(prog.ID, prog.Message) + formatted = out.sf.formatStatus(prog.ID, prog.Message) } else { jsonProgress := jsonmessage.JSONProgress{Current: prog.Current, Total: prog.Total, HideCounts: prog.HideCounts} - formatted = out.sf.FormatProgress(prog.ID, prog.Action, &jsonProgress, prog.Aux) + formatted = out.sf.formatProgress(prog.ID, prog.Action, &jsonProgress, prog.Aux) } _, err := out.out.Write(formatted) if err != nil { @@ -134,39 +126,9 @@ func (out *progressOutput) WriteProgress(prog progress.Progress) error { } if out.newLines && prog.LastUpdate { - _, err = out.out.Write(out.sf.FormatStatus("", "")) + _, err = out.out.Write(out.sf.formatStatus("", "")) return err } return nil } - -// StdoutFormatter is a streamFormatter that writes to the standard output. -type StdoutFormatter struct { - io.Writer - *StreamFormatter -} - -func (sf *StdoutFormatter) Write(buf []byte) (int, error) { - formattedBuf := sf.StreamFormatter.FormatStream(string(buf)) - n, err := sf.Writer.Write(formattedBuf) - if n != len(formattedBuf) { - return n, io.ErrShortWrite - } - return len(buf), err -} - -// StderrFormatter is a streamFormatter that writes to the standard error. -type StderrFormatter struct { - io.Writer - *StreamFormatter -} - -func (sf *StderrFormatter) Write(buf []byte) (int, error) { - formattedBuf := sf.StreamFormatter.FormatStream("\033[91m" + string(buf) + "\033[0m") - n, err := sf.Writer.Write(formattedBuf) - if n != len(formattedBuf) { - return n, io.ErrShortWrite - } - return len(buf), err -} diff --git a/vendor/github.com/docker/docker/pkg/streamformatter/streamwriter.go b/vendor/github.com/docker/docker/pkg/streamformatter/streamwriter.go new file mode 100644 index 0000000000..141d12e20e --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/streamformatter/streamwriter.go @@ -0,0 +1,47 @@ +package streamformatter + +import ( + "encoding/json" + "io" + + "github.com/docker/docker/pkg/jsonmessage" +) + +type streamWriter struct { + io.Writer + lineFormat func([]byte) string +} + +func (sw *streamWriter) Write(buf []byte) (int, error) { + formattedBuf := sw.format(buf) + n, err := sw.Writer.Write(formattedBuf) + if n != len(formattedBuf) { + return n, io.ErrShortWrite + } + return len(buf), err +} + +func (sw *streamWriter) format(buf []byte) []byte { + msg := &jsonmessage.JSONMessage{Stream: sw.lineFormat(buf)} + b, err := json.Marshal(msg) + if err != nil { + return FormatError(err) + } + return appendNewline(b) +} + +// NewStdoutWriter returns a writer which formats the output as json message +// representing stdout lines +func NewStdoutWriter(out io.Writer) io.Writer { + return &streamWriter{Writer: out, lineFormat: func(buf []byte) string { + return string(buf) + }} +} + +// NewStderrWriter returns a writer which formats the output as json message +// representing stderr lines +func NewStderrWriter(out io.Writer) io.Writer { + return &streamWriter{Writer: out, lineFormat: func(buf []byte) string { + return "\033[91m" + string(buf) + "\033[0m" + }} +} diff --git a/vendor/github.com/docker/docker/pkg/testutil/cmd/command.go b/vendor/github.com/docker/docker/pkg/testutil/cmd/command.go deleted file mode 100644 index 36aba593b2..0000000000 --- a/vendor/github.com/docker/docker/pkg/testutil/cmd/command.go +++ /dev/null @@ -1,307 +0,0 @@ -package cmd - -import ( - "bytes" - "fmt" - "io" - "os/exec" - "path/filepath" - "runtime" - "strings" - "sync" - "time" - - "github.com/docker/docker/pkg/system" - "github.com/go-check/check" -) - -type testingT interface { - Fatalf(string, ...interface{}) -} - -const ( - // None is a token to inform Result.Assert that the output should be empty - None string = "" -) - -type lockedBuffer struct { - m sync.RWMutex - buf bytes.Buffer -} - -func (buf *lockedBuffer) Write(b []byte) (int, error) { - buf.m.Lock() - defer buf.m.Unlock() - return buf.buf.Write(b) -} - -func (buf *lockedBuffer) String() string { - buf.m.RLock() - defer buf.m.RUnlock() - return buf.buf.String() -} - -// Result stores the result of running a command -type Result struct { - Cmd *exec.Cmd - ExitCode int - Error error - // Timeout is true if the command was killed because it ran for too long - Timeout bool - outBuffer *lockedBuffer - errBuffer *lockedBuffer -} - -// Assert compares the Result against the Expected struct, and fails the test if -// any of the expcetations are not met. -func (r *Result) Assert(t testingT, exp Expected) *Result { - err := r.Compare(exp) - if err == nil { - return r - } - _, file, line, ok := runtime.Caller(1) - if ok { - t.Fatalf("at %s:%d - %s", filepath.Base(file), line, err.Error()) - } else { - t.Fatalf("(no file/line info) - %s", err.Error()) - } - return nil -} - -// Compare returns a formatted error with the command, stdout, stderr, exit -// code, and any failed expectations -func (r *Result) Compare(exp Expected) error { - errors := []string{} - add := func(format string, args ...interface{}) { - errors = append(errors, fmt.Sprintf(format, args...)) - } - - if exp.ExitCode != r.ExitCode { - add("ExitCode was %d expected %d", r.ExitCode, exp.ExitCode) - } - if exp.Timeout != r.Timeout { - if exp.Timeout { - add("Expected command to timeout") - } else { - add("Expected command to finish, but it hit the timeout") - } - } - if !matchOutput(exp.Out, r.Stdout()) { - add("Expected stdout to contain %q", exp.Out) - } - if !matchOutput(exp.Err, r.Stderr()) { - add("Expected stderr to contain %q", exp.Err) - } - switch { - // If a non-zero exit code is expected there is going to be an error. - // Don't require an error message as well as an exit code because the - // error message is going to be "exit status which is not useful - case exp.Error == "" && exp.ExitCode != 0: - case exp.Error == "" && r.Error != nil: - add("Expected no error") - case exp.Error != "" && r.Error == nil: - add("Expected error to contain %q, but there was no error", exp.Error) - case exp.Error != "" && !strings.Contains(r.Error.Error(), exp.Error): - add("Expected error to contain %q", exp.Error) - } - - if len(errors) == 0 { - return nil - } - return fmt.Errorf("%s\nFailures:\n%s\n", r, strings.Join(errors, "\n")) -} - -func matchOutput(expected string, actual string) bool { - switch expected { - case None: - return actual == "" - default: - return strings.Contains(actual, expected) - } -} - -func (r *Result) String() string { - var timeout string - if r.Timeout { - timeout = " (timeout)" - } - - return fmt.Sprintf(` -Command: %s -ExitCode: %d%s -Error: %v -Stdout: %v -Stderr: %v -`, - strings.Join(r.Cmd.Args, " "), - r.ExitCode, - timeout, - r.Error, - r.Stdout(), - r.Stderr()) -} - -// Expected is the expected output from a Command. This struct is compared to a -// Result struct by Result.Assert(). -type Expected struct { - ExitCode int - Timeout bool - Error string - Out string - Err string -} - -// Success is the default expected result -var Success = Expected{} - -// Stdout returns the stdout of the process as a string -func (r *Result) Stdout() string { - return r.outBuffer.String() -} - -// Stderr returns the stderr of the process as a string -func (r *Result) Stderr() string { - return r.errBuffer.String() -} - -// Combined returns the stdout and stderr combined into a single string -func (r *Result) Combined() string { - return r.outBuffer.String() + r.errBuffer.String() -} - -// SetExitError sets Error and ExitCode based on Error -func (r *Result) SetExitError(err error) { - if err == nil { - return - } - r.Error = err - r.ExitCode = system.ProcessExitCode(err) -} - -type matches struct{} - -// Info returns the CheckerInfo -func (m *matches) Info() *check.CheckerInfo { - return &check.CheckerInfo{ - Name: "CommandMatches", - Params: []string{"result", "expected"}, - } -} - -// Check compares a result against the expected -func (m *matches) Check(params []interface{}, names []string) (bool, string) { - result, ok := params[0].(*Result) - if !ok { - return false, fmt.Sprintf("result must be a *Result, not %T", params[0]) - } - expected, ok := params[1].(Expected) - if !ok { - return false, fmt.Sprintf("expected must be an Expected, not %T", params[1]) - } - - err := result.Compare(expected) - if err == nil { - return true, "" - } - return false, err.Error() -} - -// Matches is a gocheck.Checker for comparing a Result against an Expected -var Matches = &matches{} - -// Cmd contains the arguments and options for a process to run as part of a test -// suite. -type Cmd struct { - Command []string - Timeout time.Duration - Stdin io.Reader - Stdout io.Writer - Dir string - Env []string -} - -// Command create a simple Cmd with the specified command and arguments -func Command(command string, args ...string) Cmd { - return Cmd{Command: append([]string{command}, args...)} -} - -// RunCmd runs a command and returns a Result -func RunCmd(cmd Cmd, cmdOperators ...func(*Cmd)) *Result { - for _, op := range cmdOperators { - op(&cmd) - } - result := StartCmd(cmd) - if result.Error != nil { - return result - } - return WaitOnCmd(cmd.Timeout, result) -} - -// RunCommand parses a command line and runs it, returning a result -func RunCommand(command string, args ...string) *Result { - return RunCmd(Command(command, args...)) -} - -// StartCmd starts a command, but doesn't wait for it to finish -func StartCmd(cmd Cmd) *Result { - result := buildCmd(cmd) - if result.Error != nil { - return result - } - result.SetExitError(result.Cmd.Start()) - return result -} - -func buildCmd(cmd Cmd) *Result { - var execCmd *exec.Cmd - switch len(cmd.Command) { - case 1: - execCmd = exec.Command(cmd.Command[0]) - default: - execCmd = exec.Command(cmd.Command[0], cmd.Command[1:]...) - } - outBuffer := new(lockedBuffer) - errBuffer := new(lockedBuffer) - - execCmd.Stdin = cmd.Stdin - execCmd.Dir = cmd.Dir - execCmd.Env = cmd.Env - if cmd.Stdout != nil { - execCmd.Stdout = io.MultiWriter(outBuffer, cmd.Stdout) - } else { - execCmd.Stdout = outBuffer - } - execCmd.Stderr = errBuffer - return &Result{ - Cmd: execCmd, - outBuffer: outBuffer, - errBuffer: errBuffer, - } -} - -// WaitOnCmd waits for a command to complete. If timeout is non-nil then -// only wait until the timeout. -func WaitOnCmd(timeout time.Duration, result *Result) *Result { - if timeout == time.Duration(0) { - result.SetExitError(result.Cmd.Wait()) - return result - } - - done := make(chan error, 1) - // Wait for command to exit in a goroutine - go func() { - done <- result.Cmd.Wait() - }() - - select { - case <-time.After(timeout): - killErr := result.Cmd.Process.Kill() - if killErr != nil { - fmt.Printf("failed to kill (pid=%d): %v\n", result.Cmd.Process.Pid, killErr) - } - result.Timeout = true - case err := <-done: - result.SetExitError(err) - } - return result -} diff --git a/vendor/github.com/docker/docker/pkg/testutil/utils.go b/vendor/github.com/docker/docker/pkg/testutil/utils.go index 194675385f..0522dde2b1 100644 --- a/vendor/github.com/docker/docker/pkg/testutil/utils.go +++ b/vendor/github.com/docker/docker/pkg/testutil/utils.go @@ -16,7 +16,6 @@ import ( "github.com/docker/docker/pkg/stringutils" "github.com/docker/docker/pkg/system" - icmd "github.com/docker/docker/pkg/testutil/cmd" ) // IsKilled process the specified error and returns whether the process was killed or not. @@ -212,20 +211,6 @@ func (c *ChannelBuffer) ReadTimeout(p []byte, n time.Duration) (int, error) { } } -// RunAtDifferentDate runs the specified function with the given time. -// It changes the date of the system, which can led to weird behaviors. -func RunAtDifferentDate(date time.Time, block func()) { - // Layout for date. MMDDhhmmYYYY - const timeLayout = "010203042006" - // Ensure we bring time back to now - now := time.Now().Format(timeLayout) - defer icmd.RunCommand("date", now) - - icmd.RunCommand("date", date.Format(timeLayout)) - block() - return -} - // ReadBody read the specified ReadCloser content and returns it func ReadBody(b io.ReadCloser) ([]byte, error) { defer b.Close() diff --git a/vendor/github.com/docker/docker/vendor.conf b/vendor/github.com/docker/docker/vendor.conf index 75e68008fb..021c45b77d 100644 --- a/vendor/github.com/docker/docker/vendor.conf +++ b/vendor/github.com/docker/docker/vendor.conf @@ -1,8 +1,7 @@ # the following lines are in sorted order, FYI github.com/Azure/go-ansiterm 388960b655244e76e24c75f48631564eaefade62 github.com/Microsoft/hcsshim v0.5.13 -# TODO: get rid of this fork once PR https://github.com/Microsoft/go-winio/pull/43 is merged -github.com/Microsoft/go-winio 7c7d6b461cb10872c1138a0d7f3acf9a41b5c353 https://github.com/dgageot/go-winio.git +github.com/Microsoft/go-winio v0.3.9 github.com/Sirupsen/logrus v0.11.0 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a @@ -23,14 +22,17 @@ github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987 github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5 github.com/imdario/mergo 0.2.1 +golang.org/x/sync de49d9dcd27d4f764488181bea099dfe6179bcf0 #get libnetwork packages -github.com/docker/libnetwork b13e0604016a4944025aaff521d9c125850b0d04 +github.com/docker/libnetwork cace103704768d39bd88a23d0df76df125a0e39a github.com/docker/go-events 18b43f1bc85d9cdd42c05a6cd2d444c7a200a894 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80 github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b -github.com/hashicorp/memberlist 88ac4de0d1a0ca6def284b571342db3b777a4c37 +github.com/hashicorp/memberlist v0.1.0 +github.com/sean-/seed e2103e2c35297fb7e17febb81e49b312087a2372 +github.com/hashicorp/go-sockaddr acd314c5781ea706c710d9ea70069fd2e110d61d github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870 github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef @@ -106,7 +108,7 @@ github.com/docker/containerd 9048e5e50717ea4497b757314bad98ea3763c145 github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4 # cluster -github.com/docker/swarmkit b19d028de0a6e9ca281afeb76cea2544b9edd839 +github.com/docker/swarmkit 61a92e8ec074df5769decda985df4a3ab43c77eb github.com/gogo/protobuf 8d70fb3182befc465c4a1eac8ad4d38ff49778e2 github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e @@ -142,3 +144,4 @@ github.com/xeipuuv/gojsonpointer e0fe6f68307607d540ed8eac07a342c33fa1b54a github.com/xeipuuv/gojsonreference e02fc20de94c78484cd5ffb007f8af96be030a45 github.com/xeipuuv/gojsonschema 93e72a773fade158921402d6a24c819b48aba29d gopkg.in/yaml.v2 4c78c975fe7c825c6d1466c42be594d1d6f3aba6 +github.com/opencontainers/selinux ba1aefe8057f1d0cfb8e88d0ec1dc85925ef987d diff --git a/vendor/github.com/docker/docker/volume/volume.go b/vendor/github.com/docker/docker/volume/volume.go index d73e2d511f..a23d993830 100644 --- a/vendor/github.com/docker/docker/volume/volume.go +++ b/vendor/github.com/docker/docker/volume/volume.go @@ -10,7 +10,7 @@ import ( mounttypes "github.com/docker/docker/api/types/mount" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/stringid" - "github.com/opencontainers/runc/libcontainer/label" + "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" ) @@ -311,10 +311,12 @@ func ParseMountSpec(cfg mounttypes.Mount, options ...func(*validateOpts)) (*Moun } case mounttypes.TypeBind: mp.Source = clean(convertSlash(cfg.Source)) - if cfg.BindOptions != nil { - if len(cfg.BindOptions.Propagation) > 0 { - mp.Propagation = cfg.BindOptions.Propagation - } + if cfg.BindOptions != nil && len(cfg.BindOptions.Propagation) > 0 { + mp.Propagation = cfg.BindOptions.Propagation + } else { + // If user did not specify a propagation mode, get + // default propagation mode. + mp.Propagation = DefaultPropagationMode } case mounttypes.TypeTmpfs: // NOP diff --git a/vendor/github.com/go-check/check/LICENSE b/vendor/github.com/go-check/check/LICENSE deleted file mode 100644 index 545cf2d331..0000000000 --- a/vendor/github.com/go-check/check/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Gocheck - A rich testing framework for Go - -Copyright (c) 2010-2013 Gustavo Niemeyer - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/go-check/check/README.md b/vendor/github.com/go-check/check/README.md deleted file mode 100644 index 0ca78d97e8..0000000000 --- a/vendor/github.com/go-check/check/README.md +++ /dev/null @@ -1,10 +0,0 @@ -Go-check -======== - -This is a fork of https://github.com/go-check/check - -The intention of this fork is not to change any of the original behavior, but add -some specific behaviors needed for some of my projects already using this test suite. -For documentation on the main behavior of go-check see the aforementioned repo. - -The original branch is intact at `orig_v1` diff --git a/vendor/github.com/go-check/check/benchmark.go b/vendor/github.com/go-check/check/benchmark.go deleted file mode 100644 index 46ea9dc6da..0000000000 --- a/vendor/github.com/go-check/check/benchmark.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) 2012 The Go Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -package check - -import ( - "fmt" - "runtime" - "time" -) - -var memStats runtime.MemStats - -// testingB is a type passed to Benchmark functions to manage benchmark -// timing and to specify the number of iterations to run. -type timer struct { - start time.Time // Time test or benchmark started - duration time.Duration - N int - bytes int64 - timerOn bool - benchTime time.Duration - // The initial states of memStats.Mallocs and memStats.TotalAlloc. - startAllocs uint64 - startBytes uint64 - // The net total of this test after being run. - netAllocs uint64 - netBytes uint64 -} - -// StartTimer starts timing a test. This function is called automatically -// before a benchmark starts, but it can also used to resume timing after -// a call to StopTimer. -func (c *C) StartTimer() { - if !c.timerOn { - c.start = time.Now() - c.timerOn = true - - runtime.ReadMemStats(&memStats) - c.startAllocs = memStats.Mallocs - c.startBytes = memStats.TotalAlloc - } -} - -// StopTimer stops timing a test. This can be used to pause the timer -// while performing complex initialization that you don't -// want to measure. -func (c *C) StopTimer() { - if c.timerOn { - c.duration += time.Now().Sub(c.start) - c.timerOn = false - runtime.ReadMemStats(&memStats) - c.netAllocs += memStats.Mallocs - c.startAllocs - c.netBytes += memStats.TotalAlloc - c.startBytes - } -} - -// ResetTimer sets the elapsed benchmark time to zero. -// It does not affect whether the timer is running. -func (c *C) ResetTimer() { - if c.timerOn { - c.start = time.Now() - runtime.ReadMemStats(&memStats) - c.startAllocs = memStats.Mallocs - c.startBytes = memStats.TotalAlloc - } - c.duration = 0 - c.netAllocs = 0 - c.netBytes = 0 -} - -// SetBytes informs the number of bytes that the benchmark processes -// on each iteration. If this is called in a benchmark it will also -// report MB/s. -func (c *C) SetBytes(n int64) { - c.bytes = n -} - -func (c *C) nsPerOp() int64 { - if c.N <= 0 { - return 0 - } - return c.duration.Nanoseconds() / int64(c.N) -} - -func (c *C) mbPerSec() float64 { - if c.bytes <= 0 || c.duration <= 0 || c.N <= 0 { - return 0 - } - return (float64(c.bytes) * float64(c.N) / 1e6) / c.duration.Seconds() -} - -func (c *C) timerString() string { - if c.N <= 0 { - return fmt.Sprintf("%3.3fs", float64(c.duration.Nanoseconds())/1e9) - } - mbs := c.mbPerSec() - mb := "" - if mbs != 0 { - mb = fmt.Sprintf("\t%7.2f MB/s", mbs) - } - nsop := c.nsPerOp() - ns := fmt.Sprintf("%10d ns/op", nsop) - if c.N > 0 && nsop < 100 { - // The format specifiers here make sure that - // the ones digits line up for all three possible formats. - if nsop < 10 { - ns = fmt.Sprintf("%13.2f ns/op", float64(c.duration.Nanoseconds())/float64(c.N)) - } else { - ns = fmt.Sprintf("%12.1f ns/op", float64(c.duration.Nanoseconds())/float64(c.N)) - } - } - memStats := "" - if c.benchMem { - allocedBytes := fmt.Sprintf("%8d B/op", int64(c.netBytes)/int64(c.N)) - allocs := fmt.Sprintf("%8d allocs/op", int64(c.netAllocs)/int64(c.N)) - memStats = fmt.Sprintf("\t%s\t%s", allocedBytes, allocs) - } - return fmt.Sprintf("%8d\t%s%s%s", c.N, ns, mb, memStats) -} - -func min(x, y int) int { - if x > y { - return y - } - return x -} - -func max(x, y int) int { - if x < y { - return y - } - return x -} - -// roundDown10 rounds a number down to the nearest power of 10. -func roundDown10(n int) int { - var tens = 0 - // tens = floor(log_10(n)) - for n > 10 { - n = n / 10 - tens++ - } - // result = 10^tens - result := 1 - for i := 0; i < tens; i++ { - result *= 10 - } - return result -} - -// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX]. -func roundUp(n int) int { - base := roundDown10(n) - if n < (2 * base) { - return 2 * base - } - if n < (5 * base) { - return 5 * base - } - return 10 * base -} diff --git a/vendor/github.com/go-check/check/check.go b/vendor/github.com/go-check/check/check.go deleted file mode 100644 index a4e60903c2..0000000000 --- a/vendor/github.com/go-check/check/check.go +++ /dev/null @@ -1,939 +0,0 @@ -// Package check is a rich testing extension for Go's testing package. -// -// For details about the project, see: -// -// http://labix.org/gocheck -// -package check - -import ( - "bytes" - "errors" - "fmt" - "io" - "math/rand" - "os" - "path" - "path/filepath" - "reflect" - "regexp" - "runtime" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" -) - -// ----------------------------------------------------------------------- -// Internal type which deals with suite method calling. - -const ( - fixtureKd = iota - testKd -) - -type funcKind int - -const ( - succeededSt = iota - failedSt - skippedSt - panickedSt - fixturePanickedSt - missedSt -) - -type funcStatus uint32 - -// A method value can't reach its own Method structure. -type methodType struct { - reflect.Value - Info reflect.Method -} - -func newMethod(receiver reflect.Value, i int) *methodType { - return &methodType{receiver.Method(i), receiver.Type().Method(i)} -} - -func (method *methodType) PC() uintptr { - return method.Info.Func.Pointer() -} - -func (method *methodType) suiteName() string { - t := method.Info.Type.In(0) - if t.Kind() == reflect.Ptr { - t = t.Elem() - } - return t.Name() -} - -func (method *methodType) String() string { - return method.suiteName() + "." + method.Info.Name -} - -func (method *methodType) matches(re *regexp.Regexp) bool { - return (re.MatchString(method.Info.Name) || - re.MatchString(method.suiteName()) || - re.MatchString(method.String())) -} - -type C struct { - method *methodType - kind funcKind - testName string - _status funcStatus - logb *logger - logw io.Writer - done chan *C - reason string - mustFail bool - tempDir *tempDir - benchMem bool - startTime time.Time - timer -} - -func (c *C) status() funcStatus { - return funcStatus(atomic.LoadUint32((*uint32)(&c._status))) -} - -func (c *C) setStatus(s funcStatus) { - atomic.StoreUint32((*uint32)(&c._status), uint32(s)) -} - -func (c *C) stopNow() { - runtime.Goexit() -} - -// logger is a concurrency safe byte.Buffer -type logger struct { - sync.Mutex - writer bytes.Buffer -} - -func (l *logger) Write(buf []byte) (int, error) { - l.Lock() - defer l.Unlock() - return l.writer.Write(buf) -} - -func (l *logger) WriteTo(w io.Writer) (int64, error) { - l.Lock() - defer l.Unlock() - return l.writer.WriteTo(w) -} - -func (l *logger) String() string { - l.Lock() - defer l.Unlock() - return l.writer.String() -} - -// ----------------------------------------------------------------------- -// Handling of temporary files and directories. - -type tempDir struct { - sync.Mutex - path string - counter int -} - -func (td *tempDir) newPath() string { - td.Lock() - defer td.Unlock() - if td.path == "" { - var err error - for i := 0; i != 100; i++ { - path := fmt.Sprintf("%s%ccheck-%d", os.TempDir(), os.PathSeparator, rand.Int()) - if err = os.Mkdir(path, 0700); err == nil { - td.path = path - break - } - } - if td.path == "" { - panic("Couldn't create temporary directory: " + err.Error()) - } - } - result := filepath.Join(td.path, strconv.Itoa(td.counter)) - td.counter += 1 - return result -} - -func (td *tempDir) removeAll() { - td.Lock() - defer td.Unlock() - if td.path != "" { - err := os.RemoveAll(td.path) - if err != nil { - fmt.Fprintf(os.Stderr, "WARNING: Error cleaning up temporaries: "+err.Error()) - } - } -} - -// Create a new temporary directory which is automatically removed after -// the suite finishes running. -func (c *C) MkDir() string { - path := c.tempDir.newPath() - if err := os.Mkdir(path, 0700); err != nil { - panic(fmt.Sprintf("Couldn't create temporary directory %s: %s", path, err.Error())) - } - return path -} - -// ----------------------------------------------------------------------- -// Low-level logging functions. - -func (c *C) log(args ...interface{}) { - c.writeLog([]byte(fmt.Sprint(args...) + "\n")) -} - -func (c *C) logf(format string, args ...interface{}) { - c.writeLog([]byte(fmt.Sprintf(format+"\n", args...))) -} - -func (c *C) logNewLine() { - c.writeLog([]byte{'\n'}) -} - -func (c *C) writeLog(buf []byte) { - c.logb.Write(buf) - if c.logw != nil { - c.logw.Write(buf) - } -} - -func hasStringOrError(x interface{}) (ok bool) { - _, ok = x.(fmt.Stringer) - if ok { - return - } - _, ok = x.(error) - return -} - -func (c *C) logValue(label string, value interface{}) { - if label == "" { - if hasStringOrError(value) { - c.logf("... %#v (%q)", value, value) - } else { - c.logf("... %#v", value) - } - } else if value == nil { - c.logf("... %s = nil", label) - } else { - if hasStringOrError(value) { - fv := fmt.Sprintf("%#v", value) - qv := fmt.Sprintf("%q", value) - if fv != qv { - c.logf("... %s %s = %s (%s)", label, reflect.TypeOf(value), fv, qv) - return - } - } - if s, ok := value.(string); ok && isMultiLine(s) { - c.logf(`... %s %s = "" +`, label, reflect.TypeOf(value)) - c.logMultiLine(s) - } else { - c.logf("... %s %s = %#v", label, reflect.TypeOf(value), value) - } - } -} - -func (c *C) logMultiLine(s string) { - b := make([]byte, 0, len(s)*2) - i := 0 - n := len(s) - for i < n { - j := i + 1 - for j < n && s[j-1] != '\n' { - j++ - } - b = append(b, "... "...) - b = strconv.AppendQuote(b, s[i:j]) - if j < n { - b = append(b, " +"...) - } - b = append(b, '\n') - i = j - } - c.writeLog(b) -} - -func isMultiLine(s string) bool { - for i := 0; i+1 < len(s); i++ { - if s[i] == '\n' { - return true - } - } - return false -} - -func (c *C) logString(issue string) { - c.log("... ", issue) -} - -func (c *C) logCaller(skip int) { - // This is a bit heavier than it ought to be. - skip += 1 // Our own frame. - pc, callerFile, callerLine, ok := runtime.Caller(skip) - if !ok { - return - } - var testFile string - var testLine int - testFunc := runtime.FuncForPC(c.method.PC()) - if runtime.FuncForPC(pc) != testFunc { - for { - skip += 1 - if pc, file, line, ok := runtime.Caller(skip); ok { - // Note that the test line may be different on - // distinct calls for the same test. Showing - // the "internal" line is helpful when debugging. - if runtime.FuncForPC(pc) == testFunc { - testFile, testLine = file, line - break - } - } else { - break - } - } - } - if testFile != "" && (testFile != callerFile || testLine != callerLine) { - c.logCode(testFile, testLine) - } - c.logCode(callerFile, callerLine) -} - -func (c *C) logCode(path string, line int) { - c.logf("%s:%d:", nicePath(path), line) - code, err := printLine(path, line) - if code == "" { - code = "..." // XXX Open the file and take the raw line. - if err != nil { - code += err.Error() - } - } - c.log(indent(code, " ")) -} - -var valueGo = filepath.Join("reflect", "value.go") -var asmGo = filepath.Join("runtime", "asm_") - -func (c *C) logPanic(skip int, value interface{}) { - skip++ // Our own frame. - initialSkip := skip - for ; ; skip++ { - if pc, file, line, ok := runtime.Caller(skip); ok { - if skip == initialSkip { - c.logf("... Panic: %s (PC=0x%X)\n", value, pc) - } - name := niceFuncName(pc) - path := nicePath(file) - if strings.Contains(path, "/gopkg.in/check.v") { - continue - } - if name == "Value.call" && strings.HasSuffix(path, valueGo) { - continue - } - if (name == "call16" || name == "call32") && strings.Contains(path, asmGo) { - continue - } - c.logf("%s:%d\n in %s", nicePath(file), line, name) - } else { - break - } - } -} - -func (c *C) logSoftPanic(issue string) { - c.log("... Panic: ", issue) -} - -func (c *C) logArgPanic(method *methodType, expectedType string) { - c.logf("... Panic: %s argument should be %s", - niceFuncName(method.PC()), expectedType) -} - -// ----------------------------------------------------------------------- -// Some simple formatting helpers. - -var initWD, initWDErr = os.Getwd() - -func init() { - if initWDErr == nil { - initWD = strings.Replace(initWD, "\\", "/", -1) + "/" - } -} - -func nicePath(path string) string { - if initWDErr == nil { - if strings.HasPrefix(path, initWD) { - return path[len(initWD):] - } - } - return path -} - -func niceFuncPath(pc uintptr) string { - function := runtime.FuncForPC(pc) - if function != nil { - filename, line := function.FileLine(pc) - return fmt.Sprintf("%s:%d", nicePath(filename), line) - } - return "" -} - -func niceFuncName(pc uintptr) string { - function := runtime.FuncForPC(pc) - if function != nil { - name := path.Base(function.Name()) - if i := strings.Index(name, "."); i > 0 { - name = name[i+1:] - } - if strings.HasPrefix(name, "(*") { - if i := strings.Index(name, ")"); i > 0 { - name = name[2:i] + name[i+1:] - } - } - if i := strings.LastIndex(name, ".*"); i != -1 { - name = name[:i] + "." + name[i+2:] - } - if i := strings.LastIndex(name, "·"); i != -1 { - name = name[:i] + "." + name[i+2:] - } - return name - } - return "" -} - -// ----------------------------------------------------------------------- -// Result tracker to aggregate call results. - -type Result struct { - Succeeded int - Failed int - Skipped int - Panicked int - FixturePanicked int - ExpectedFailures int - Missed int // Not even tried to run, related to a panic in the fixture. - RunError error // Houston, we've got a problem. - WorkDir string // If KeepWorkDir is true -} - -type resultTracker struct { - result Result - _lastWasProblem bool - _waiting int - _missed int - _expectChan chan *C - _doneChan chan *C - _stopChan chan bool -} - -func newResultTracker() *resultTracker { - return &resultTracker{_expectChan: make(chan *C), // Synchronous - _doneChan: make(chan *C, 32), // Asynchronous - _stopChan: make(chan bool)} // Synchronous -} - -func (tracker *resultTracker) start() { - go tracker._loopRoutine() -} - -func (tracker *resultTracker) waitAndStop() { - <-tracker._stopChan -} - -func (tracker *resultTracker) expectCall(c *C) { - tracker._expectChan <- c -} - -func (tracker *resultTracker) callDone(c *C) { - tracker._doneChan <- c -} - -func (tracker *resultTracker) _loopRoutine() { - for { - var c *C - if tracker._waiting > 0 { - // Calls still running. Can't stop. - select { - // XXX Reindent this (not now to make diff clear) - case c = <-tracker._expectChan: - tracker._waiting += 1 - case c = <-tracker._doneChan: - tracker._waiting -= 1 - switch c.status() { - case succeededSt: - if c.kind == testKd { - if c.mustFail { - tracker.result.ExpectedFailures++ - } else { - tracker.result.Succeeded++ - } - } - case failedSt: - tracker.result.Failed++ - case panickedSt: - if c.kind == fixtureKd { - tracker.result.FixturePanicked++ - } else { - tracker.result.Panicked++ - } - case fixturePanickedSt: - // Track it as missed, since the panic - // was on the fixture, not on the test. - tracker.result.Missed++ - case missedSt: - tracker.result.Missed++ - case skippedSt: - if c.kind == testKd { - tracker.result.Skipped++ - } - } - } - } else { - // No calls. Can stop, but no done calls here. - select { - case tracker._stopChan <- true: - return - case c = <-tracker._expectChan: - tracker._waiting += 1 - case c = <-tracker._doneChan: - panic("Tracker got an unexpected done call.") - } - } - } -} - -// ----------------------------------------------------------------------- -// The underlying suite runner. - -type suiteRunner struct { - suite interface{} - setUpSuite, tearDownSuite *methodType - setUpTest, tearDownTest *methodType - onTimeout *methodType - tests []*methodType - tracker *resultTracker - tempDir *tempDir - keepDir bool - output *outputWriter - reportedProblemLast bool - benchTime time.Duration - benchMem bool - checkTimeout time.Duration -} - -type RunConf struct { - Output io.Writer - Stream bool - Verbose bool - Filter string - Benchmark bool - BenchmarkTime time.Duration // Defaults to 1 second - BenchmarkMem bool - KeepWorkDir bool - CheckTimeout time.Duration -} - -// Create a new suiteRunner able to run all methods in the given suite. -func newSuiteRunner(suite interface{}, runConf *RunConf) *suiteRunner { - var conf RunConf - if runConf != nil { - conf = *runConf - } - if conf.Output == nil { - conf.Output = os.Stdout - } - if conf.Benchmark { - conf.Verbose = true - } - - suiteType := reflect.TypeOf(suite) - suiteNumMethods := suiteType.NumMethod() - suiteValue := reflect.ValueOf(suite) - - runner := &suiteRunner{ - suite: suite, - output: newOutputWriter(conf.Output, conf.Stream, conf.Verbose), - tracker: newResultTracker(), - benchTime: conf.BenchmarkTime, - benchMem: conf.BenchmarkMem, - tempDir: &tempDir{}, - keepDir: conf.KeepWorkDir, - tests: make([]*methodType, 0, suiteNumMethods), - checkTimeout: conf.CheckTimeout, - } - if runner.benchTime == 0 { - runner.benchTime = 1 * time.Second - } - - var filterRegexp *regexp.Regexp - if conf.Filter != "" { - if regexp, err := regexp.Compile(conf.Filter); err != nil { - msg := "Bad filter expression: " + err.Error() - runner.tracker.result.RunError = errors.New(msg) - return runner - } else { - filterRegexp = regexp - } - } - - for i := 0; i != suiteNumMethods; i++ { - method := newMethod(suiteValue, i) - switch method.Info.Name { - case "SetUpSuite": - runner.setUpSuite = method - case "TearDownSuite": - runner.tearDownSuite = method - case "SetUpTest": - runner.setUpTest = method - case "TearDownTest": - runner.tearDownTest = method - case "OnTimeout": - runner.onTimeout = method - default: - prefix := "Test" - if conf.Benchmark { - prefix = "Benchmark" - } - if !strings.HasPrefix(method.Info.Name, prefix) { - continue - } - if filterRegexp == nil || method.matches(filterRegexp) { - runner.tests = append(runner.tests, method) - } - } - } - return runner -} - -// Run all methods in the given suite. -func (runner *suiteRunner) run() *Result { - if runner.tracker.result.RunError == nil && len(runner.tests) > 0 { - runner.tracker.start() - if runner.checkFixtureArgs() { - c := runner.runFixture(runner.setUpSuite, "", nil) - if c == nil || c.status() == succeededSt { - for i := 0; i != len(runner.tests); i++ { - c := runner.runTest(runner.tests[i]) - if c.status() == fixturePanickedSt { - runner.skipTests(missedSt, runner.tests[i+1:]) - break - } - } - } else if c != nil && c.status() == skippedSt { - runner.skipTests(skippedSt, runner.tests) - } else { - runner.skipTests(missedSt, runner.tests) - } - runner.runFixture(runner.tearDownSuite, "", nil) - } else { - runner.skipTests(missedSt, runner.tests) - } - runner.tracker.waitAndStop() - if runner.keepDir { - runner.tracker.result.WorkDir = runner.tempDir.path - } else { - runner.tempDir.removeAll() - } - } - return &runner.tracker.result -} - -// Create a call object with the given suite method, and fork a -// goroutine with the provided dispatcher for running it. -func (runner *suiteRunner) forkCall(method *methodType, kind funcKind, testName string, logb *logger, dispatcher func(c *C)) *C { - var logw io.Writer - if runner.output.Stream { - logw = runner.output - } - if logb == nil { - logb = new(logger) - } - c := &C{ - method: method, - kind: kind, - testName: testName, - logb: logb, - logw: logw, - tempDir: runner.tempDir, - done: make(chan *C, 1), - timer: timer{benchTime: runner.benchTime}, - startTime: time.Now(), - benchMem: runner.benchMem, - } - runner.tracker.expectCall(c) - go (func() { - runner.reportCallStarted(c) - defer runner.callDone(c) - dispatcher(c) - })() - return c -} - -type timeoutErr struct { - method *methodType - t time.Duration -} - -func (e timeoutErr) Error() string { - return fmt.Sprintf("%s test timed out after %v", e.method.String(), e.t) -} - -func isTimeout(e error) bool { - if e == nil { - return false - } - _, ok := e.(timeoutErr) - return ok -} - -// Same as forkCall(), but wait for call to finish before returning. -func (runner *suiteRunner) runFunc(method *methodType, kind funcKind, testName string, logb *logger, dispatcher func(c *C)) *C { - var timeout <-chan time.Time - if runner.checkTimeout != 0 { - timeout = time.After(runner.checkTimeout) - } - c := runner.forkCall(method, kind, testName, logb, dispatcher) - select { - case <-c.done: - case <-timeout: - if runner.onTimeout != nil { - // run the OnTimeout callback, allowing the suite to collect any sort of debug information it can - // `runFixture` is syncronous, so run this in a separate goroutine with a timeout - cChan := make(chan *C) - go func() { - cChan <- runner.runFixture(runner.onTimeout, c.testName, c.logb) - }() - select { - case <-cChan: - case <-time.After(runner.checkTimeout): - } - } - panic(timeoutErr{method, runner.checkTimeout}) - } - return c -} - -// Handle a finished call. If there were any panics, update the call status -// accordingly. Then, mark the call as done and report to the tracker. -func (runner *suiteRunner) callDone(c *C) { - value := recover() - if value != nil { - switch v := value.(type) { - case *fixturePanic: - if v.status == skippedSt { - c.setStatus(skippedSt) - } else { - c.logSoftPanic("Fixture has panicked (see related PANIC)") - c.setStatus(fixturePanickedSt) - } - default: - c.logPanic(1, value) - c.setStatus(panickedSt) - } - } - if c.mustFail { - switch c.status() { - case failedSt: - c.setStatus(succeededSt) - case succeededSt: - c.setStatus(failedSt) - c.logString("Error: Test succeeded, but was expected to fail") - c.logString("Reason: " + c.reason) - } - } - - runner.reportCallDone(c) - c.done <- c -} - -// Runs a fixture call synchronously. The fixture will still be run in a -// goroutine like all suite methods, but this method will not return -// while the fixture goroutine is not done, because the fixture must be -// run in a desired order. -func (runner *suiteRunner) runFixture(method *methodType, testName string, logb *logger) *C { - if method != nil { - c := runner.runFunc(method, fixtureKd, testName, logb, func(c *C) { - c.ResetTimer() - c.StartTimer() - defer c.StopTimer() - c.method.Call([]reflect.Value{reflect.ValueOf(c)}) - }) - return c - } - return nil -} - -// Run the fixture method with runFixture(), but panic with a fixturePanic{} -// in case the fixture method panics. This makes it easier to track the -// fixture panic together with other call panics within forkTest(). -func (runner *suiteRunner) runFixtureWithPanic(method *methodType, testName string, logb *logger, skipped *bool) *C { - if skipped != nil && *skipped { - return nil - } - c := runner.runFixture(method, testName, logb) - if c != nil && c.status() != succeededSt { - if skipped != nil { - *skipped = c.status() == skippedSt - } - panic(&fixturePanic{c.status(), method}) - } - return c -} - -type fixturePanic struct { - status funcStatus - method *methodType -} - -// Run the suite test method, together with the test-specific fixture, -// asynchronously. -func (runner *suiteRunner) forkTest(method *methodType) *C { - testName := method.String() - return runner.forkCall(method, testKd, testName, nil, func(c *C) { - var skipped bool - defer runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, &skipped) - defer c.StopTimer() - benchN := 1 - for { - runner.runFixtureWithPanic(runner.setUpTest, testName, c.logb, &skipped) - mt := c.method.Type() - if mt.NumIn() != 1 || mt.In(0) != reflect.TypeOf(c) { - // Rather than a plain panic, provide a more helpful message when - // the argument type is incorrect. - c.setStatus(panickedSt) - c.logArgPanic(c.method, "*check.C") - return - } - - if strings.HasPrefix(c.method.Info.Name, "Test") { - c.ResetTimer() - c.StartTimer() - c.method.Call([]reflect.Value{reflect.ValueOf(c)}) - return - } - - if !strings.HasPrefix(c.method.Info.Name, "Benchmark") { - panic("unexpected method prefix: " + c.method.Info.Name) - } - - runtime.GC() - c.N = benchN - c.ResetTimer() - c.StartTimer() - - c.method.Call([]reflect.Value{reflect.ValueOf(c)}) - c.StopTimer() - if c.status() != succeededSt || c.duration >= c.benchTime || benchN >= 1e9 { - return - } - perOpN := int(1e9) - if c.nsPerOp() != 0 { - perOpN = int(c.benchTime.Nanoseconds() / c.nsPerOp()) - } - - // Logic taken from the stock testing package: - // - Run more iterations than we think we'll need for a second (1.5x). - // - Don't grow too fast in case we had timing errors previously. - // - Be sure to run at least one more than last time. - benchN = max(min(perOpN+perOpN/2, 100*benchN), benchN+1) - benchN = roundUp(benchN) - - skipped = true // Don't run the deferred one if this panics. - runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, nil) - skipped = false - } - }) -} - -// Same as forkTest(), but wait for the test to finish before returning. -func (runner *suiteRunner) runTest(method *methodType) *C { - var timeout <-chan time.Time - if runner.checkTimeout != 0 { - timeout = time.After(runner.checkTimeout) - } - c := runner.forkTest(method) - select { - case <-c.done: - case <-timeout: - if runner.onTimeout != nil { - // run the OnTimeout callback, allowing the suite to collect any sort of debug information it can - // `runFixture` is syncronous, so run this in a separate goroutine with a timeout - cChan := make(chan *C) - go func() { - cChan <- runner.runFixture(runner.onTimeout, c.testName, c.logb) - }() - select { - case <-cChan: - case <-time.After(runner.checkTimeout): - } - } - panic(timeoutErr{method, runner.checkTimeout}) - } - return c -} - -// Helper to mark tests as skipped or missed. A bit heavy for what -// it does, but it enables homogeneous handling of tracking, including -// nice verbose output. -func (runner *suiteRunner) skipTests(status funcStatus, methods []*methodType) { - for _, method := range methods { - runner.runFunc(method, testKd, "", nil, func(c *C) { - c.setStatus(status) - }) - } -} - -// Verify if the fixture arguments are *check.C. In case of errors, -// log the error as a panic in the fixture method call, and return false. -func (runner *suiteRunner) checkFixtureArgs() bool { - succeeded := true - argType := reflect.TypeOf(&C{}) - for _, method := range []*methodType{runner.setUpSuite, runner.tearDownSuite, runner.setUpTest, runner.tearDownTest, runner.onTimeout} { - if method != nil { - mt := method.Type() - if mt.NumIn() != 1 || mt.In(0) != argType { - succeeded = false - runner.runFunc(method, fixtureKd, "", nil, func(c *C) { - c.logArgPanic(method, "*check.C") - c.setStatus(panickedSt) - }) - } - } - } - return succeeded -} - -func (runner *suiteRunner) reportCallStarted(c *C) { - runner.output.WriteCallStarted("START", c) -} - -func (runner *suiteRunner) reportCallDone(c *C) { - runner.tracker.callDone(c) - switch c.status() { - case succeededSt: - if c.mustFail { - runner.output.WriteCallSuccess("FAIL EXPECTED", c) - } else { - runner.output.WriteCallSuccess("PASS", c) - } - case skippedSt: - runner.output.WriteCallSuccess("SKIP", c) - case failedSt: - runner.output.WriteCallProblem("FAIL", c) - case panickedSt: - runner.output.WriteCallProblem("PANIC", c) - case fixturePanickedSt: - // That's a testKd call reporting that its fixture - // has panicked. The fixture call which caused the - // panic itself was tracked above. We'll report to - // aid debugging. - runner.output.WriteCallProblem("PANIC", c) - case missedSt: - runner.output.WriteCallSuccess("MISS", c) - } -} diff --git a/vendor/github.com/go-check/check/checkers.go b/vendor/github.com/go-check/check/checkers.go deleted file mode 100644 index bac338729c..0000000000 --- a/vendor/github.com/go-check/check/checkers.go +++ /dev/null @@ -1,458 +0,0 @@ -package check - -import ( - "fmt" - "reflect" - "regexp" -) - -// ----------------------------------------------------------------------- -// CommentInterface and Commentf helper, to attach extra information to checks. - -type comment struct { - format string - args []interface{} -} - -// Commentf returns an infomational value to use with Assert or Check calls. -// If the checker test fails, the provided arguments will be passed to -// fmt.Sprintf, and will be presented next to the logged failure. -// -// For example: -// -// c.Assert(v, Equals, 42, Commentf("Iteration #%d failed.", i)) -// -// Note that if the comment is constant, a better option is to -// simply use a normal comment right above or next to the line, as -// it will also get printed with any errors: -// -// c.Assert(l, Equals, 8192) // Ensure buffer size is correct (bug #123) -// -func Commentf(format string, args ...interface{}) CommentInterface { - return &comment{format, args} -} - -// CommentInterface must be implemented by types that attach extra -// information to failed checks. See the Commentf function for details. -type CommentInterface interface { - CheckCommentString() string -} - -func (c *comment) CheckCommentString() string { - return fmt.Sprintf(c.format, c.args...) -} - -// ----------------------------------------------------------------------- -// The Checker interface. - -// The Checker interface must be provided by checkers used with -// the Assert and Check verification methods. -type Checker interface { - Info() *CheckerInfo - Check(params []interface{}, names []string) (result bool, error string) -} - -// See the Checker interface. -type CheckerInfo struct { - Name string - Params []string -} - -func (info *CheckerInfo) Info() *CheckerInfo { - return info -} - -// ----------------------------------------------------------------------- -// Not checker logic inverter. - -// The Not checker inverts the logic of the provided checker. The -// resulting checker will succeed where the original one failed, and -// vice-versa. -// -// For example: -// -// c.Assert(a, Not(Equals), b) -// -func Not(checker Checker) Checker { - return ¬Checker{checker} -} - -type notChecker struct { - sub Checker -} - -func (checker *notChecker) Info() *CheckerInfo { - info := *checker.sub.Info() - info.Name = "Not(" + info.Name + ")" - return &info -} - -func (checker *notChecker) Check(params []interface{}, names []string) (result bool, error string) { - result, error = checker.sub.Check(params, names) - result = !result - return -} - -// ----------------------------------------------------------------------- -// IsNil checker. - -type isNilChecker struct { - *CheckerInfo -} - -// The IsNil checker tests whether the obtained value is nil. -// -// For example: -// -// c.Assert(err, IsNil) -// -var IsNil Checker = &isNilChecker{ - &CheckerInfo{Name: "IsNil", Params: []string{"value"}}, -} - -func (checker *isNilChecker) Check(params []interface{}, names []string) (result bool, error string) { - return isNil(params[0]), "" -} - -func isNil(obtained interface{}) (result bool) { - if obtained == nil { - result = true - } else { - switch v := reflect.ValueOf(obtained); v.Kind() { - case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: - return v.IsNil() - } - } - return -} - -// ----------------------------------------------------------------------- -// NotNil checker. Alias for Not(IsNil), since it's so common. - -type notNilChecker struct { - *CheckerInfo -} - -// The NotNil checker verifies that the obtained value is not nil. -// -// For example: -// -// c.Assert(iface, NotNil) -// -// This is an alias for Not(IsNil), made available since it's a -// fairly common check. -// -var NotNil Checker = ¬NilChecker{ - &CheckerInfo{Name: "NotNil", Params: []string{"value"}}, -} - -func (checker *notNilChecker) Check(params []interface{}, names []string) (result bool, error string) { - return !isNil(params[0]), "" -} - -// ----------------------------------------------------------------------- -// Equals checker. - -type equalsChecker struct { - *CheckerInfo -} - -// The Equals checker verifies that the obtained value is equal to -// the expected value, according to usual Go semantics for ==. -// -// For example: -// -// c.Assert(value, Equals, 42) -// -var Equals Checker = &equalsChecker{ - &CheckerInfo{Name: "Equals", Params: []string{"obtained", "expected"}}, -} - -func (checker *equalsChecker) Check(params []interface{}, names []string) (result bool, error string) { - defer func() { - if v := recover(); v != nil { - result = false - error = fmt.Sprint(v) - } - }() - return params[0] == params[1], "" -} - -// ----------------------------------------------------------------------- -// DeepEquals checker. - -type deepEqualsChecker struct { - *CheckerInfo -} - -// The DeepEquals checker verifies that the obtained value is deep-equal to -// the expected value. The check will work correctly even when facing -// slices, interfaces, and values of different types (which always fail -// the test). -// -// For example: -// -// c.Assert(value, DeepEquals, 42) -// c.Assert(array, DeepEquals, []string{"hi", "there"}) -// -var DeepEquals Checker = &deepEqualsChecker{ - &CheckerInfo{Name: "DeepEquals", Params: []string{"obtained", "expected"}}, -} - -func (checker *deepEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) { - return reflect.DeepEqual(params[0], params[1]), "" -} - -// ----------------------------------------------------------------------- -// HasLen checker. - -type hasLenChecker struct { - *CheckerInfo -} - -// The HasLen checker verifies that the obtained value has the -// provided length. In many cases this is superior to using Equals -// in conjuction with the len function because in case the check -// fails the value itself will be printed, instead of its length, -// providing more details for figuring the problem. -// -// For example: -// -// c.Assert(list, HasLen, 5) -// -var HasLen Checker = &hasLenChecker{ - &CheckerInfo{Name: "HasLen", Params: []string{"obtained", "n"}}, -} - -func (checker *hasLenChecker) Check(params []interface{}, names []string) (result bool, error string) { - n, ok := params[1].(int) - if !ok { - return false, "n must be an int" - } - value := reflect.ValueOf(params[0]) - switch value.Kind() { - case reflect.Map, reflect.Array, reflect.Slice, reflect.Chan, reflect.String: - default: - return false, "obtained value type has no length" - } - return value.Len() == n, "" -} - -// ----------------------------------------------------------------------- -// ErrorMatches checker. - -type errorMatchesChecker struct { - *CheckerInfo -} - -// The ErrorMatches checker verifies that the error value -// is non nil and matches the regular expression provided. -// -// For example: -// -// c.Assert(err, ErrorMatches, "perm.*denied") -// -var ErrorMatches Checker = errorMatchesChecker{ - &CheckerInfo{Name: "ErrorMatches", Params: []string{"value", "regex"}}, -} - -func (checker errorMatchesChecker) Check(params []interface{}, names []string) (result bool, errStr string) { - if params[0] == nil { - return false, "Error value is nil" - } - err, ok := params[0].(error) - if !ok { - return false, "Value is not an error" - } - params[0] = err.Error() - names[0] = "error" - return matches(params[0], params[1]) -} - -// ----------------------------------------------------------------------- -// Matches checker. - -type matchesChecker struct { - *CheckerInfo -} - -// The Matches checker verifies that the string provided as the obtained -// value (or the string resulting from obtained.String()) matches the -// regular expression provided. -// -// For example: -// -// c.Assert(err, Matches, "perm.*denied") -// -var Matches Checker = &matchesChecker{ - &CheckerInfo{Name: "Matches", Params: []string{"value", "regex"}}, -} - -func (checker *matchesChecker) Check(params []interface{}, names []string) (result bool, error string) { - return matches(params[0], params[1]) -} - -func matches(value, regex interface{}) (result bool, error string) { - reStr, ok := regex.(string) - if !ok { - return false, "Regex must be a string" - } - valueStr, valueIsStr := value.(string) - if !valueIsStr { - if valueWithStr, valueHasStr := value.(fmt.Stringer); valueHasStr { - valueStr, valueIsStr = valueWithStr.String(), true - } - } - if valueIsStr { - matches, err := regexp.MatchString("^"+reStr+"$", valueStr) - if err != nil { - return false, "Can't compile regex: " + err.Error() - } - return matches, "" - } - return false, "Obtained value is not a string and has no .String()" -} - -// ----------------------------------------------------------------------- -// Panics checker. - -type panicsChecker struct { - *CheckerInfo -} - -// The Panics checker verifies that calling the provided zero-argument -// function will cause a panic which is deep-equal to the provided value. -// -// For example: -// -// c.Assert(func() { f(1, 2) }, Panics, &SomeErrorType{"BOOM"}). -// -// -var Panics Checker = &panicsChecker{ - &CheckerInfo{Name: "Panics", Params: []string{"function", "expected"}}, -} - -func (checker *panicsChecker) Check(params []interface{}, names []string) (result bool, error string) { - f := reflect.ValueOf(params[0]) - if f.Kind() != reflect.Func || f.Type().NumIn() != 0 { - return false, "Function must take zero arguments" - } - defer func() { - // If the function has not panicked, then don't do the check. - if error != "" { - return - } - params[0] = recover() - names[0] = "panic" - result = reflect.DeepEqual(params[0], params[1]) - }() - f.Call(nil) - return false, "Function has not panicked" -} - -type panicMatchesChecker struct { - *CheckerInfo -} - -// The PanicMatches checker verifies that calling the provided zero-argument -// function will cause a panic with an error value matching -// the regular expression provided. -// -// For example: -// -// c.Assert(func() { f(1, 2) }, PanicMatches, `open.*: no such file or directory`). -// -// -var PanicMatches Checker = &panicMatchesChecker{ - &CheckerInfo{Name: "PanicMatches", Params: []string{"function", "expected"}}, -} - -func (checker *panicMatchesChecker) Check(params []interface{}, names []string) (result bool, errmsg string) { - f := reflect.ValueOf(params[0]) - if f.Kind() != reflect.Func || f.Type().NumIn() != 0 { - return false, "Function must take zero arguments" - } - defer func() { - // If the function has not panicked, then don't do the check. - if errmsg != "" { - return - } - obtained := recover() - names[0] = "panic" - if e, ok := obtained.(error); ok { - params[0] = e.Error() - } else if _, ok := obtained.(string); ok { - params[0] = obtained - } else { - errmsg = "Panic value is not a string or an error" - return - } - result, errmsg = matches(params[0], params[1]) - }() - f.Call(nil) - return false, "Function has not panicked" -} - -// ----------------------------------------------------------------------- -// FitsTypeOf checker. - -type fitsTypeChecker struct { - *CheckerInfo -} - -// The FitsTypeOf checker verifies that the obtained value is -// assignable to a variable with the same type as the provided -// sample value. -// -// For example: -// -// c.Assert(value, FitsTypeOf, int64(0)) -// c.Assert(value, FitsTypeOf, os.Error(nil)) -// -var FitsTypeOf Checker = &fitsTypeChecker{ - &CheckerInfo{Name: "FitsTypeOf", Params: []string{"obtained", "sample"}}, -} - -func (checker *fitsTypeChecker) Check(params []interface{}, names []string) (result bool, error string) { - obtained := reflect.ValueOf(params[0]) - sample := reflect.ValueOf(params[1]) - if !obtained.IsValid() { - return false, "" - } - if !sample.IsValid() { - return false, "Invalid sample value" - } - return obtained.Type().AssignableTo(sample.Type()), "" -} - -// ----------------------------------------------------------------------- -// Implements checker. - -type implementsChecker struct { - *CheckerInfo -} - -// The Implements checker verifies that the obtained value -// implements the interface specified via a pointer to an interface -// variable. -// -// For example: -// -// var e os.Error -// c.Assert(err, Implements, &e) -// -var Implements Checker = &implementsChecker{ - &CheckerInfo{Name: "Implements", Params: []string{"obtained", "ifaceptr"}}, -} - -func (checker *implementsChecker) Check(params []interface{}, names []string) (result bool, error string) { - obtained := reflect.ValueOf(params[0]) - ifaceptr := reflect.ValueOf(params[1]) - if !obtained.IsValid() { - return false, "" - } - if !ifaceptr.IsValid() || ifaceptr.Kind() != reflect.Ptr || ifaceptr.Elem().Kind() != reflect.Interface { - return false, "ifaceptr should be a pointer to an interface variable" - } - return obtained.Type().Implements(ifaceptr.Elem().Type()), "" -} diff --git a/vendor/github.com/go-check/check/helpers.go b/vendor/github.com/go-check/check/helpers.go deleted file mode 100644 index 58a733b50f..0000000000 --- a/vendor/github.com/go-check/check/helpers.go +++ /dev/null @@ -1,231 +0,0 @@ -package check - -import ( - "fmt" - "strings" - "time" -) - -// TestName returns the current test name in the form "SuiteName.TestName" -func (c *C) TestName() string { - return c.testName -} - -// ----------------------------------------------------------------------- -// Basic succeeding/failing logic. - -// Failed returns whether the currently running test has already failed. -func (c *C) Failed() bool { - return c.status() == failedSt -} - -// Fail marks the currently running test as failed. -// -// Something ought to have been previously logged so the developer can tell -// what went wrong. The higher level helper functions will fail the test -// and do the logging properly. -func (c *C) Fail() { - c.setStatus(failedSt) -} - -// FailNow marks the currently running test as failed and stops running it. -// Something ought to have been previously logged so the developer can tell -// what went wrong. The higher level helper functions will fail the test -// and do the logging properly. -func (c *C) FailNow() { - c.Fail() - c.stopNow() -} - -// Succeed marks the currently running test as succeeded, undoing any -// previous failures. -func (c *C) Succeed() { - c.setStatus(succeededSt) -} - -// SucceedNow marks the currently running test as succeeded, undoing any -// previous failures, and stops running the test. -func (c *C) SucceedNow() { - c.Succeed() - c.stopNow() -} - -// ExpectFailure informs that the running test is knowingly broken for -// the provided reason. If the test does not fail, an error will be reported -// to raise attention to this fact. This method is useful to temporarily -// disable tests which cover well known problems until a better time to -// fix the problem is found, without forgetting about the fact that a -// failure still exists. -func (c *C) ExpectFailure(reason string) { - if reason == "" { - panic("Missing reason why the test is expected to fail") - } - c.mustFail = true - c.reason = reason -} - -// Skip skips the running test for the provided reason. If run from within -// SetUpTest, the individual test being set up will be skipped, and if run -// from within SetUpSuite, the whole suite is skipped. -func (c *C) Skip(reason string) { - if reason == "" { - panic("Missing reason why the test is being skipped") - } - c.reason = reason - c.setStatus(skippedSt) - c.stopNow() -} - -// ----------------------------------------------------------------------- -// Basic logging. - -// GetTestLog returns the current test error output. -func (c *C) GetTestLog() string { - return c.logb.String() -} - -// Log logs some information into the test error output. -// The provided arguments are assembled together into a string with fmt.Sprint. -func (c *C) Log(args ...interface{}) { - c.log(args...) -} - -// Log logs some information into the test error output. -// The provided arguments are assembled together into a string with fmt.Sprintf. -func (c *C) Logf(format string, args ...interface{}) { - c.logf(format, args...) -} - -// Output enables *C to be used as a logger in functions that require only -// the minimum interface of *log.Logger. -func (c *C) Output(calldepth int, s string) error { - d := time.Now().Sub(c.startTime) - msec := d / time.Millisecond - sec := d / time.Second - min := d / time.Minute - - c.Logf("[LOG] %d:%02d.%03d %s", min, sec%60, msec%1000, s) - return nil -} - -// Error logs an error into the test error output and marks the test as failed. -// The provided arguments are assembled together into a string with fmt.Sprint. -func (c *C) Error(args ...interface{}) { - c.logCaller(1) - c.logString(fmt.Sprint("Error: ", fmt.Sprint(args...))) - c.logNewLine() - c.Fail() -} - -// Errorf logs an error into the test error output and marks the test as failed. -// The provided arguments are assembled together into a string with fmt.Sprintf. -func (c *C) Errorf(format string, args ...interface{}) { - c.logCaller(1) - c.logString(fmt.Sprintf("Error: "+format, args...)) - c.logNewLine() - c.Fail() -} - -// Fatal logs an error into the test error output, marks the test as failed, and -// stops the test execution. The provided arguments are assembled together into -// a string with fmt.Sprint. -func (c *C) Fatal(args ...interface{}) { - c.logCaller(1) - c.logString(fmt.Sprint("Error: ", fmt.Sprint(args...))) - c.logNewLine() - c.FailNow() -} - -// Fatlaf logs an error into the test error output, marks the test as failed, and -// stops the test execution. The provided arguments are assembled together into -// a string with fmt.Sprintf. -func (c *C) Fatalf(format string, args ...interface{}) { - c.logCaller(1) - c.logString(fmt.Sprint("Error: ", fmt.Sprintf(format, args...))) - c.logNewLine() - c.FailNow() -} - -// ----------------------------------------------------------------------- -// Generic checks and assertions based on checkers. - -// Check verifies if the first value matches the expected value according -// to the provided checker. If they do not match, an error is logged, the -// test is marked as failed, and the test execution continues. -// -// Some checkers may not need the expected argument (e.g. IsNil). -// -// Extra arguments provided to the function are logged next to the reported -// problem when the matching fails. -func (c *C) Check(obtained interface{}, checker Checker, args ...interface{}) bool { - return c.internalCheck("Check", obtained, checker, args...) -} - -// Assert ensures that the first value matches the expected value according -// to the provided checker. If they do not match, an error is logged, the -// test is marked as failed, and the test execution stops. -// -// Some checkers may not need the expected argument (e.g. IsNil). -// -// Extra arguments provided to the function are logged next to the reported -// problem when the matching fails. -func (c *C) Assert(obtained interface{}, checker Checker, args ...interface{}) { - if !c.internalCheck("Assert", obtained, checker, args...) { - c.stopNow() - } -} - -func (c *C) internalCheck(funcName string, obtained interface{}, checker Checker, args ...interface{}) bool { - if checker == nil { - c.logCaller(2) - c.logString(fmt.Sprintf("%s(obtained, nil!?, ...):", funcName)) - c.logString("Oops.. you've provided a nil checker!") - c.logNewLine() - c.Fail() - return false - } - - // If the last argument is a bug info, extract it out. - var comment CommentInterface - if len(args) > 0 { - if c, ok := args[len(args)-1].(CommentInterface); ok { - comment = c - args = args[:len(args)-1] - } - } - - params := append([]interface{}{obtained}, args...) - info := checker.Info() - - if len(params) != len(info.Params) { - names := append([]string{info.Params[0], info.Name}, info.Params[1:]...) - c.logCaller(2) - c.logString(fmt.Sprintf("%s(%s):", funcName, strings.Join(names, ", "))) - c.logString(fmt.Sprintf("Wrong number of parameters for %s: want %d, got %d", info.Name, len(names), len(params)+1)) - c.logNewLine() - c.Fail() - return false - } - - // Copy since it may be mutated by Check. - names := append([]string{}, info.Params...) - - // Do the actual check. - result, error := checker.Check(params, names) - if !result || error != "" { - c.logCaller(2) - for i := 0; i != len(params); i++ { - c.logValue(names[i], params[i]) - } - if comment != nil { - c.logString(comment.CheckCommentString()) - } - if error != "" { - c.logString(error) - } - c.logNewLine() - c.Fail() - return false - } - return true -} diff --git a/vendor/github.com/go-check/check/printer.go b/vendor/github.com/go-check/check/printer.go deleted file mode 100644 index e0f7557b5c..0000000000 --- a/vendor/github.com/go-check/check/printer.go +++ /dev/null @@ -1,168 +0,0 @@ -package check - -import ( - "bytes" - "go/ast" - "go/parser" - "go/printer" - "go/token" - "os" -) - -func indent(s, with string) (r string) { - eol := true - for i := 0; i != len(s); i++ { - c := s[i] - switch { - case eol && c == '\n' || c == '\r': - case c == '\n' || c == '\r': - eol = true - case eol: - eol = false - s = s[:i] + with + s[i:] - i += len(with) - } - } - return s -} - -func printLine(filename string, line int) (string, error) { - fset := token.NewFileSet() - file, err := os.Open(filename) - if err != nil { - return "", err - } - fnode, err := parser.ParseFile(fset, filename, file, parser.ParseComments) - if err != nil { - return "", err - } - config := &printer.Config{Mode: printer.UseSpaces, Tabwidth: 4} - lp := &linePrinter{fset: fset, fnode: fnode, line: line, config: config} - ast.Walk(lp, fnode) - result := lp.output.Bytes() - // Comments leave \n at the end. - n := len(result) - for n > 0 && result[n-1] == '\n' { - n-- - } - return string(result[:n]), nil -} - -type linePrinter struct { - config *printer.Config - fset *token.FileSet - fnode *ast.File - line int - output bytes.Buffer - stmt ast.Stmt -} - -func (lp *linePrinter) emit() bool { - if lp.stmt != nil { - lp.trim(lp.stmt) - lp.printWithComments(lp.stmt) - lp.stmt = nil - return true - } - return false -} - -func (lp *linePrinter) printWithComments(n ast.Node) { - nfirst := lp.fset.Position(n.Pos()).Line - nlast := lp.fset.Position(n.End()).Line - for _, g := range lp.fnode.Comments { - cfirst := lp.fset.Position(g.Pos()).Line - clast := lp.fset.Position(g.End()).Line - if clast == nfirst-1 && lp.fset.Position(n.Pos()).Column == lp.fset.Position(g.Pos()).Column { - for _, c := range g.List { - lp.output.WriteString(c.Text) - lp.output.WriteByte('\n') - } - } - if cfirst >= nfirst && cfirst <= nlast && n.End() <= g.List[0].Slash { - // The printer will not include the comment if it starts past - // the node itself. Trick it into printing by overlapping the - // slash with the end of the statement. - g.List[0].Slash = n.End() - 1 - } - } - node := &printer.CommentedNode{n, lp.fnode.Comments} - lp.config.Fprint(&lp.output, lp.fset, node) -} - -func (lp *linePrinter) Visit(n ast.Node) (w ast.Visitor) { - if n == nil { - if lp.output.Len() == 0 { - lp.emit() - } - return nil - } - first := lp.fset.Position(n.Pos()).Line - last := lp.fset.Position(n.End()).Line - if first <= lp.line && last >= lp.line { - // Print the innermost statement containing the line. - if stmt, ok := n.(ast.Stmt); ok { - if _, ok := n.(*ast.BlockStmt); !ok { - lp.stmt = stmt - } - } - if first == lp.line && lp.emit() { - return nil - } - return lp - } - return nil -} - -func (lp *linePrinter) trim(n ast.Node) bool { - stmt, ok := n.(ast.Stmt) - if !ok { - return true - } - line := lp.fset.Position(n.Pos()).Line - if line != lp.line { - return false - } - switch stmt := stmt.(type) { - case *ast.IfStmt: - stmt.Body = lp.trimBlock(stmt.Body) - case *ast.SwitchStmt: - stmt.Body = lp.trimBlock(stmt.Body) - case *ast.TypeSwitchStmt: - stmt.Body = lp.trimBlock(stmt.Body) - case *ast.CaseClause: - stmt.Body = lp.trimList(stmt.Body) - case *ast.CommClause: - stmt.Body = lp.trimList(stmt.Body) - case *ast.BlockStmt: - stmt.List = lp.trimList(stmt.List) - } - return true -} - -func (lp *linePrinter) trimBlock(stmt *ast.BlockStmt) *ast.BlockStmt { - if !lp.trim(stmt) { - return lp.emptyBlock(stmt) - } - stmt.Rbrace = stmt.Lbrace - return stmt -} - -func (lp *linePrinter) trimList(stmts []ast.Stmt) []ast.Stmt { - for i := 0; i != len(stmts); i++ { - if !lp.trim(stmts[i]) { - stmts[i] = lp.emptyStmt(stmts[i]) - break - } - } - return stmts -} - -func (lp *linePrinter) emptyStmt(n ast.Node) *ast.ExprStmt { - return &ast.ExprStmt{&ast.Ellipsis{n.Pos(), nil}} -} - -func (lp *linePrinter) emptyBlock(n ast.Node) *ast.BlockStmt { - p := n.Pos() - return &ast.BlockStmt{p, []ast.Stmt{lp.emptyStmt(n)}, p} -} diff --git a/vendor/github.com/go-check/check/reporter.go b/vendor/github.com/go-check/check/reporter.go deleted file mode 100644 index fb04f76f64..0000000000 --- a/vendor/github.com/go-check/check/reporter.go +++ /dev/null @@ -1,88 +0,0 @@ -package check - -import ( - "fmt" - "io" - "sync" -) - -// ----------------------------------------------------------------------- -// Output writer manages atomic output writing according to settings. - -type outputWriter struct { - m sync.Mutex - writer io.Writer - wroteCallProblemLast bool - Stream bool - Verbose bool -} - -func newOutputWriter(writer io.Writer, stream, verbose bool) *outputWriter { - return &outputWriter{writer: writer, Stream: stream, Verbose: verbose} -} - -func (ow *outputWriter) Write(content []byte) (n int, err error) { - ow.m.Lock() - n, err = ow.writer.Write(content) - ow.m.Unlock() - return -} - -func (ow *outputWriter) WriteCallStarted(label string, c *C) { - if ow.Stream { - header := renderCallHeader(label, c, "", "\n") - ow.m.Lock() - ow.writer.Write([]byte(header)) - ow.m.Unlock() - } -} - -func (ow *outputWriter) WriteCallProblem(label string, c *C) { - var prefix string - if !ow.Stream { - prefix = "\n-----------------------------------" + - "-----------------------------------\n" - } - header := renderCallHeader(label, c, prefix, "\n\n") - ow.m.Lock() - ow.wroteCallProblemLast = true - ow.writer.Write([]byte(header)) - if !ow.Stream { - c.logb.WriteTo(ow.writer) - } - ow.m.Unlock() -} - -func (ow *outputWriter) WriteCallSuccess(label string, c *C) { - if ow.Stream || (ow.Verbose && c.kind == testKd) { - // TODO Use a buffer here. - var suffix string - if c.reason != "" { - suffix = " (" + c.reason + ")" - } - if c.status() == succeededSt { - suffix += "\t" + c.timerString() - } - suffix += "\n" - if ow.Stream { - suffix += "\n" - } - header := renderCallHeader(label, c, "", suffix) - ow.m.Lock() - // Resist temptation of using line as prefix above due to race. - if !ow.Stream && ow.wroteCallProblemLast { - header = "\n-----------------------------------" + - "-----------------------------------\n" + - header - } - ow.wroteCallProblemLast = false - ow.writer.Write([]byte(header)) - ow.m.Unlock() - } -} - -func renderCallHeader(label string, c *C, prefix, suffix string) string { - pc := c.method.PC() - return fmt.Sprintf("%s%s: %s: %s%s", prefix, label, niceFuncPath(pc), - niceFuncName(pc), suffix) -} diff --git a/vendor/github.com/go-check/check/run.go b/vendor/github.com/go-check/check/run.go deleted file mode 100644 index f72119656d..0000000000 --- a/vendor/github.com/go-check/check/run.go +++ /dev/null @@ -1,183 +0,0 @@ -package check - -import ( - "bufio" - "flag" - "fmt" - "os" - "testing" - "time" -) - -// ----------------------------------------------------------------------- -// Test suite registry. - -var allSuites []interface{} - -// Suite registers the given value as a test suite to be run. Any methods -// starting with the Test prefix in the given value will be considered as -// a test method. -func Suite(suite interface{}) interface{} { - allSuites = append(allSuites, suite) - return suite -} - -// ----------------------------------------------------------------------- -// Public running interface. - -var ( - oldFilterFlag = flag.String("gocheck.f", "", "Regular expression selecting which tests and/or suites to run") - oldVerboseFlag = flag.Bool("gocheck.v", false, "Verbose mode") - oldStreamFlag = flag.Bool("gocheck.vv", false, "Super verbose mode (disables output caching)") - oldBenchFlag = flag.Bool("gocheck.b", false, "Run benchmarks") - oldBenchTime = flag.Duration("gocheck.btime", 1*time.Second, "approximate run time for each benchmark") - oldListFlag = flag.Bool("gocheck.list", false, "List the names of all tests that will be run") - oldWorkFlag = flag.Bool("gocheck.work", false, "Display and do not remove the test working directory") - - newFilterFlag = flag.String("check.f", "", "Regular expression selecting which tests and/or suites to run") - newVerboseFlag = flag.Bool("check.v", false, "Verbose mode") - newStreamFlag = flag.Bool("check.vv", false, "Super verbose mode (disables output caching)") - newBenchFlag = flag.Bool("check.b", false, "Run benchmarks") - newBenchTime = flag.Duration("check.btime", 1*time.Second, "approximate run time for each benchmark") - newBenchMem = flag.Bool("check.bmem", false, "Report memory benchmarks") - newListFlag = flag.Bool("check.list", false, "List the names of all tests that will be run") - newWorkFlag = flag.Bool("check.work", false, "Display and do not remove the test working directory") - checkTimeout = flag.String("check.timeout", "", "Panic if test runs longer than specified duration") -) - -// TestingT runs all test suites registered with the Suite function, -// printing results to stdout, and reporting any failures back to -// the "testing" package. -func TestingT(testingT *testing.T) { - benchTime := *newBenchTime - if benchTime == 1*time.Second { - benchTime = *oldBenchTime - } - conf := &RunConf{ - Filter: *oldFilterFlag + *newFilterFlag, - Verbose: *oldVerboseFlag || *newVerboseFlag, - Stream: *oldStreamFlag || *newStreamFlag, - Benchmark: *oldBenchFlag || *newBenchFlag, - BenchmarkTime: benchTime, - BenchmarkMem: *newBenchMem, - KeepWorkDir: *oldWorkFlag || *newWorkFlag, - } - if *checkTimeout != "" { - timeout, err := time.ParseDuration(*checkTimeout) - if err != nil { - testingT.Fatalf("error parsing specified timeout flag: %v", err) - } - conf.CheckTimeout = timeout - } - if *oldListFlag || *newListFlag { - w := bufio.NewWriter(os.Stdout) - for _, name := range ListAll(conf) { - fmt.Fprintln(w, name) - } - w.Flush() - return - } - result := RunAll(conf) - println(result.String()) - if !result.Passed() { - testingT.Fail() - } -} - -// RunAll runs all test suites registered with the Suite function, using the -// provided run configuration. -func RunAll(runConf *RunConf) *Result { - result := Result{} - for _, suite := range allSuites { - result.Add(Run(suite, runConf)) - } - return &result -} - -// Run runs the provided test suite using the provided run configuration. -func Run(suite interface{}, runConf *RunConf) *Result { - runner := newSuiteRunner(suite, runConf) - return runner.run() -} - -// ListAll returns the names of all the test functions registered with the -// Suite function that will be run with the provided run configuration. -func ListAll(runConf *RunConf) []string { - var names []string - for _, suite := range allSuites { - names = append(names, List(suite, runConf)...) - } - return names -} - -// List returns the names of the test functions in the given -// suite that will be run with the provided run configuration. -func List(suite interface{}, runConf *RunConf) []string { - var names []string - runner := newSuiteRunner(suite, runConf) - for _, t := range runner.tests { - names = append(names, t.String()) - } - return names -} - -// ----------------------------------------------------------------------- -// Result methods. - -func (r *Result) Add(other *Result) { - r.Succeeded += other.Succeeded - r.Skipped += other.Skipped - r.Failed += other.Failed - r.Panicked += other.Panicked - r.FixturePanicked += other.FixturePanicked - r.ExpectedFailures += other.ExpectedFailures - r.Missed += other.Missed - if r.WorkDir != "" && other.WorkDir != "" { - r.WorkDir += ":" + other.WorkDir - } else if other.WorkDir != "" { - r.WorkDir = other.WorkDir - } -} - -func (r *Result) Passed() bool { - return (r.Failed == 0 && r.Panicked == 0 && - r.FixturePanicked == 0 && r.Missed == 0 && - r.RunError == nil) -} - -func (r *Result) String() string { - if r.RunError != nil { - return "ERROR: " + r.RunError.Error() - } - - var value string - if r.Failed == 0 && r.Panicked == 0 && r.FixturePanicked == 0 && - r.Missed == 0 { - value = "OK: " - } else { - value = "OOPS: " - } - value += fmt.Sprintf("%d passed", r.Succeeded) - if r.Skipped != 0 { - value += fmt.Sprintf(", %d skipped", r.Skipped) - } - if r.ExpectedFailures != 0 { - value += fmt.Sprintf(", %d expected failures", r.ExpectedFailures) - } - if r.Failed != 0 { - value += fmt.Sprintf(", %d FAILED", r.Failed) - } - if r.Panicked != 0 { - value += fmt.Sprintf(", %d PANICKED", r.Panicked) - } - if r.FixturePanicked != 0 { - value += fmt.Sprintf(", %d FIXTURE-PANICKED", r.FixturePanicked) - } - if r.Missed != 0 { - value += fmt.Sprintf(", %d MISSED", r.Missed) - } - if r.WorkDir != "" { - value += "\nWORK=" + r.WorkDir - } - return value -} diff --git a/vendor/github.com/opencontainers/selinux/LICENSE b/vendor/github.com/opencontainers/selinux/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/opencontainers/selinux/README.md b/vendor/github.com/opencontainers/selinux/README.md new file mode 100644 index 0000000000..043a929371 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/README.md @@ -0,0 +1,7 @@ +# selinux + +[![GoDoc](https://godoc.org/github.com/opencontainers/selinux?status.svg)](https://godoc.org/github.com/opencontainers/selinux) [![Go Report Card](https://goreportcard.com/badge/github.com/opencontainers/selinux)](https://goreportcard.com/report/github.com/opencontainers/selinux) [![Build Status](https://travis-ci.org/opencontainers/selinux.svg?branch=master)](https://travis-ci.org/opencontainers/selinux) + +Common SELinux package used across the container ecosystem. + +Please see the [godoc](https://godoc.org/github.com/opencontainers/selinux) for more information. diff --git a/vendor/github.com/opencontainers/runc/libcontainer/label/label.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label.go similarity index 97% rename from vendor/github.com/opencontainers/runc/libcontainer/label/label.go rename to vendor/github.com/opencontainers/selinux/go-selinux/label/label.go index fddec46334..6cfc5fded8 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/label/label.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label.go @@ -52,7 +52,7 @@ func ReserveLabel(label string) error { return nil } -func UnreserveLabel(label string) error { +func ReleaseLabel(label string) error { return nil } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/label/label_selinux.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go similarity index 82% rename from vendor/github.com/opencontainers/runc/libcontainer/label/label_selinux.go rename to vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go index f469131e97..569dcf0841 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/label/label_selinux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go @@ -6,7 +6,7 @@ import ( "fmt" "strings" - "github.com/opencontainers/runc/libcontainer/selinux" + "github.com/opencontainers/selinux/go-selinux" ) // Valid Label Options @@ -25,10 +25,10 @@ var ErrIncompatibleLabel = fmt.Errorf("Bad SELinux option z and Z can not be use // the labels. The labels returned will include a random MCS String, that is // guaranteed to be unique. func InitLabels(options []string) (string, string, error) { - if !selinux.SelinuxEnabled() { + if !selinux.GetEnabled() { return "", "", nil } - processLabel, mountLabel := selinux.GetLxcContexts() + processLabel, mountLabel := selinux.ContainerLabels() if processLabel != "" { pcon := selinux.NewContext(processLabel) mcon := selinux.NewContext(mountLabel) @@ -55,8 +55,8 @@ func InitLabels(options []string) (string, string, error) { return processLabel, mountLabel, nil } -func GetROMountLabel() string { - return selinux.GetROFileLabel() +func ROMountLabel() string { + return selinux.ROFileLabel() } // DEPRECATED: The GenLabels function is only to be used during the transition to the official API. @@ -88,33 +88,33 @@ func SetProcessLabel(processLabel string) error { if processLabel == "" { return nil } - return selinux.Setexeccon(processLabel) + return selinux.SetExecLabel(processLabel) } -// GetProcessLabel returns the process label that the kernel will assign +// ProcessLabel returns the process label that the kernel will assign // to the next program executed by the current process. If "" is returned // this indicates that the default labeling will happen for the process. -func GetProcessLabel() (string, error) { - return selinux.Getexeccon() +func ProcessLabel() (string, error) { + return selinux.ExecLabel() } // GetFileLabel returns the label for specified path -func GetFileLabel(path string) (string, error) { - return selinux.Getfilecon(path) +func FileLabel(path string) (string, error) { + return selinux.FileLabel(path) } // SetFileLabel modifies the "path" label to the specified file label func SetFileLabel(path string, fileLabel string) error { - if selinux.SelinuxEnabled() && fileLabel != "" { - return selinux.Setfilecon(path, fileLabel) + if selinux.GetEnabled() && fileLabel != "" { + return selinux.SetFileLabel(path, fileLabel) } return nil } // SetFileCreateLabel tells the kernel the label for all files to be created func SetFileCreateLabel(fileLabel string) error { - if selinux.SelinuxEnabled() { - return selinux.Setfscreatecon(fileLabel) + if selinux.GetEnabled() { + return selinux.SetFSCreateLabel(fileLabel) } return nil } @@ -123,7 +123,7 @@ func SetFileCreateLabel(fileLabel string) error { // It changes the MCS label to s0 if shared is true. // This will allow all containers to share the content. func Relabel(path string, fileLabel string, shared bool) error { - if !selinux.SelinuxEnabled() { + if !selinux.GetEnabled() { return nil } @@ -147,14 +147,14 @@ func Relabel(path string, fileLabel string, shared bool) error { return nil } -// GetPidLabel will return the label of the process running with the specified pid -func GetPidLabel(pid int) (string, error) { - return selinux.Getpidcon(pid) +// PidLabel will return the label of the process running with the specified pid +func PidLabel(pid int) (string, error) { + return selinux.PidLabel(pid) } // Init initialises the labeling system func Init() { - selinux.SelinuxEnabled() + selinux.GetEnabled() } // ReserveLabel will record the fact that the MCS label has already been used. @@ -165,15 +165,15 @@ func ReserveLabel(label string) error { return nil } -// UnreserveLabel will remove the reservation of the MCS label. +// ReleaseLabel will remove the reservation of the MCS label. // This will allow InitLabels to use the MCS label in a newly created // containers -func UnreserveLabel(label string) error { - selinux.FreeLxcContexts(label) +func ReleaseLabel(label string) error { + selinux.ReleaseLabel(label) return nil } -// DupSecOpt takes an process label and returns security options that +// DupSecOpt takes a process label and returns security options that // can be used to set duplicate labels on future container processes func DupSecOpt(src string) []string { return selinux.DupSecOpt(src) diff --git a/vendor/github.com/opencontainers/runc/libcontainer/selinux/selinux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go similarity index 60% rename from vendor/github.com/opencontainers/runc/libcontainer/selinux/selinux.go rename to vendor/github.com/opencontainers/selinux/go-selinux/selinux.go index fcaba1d29e..4cf2c45de7 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/selinux/selinux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go @@ -15,13 +15,14 @@ import ( "strings" "sync" "syscall" - - "github.com/opencontainers/runc/libcontainer/system" ) const ( - Enforcing = 1 - Permissive = 0 + // Enforcing constant indicate SELinux is in enforcing mode + Enforcing = 1 + // Permissive constant to indicate SELinux is in permissive mode + Permissive = 0 + // Disabled constant to indicate SELinux is disabled Disabled = -1 selinuxDir = "/etc/selinux/" selinuxConfig = selinuxDir + "config" @@ -32,33 +33,74 @@ const ( stRdOnly = 0x01 ) +type selinuxState struct { + enabledSet bool + enabled bool + selinuxfsSet bool + selinuxfs string + mcsList map[string]bool + sync.Mutex +} + var ( - assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`) - mcsList = make(map[string]bool) - mcsLock sync.Mutex - selinuxfs = "unknown" - selinuxEnabled = false // Stores whether selinux is currently enabled - selinuxEnabledChecked = false // Stores whether selinux enablement has been checked or established yet + assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`) + state = selinuxState{ + mcsList: make(map[string]bool), + } ) -type SELinuxContext map[string]string +// Context is a representation of the SELinux label broken into 4 parts +type Context map[string]string + +func (s *selinuxState) setEnable(enabled bool) bool { + s.Lock() + defer s.Unlock() + s.enabledSet = true + s.enabled = enabled + return s.enabled +} + +func (s *selinuxState) getEnabled() bool { + s.Lock() + enabled := s.enabled + enabledSet := s.enabledSet + s.Unlock() + if enabledSet { + return enabled + } + + enabled = false + if fs := getSelinuxMountPoint(); fs != "" { + if con, _ := CurrentLabel(); con != "kernel" { + enabled = true + } + } + return s.setEnable(enabled) +} // SetDisabled disables selinux support for the package func SetDisabled() { - selinuxEnabled, selinuxEnabledChecked = false, true + state.setEnable(false) } -// getSelinuxMountPoint returns the path to the mountpoint of an selinuxfs -// filesystem or an empty string if no mountpoint is found. Selinuxfs is -// a proc-like pseudo-filesystem that exposes the selinux policy API to -// processes. The existence of an selinuxfs mount is used to determine -// whether selinux is currently enabled or not. -func getSelinuxMountPoint() string { - if selinuxfs != "unknown" { +func (s *selinuxState) setSELinuxfs(selinuxfs string) string { + s.Lock() + defer s.Unlock() + s.selinuxfsSet = true + s.selinuxfs = selinuxfs + return s.selinuxfs +} + +func (s *selinuxState) getSELinuxfs() string { + s.Lock() + selinuxfs := s.selinuxfs + selinuxfsSet := s.selinuxfsSet + s.Unlock() + if selinuxfsSet { return selinuxfs } - selinuxfs = "" + selinuxfs = "" f, err := os.Open("/proc/self/mountinfo") if err != nil { return selinuxfs @@ -91,21 +133,21 @@ func getSelinuxMountPoint() string { selinuxfs = "" } } - return selinuxfs + return s.setSELinuxfs(selinuxfs) } -// SelinuxEnabled returns whether selinux is currently enabled. -func SelinuxEnabled() bool { - if selinuxEnabledChecked { - return selinuxEnabled - } - selinuxEnabledChecked = true - if fs := getSelinuxMountPoint(); fs != "" { - if con, _ := Getcon(); con != "kernel" { - selinuxEnabled = true - } - } - return selinuxEnabled +// getSelinuxMountPoint returns the path to the mountpoint of an selinuxfs +// filesystem or an empty string if no mountpoint is found. Selinuxfs is +// a proc-like pseudo-filesystem that exposes the selinux policy API to +// processes. The existence of an selinuxfs mount is used to determine +// whether selinux is currently enabled or not. +func getSelinuxMountPoint() string { + return state.getSELinuxfs() +} + +// GetEnabled returns whether selinux is currently enabled. +func GetEnabled() bool { + return state.getEnabled() } func readConfig(target string) (value string) { @@ -166,43 +208,55 @@ func readCon(name string) (string, error) { return val, err } -// Setfilecon sets the SELinux label for this path or returns an error. -func Setfilecon(path string, scon string) error { - return system.Lsetxattr(path, xattrNameSelinux, []byte(scon), 0) +// SetFileLabel sets the SELinux label for this path or returns an error. +func SetFileLabel(path string, label string) error { + return lsetxattr(path, xattrNameSelinux, []byte(label), 0) } -// Getfilecon returns the SELinux label for this path or returns an error. -func Getfilecon(path string) (string, error) { - con, err := system.Lgetxattr(path, xattrNameSelinux) +// Filecon returns the SELinux label for this path or returns an error. +func FileLabel(path string) (string, error) { + label, err := lgetxattr(path, xattrNameSelinux) if err != nil { return "", err } // Trim the NUL byte at the end of the byte buffer, if present. - if len(con) > 0 && con[len(con)-1] == '\x00' { - con = con[:len(con)-1] + if len(label) > 0 && label[len(label)-1] == '\x00' { + label = label[:len(label)-1] } - return string(con), nil + return string(label), nil } -func Setfscreatecon(scon string) error { - return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()), scon) +/* +SetFSCreateLabel tells kernel the label to create all file system objects +created by this task. Setting label="" to return to default. +*/ +func SetFSCreateLabel(label string) error { + return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid()), label) } -func Getfscreatecon() (string, error) { +/* +FSCreateLabel returns the default label the kernel which the kernel is using +for file system objects created by this task. "" indicates default. +*/ +func FSCreateLabel() (string, error) { return readCon(fmt.Sprintf("/proc/self/task/%d/attr/fscreate", syscall.Gettid())) } -// Getcon returns the SELinux label of the current process thread, or an error. -func Getcon() (string, error) { +// CurrentLabel returns the SELinux label of the current process thread, or an error. +func CurrentLabel() (string, error) { return readCon(fmt.Sprintf("/proc/self/task/%d/attr/current", syscall.Gettid())) } -// Getpidcon returns the SELinux label of the given pid, or an error. -func Getpidcon(pid int) (string, error) { +// PidLabel returns the SELinux label of the given pid, or an error. +func PidLabel(pid int) (string, error) { return readCon(fmt.Sprintf("/proc/%d/attr/current", pid)) } -func Getexeccon() (string, error) { +/* +ExecLabel returns the SELinux label that the kernel will use for any programs +that are executed by the current process thread, or an error. +*/ +func ExecLabel() (string, error) { return readCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid())) } @@ -221,19 +275,25 @@ func writeCon(name string, val string) error { return err } -func Setexeccon(scon string) error { - return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()), scon) +/* +SetExecLabel sets the SELinux label that the kernel will use for any programs +that are executed by the current process thread, or an error. +*/ +func SetExecLabel(label string) error { + return writeCon(fmt.Sprintf("/proc/self/task/%d/attr/exec", syscall.Gettid()), label) } -func (c SELinuxContext) Get() string { +// Get returns the Context as a string +func (c Context) Get() string { return fmt.Sprintf("%s:%s:%s:%s", c["user"], c["role"], c["type"], c["level"]) } -func NewContext(scon string) SELinuxContext { - c := make(SELinuxContext) +// NewContext creates a new Context struct from the specified label +func NewContext(label string) Context { + c := make(Context) - if len(scon) != 0 { - con := strings.SplitN(scon, ":", 4) + if len(label) != 0 { + con := strings.SplitN(label, ":", 4) c["user"] = con[0] c["role"] = con[1] c["type"] = con[2] @@ -242,9 +302,10 @@ func NewContext(scon string) SELinuxContext { return c } -func ReserveLabel(scon string) { - if len(scon) != 0 { - con := strings.SplitN(scon, ":", 4) +// ReserveLabel reserves the MLS/MCS level component of the specified label +func ReserveLabel(label string) { + if len(label) != 0 { + con := strings.SplitN(label, ":", 4) mcsAdd(con[3]) } } @@ -253,7 +314,8 @@ func selinuxEnforcePath() string { return fmt.Sprintf("%s/enforce", selinuxPath) } -func SelinuxGetEnforce() int { +// EnforceMode returns the current SELinux mode Enforcing, Permissive, Disabled +func EnforceMode() int { var enforce int enforceS, err := readCon(selinuxEnforcePath()) @@ -268,11 +330,20 @@ func SelinuxGetEnforce() int { return enforce } -func SelinuxSetEnforce(mode int) error { +/* +SetEnforce sets the current SELinux mode Enforcing, Permissive. +Disabled is not valid, since this needs to be set at boot time. +*/ +func SetEnforceMode(mode int) error { return writeCon(selinuxEnforcePath(), fmt.Sprintf("%d", mode)) } -func SelinuxGetEnforceMode() int { +/* +DefaultEnforceMode returns the systems default SELinux mode Enforcing, +Permissive or Disabled. Note this is is just the default at boot time. +EnforceMode tells you the systems current mode. +*/ +func DefaultEnforceMode() int { switch readConfig(selinuxTag) { case "enforcing": return Enforcing @@ -283,22 +354,22 @@ func SelinuxGetEnforceMode() int { } func mcsAdd(mcs string) error { - mcsLock.Lock() - defer mcsLock.Unlock() - if mcsList[mcs] { + state.Lock() + defer state.Unlock() + if state.mcsList[mcs] { return fmt.Errorf("MCS Label already exists") } - mcsList[mcs] = true + state.mcsList[mcs] = true return nil } func mcsDelete(mcs string) { - mcsLock.Lock() - mcsList[mcs] = false - mcsLock.Unlock() + state.Lock() + defer state.Unlock() + state.mcsList[mcs] = false } -func IntToMcs(id int, catRange uint32) string { +func intToMcs(id int, catRange uint32) string { var ( SETSIZE = int(catRange) TIER = SETSIZE @@ -334,9 +405,7 @@ func uniqMcs(catRange uint32) string { continue } else { if c1 > c2 { - t := c1 - c1 = c2 - c2 = t + c1, c2 = c2, c1 } } mcs = fmt.Sprintf("s0:c%d,c%d", c1, c2) @@ -348,26 +417,35 @@ func uniqMcs(catRange uint32) string { return mcs } -func FreeLxcContexts(scon string) { - if len(scon) != 0 { - con := strings.SplitN(scon, ":", 4) +/* +ReleaseLabel will unreserve the MLS/MCS Level field of the specified label. +Allowing it to be used by another process. +*/ +func ReleaseLabel(label string) { + if len(label) != 0 { + con := strings.SplitN(label, ":", 4) mcsDelete(con[3]) } } var roFileLabel string -func GetROFileLabel() (fileLabel string) { +// ROFileLabel returns the specified SELinux readonly file label +func ROFileLabel() (fileLabel string) { return roFileLabel } -func GetLxcContexts() (processLabel string, fileLabel string) { +/* +ContainerLabels returns an allocated processLabel and fileLabel to be used for +container labeling by the calling process. +*/ +func ContainerLabels() (processLabel string, fileLabel string) { var ( val, key string bufin *bufio.Reader ) - if !SelinuxEnabled() { + if !GetEnabled() { return "", "" } lxcPath := fmt.Sprintf("%s/contexts/lxc_contexts", getSELinuxPolicyRoot()) @@ -419,7 +497,6 @@ func GetLxcContexts() (processLabel string, fileLabel string) { roFileLabel = fileLabel } exit: - // mcs := IntToMcs(os.Getpid(), 1024) mcs := uniqMcs(1024) scon := NewContext(processLabel) scon["level"] = mcs @@ -430,10 +507,15 @@ exit: return processLabel, fileLabel } +// SecurityCheckContext validates that the SELinux label is understood by the kernel func SecurityCheckContext(val string) error { return writeCon(fmt.Sprintf("%s.context", selinuxPath), val) } +/* +CopyLevel returns a label with the MLS/MCS level from src label replaces on +the dest label. +*/ func CopyLevel(src, dest string) (string, error) { if src == "" { return "", nil @@ -458,31 +540,31 @@ func badPrefix(fpath string) error { for _, prefix := range badprefixes { if fpath == prefix || strings.HasPrefix(fpath, fmt.Sprintf("%s/", prefix)) { - return fmt.Errorf("Relabeling content in %s is not allowed.", prefix) + return fmt.Errorf("relabeling content in %s is not allowed", prefix) } } return nil } -// Chcon changes the fpath file object to the SELinux label scon. +// Chcon changes the fpath file object to the SELinux label label. // If the fpath is a directory and recurse is true Chcon will walk the // directory tree setting the label -func Chcon(fpath string, scon string, recurse bool) error { - if scon == "" { +func Chcon(fpath string, label string, recurse bool) error { + if label == "" { return nil } if err := badPrefix(fpath); err != nil { return err } callback := func(p string, info os.FileInfo, err error) error { - return Setfilecon(p, scon) + return SetFileLabel(p, label) } if recurse { return filepath.Walk(fpath, callback) } - return Setfilecon(fpath, scon) + return SetFileLabel(fpath, label) } // DupSecOpt takes an SELinux process label and returns security options that @@ -498,14 +580,14 @@ func DupSecOpt(src string) []string { con["level"] == "" { return nil } - return []string{"label=user:" + con["user"], - "label=role:" + con["role"], - "label=type:" + con["type"], - "label=level:" + con["level"]} + return []string{"user:" + con["user"], + "role:" + con["role"], + "type:" + con["type"], + "level:" + con["level"]} } // DisableSecOpt returns a security opt that can be used to disabling SELinux // labeling support for future container processes func DisableSecOpt() []string { - return []string{"label=disable"} + return []string{"disable"} } diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go b/vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go new file mode 100644 index 0000000000..7f2ef85049 --- /dev/null +++ b/vendor/github.com/opencontainers/selinux/go-selinux/xattrs.go @@ -0,0 +1,78 @@ +// +build linux + +package selinux + +import ( + "syscall" + "unsafe" +) + +var _zero uintptr + +// Returns a []byte slice if the xattr is set and nil otherwise +// Requires path and its attribute as arguments +func lgetxattr(path string, attr string) ([]byte, error) { + var sz int + pathBytes, err := syscall.BytePtrFromString(path) + if err != nil { + return nil, err + } + attrBytes, err := syscall.BytePtrFromString(attr) + if err != nil { + return nil, err + } + + // Start with a 128 length byte array + sz = 128 + dest := make([]byte, sz) + destBytes := unsafe.Pointer(&dest[0]) + _sz, _, errno := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0) + + switch { + case errno == syscall.ENODATA: + return nil, errno + case errno == syscall.ENOTSUP: + return nil, errno + case errno == syscall.ERANGE: + // 128 byte array might just not be good enough, + // A dummy buffer is used ``uintptr(0)`` to get real size + // of the xattrs on disk + _sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(unsafe.Pointer(nil)), uintptr(0), 0, 0) + sz = int(_sz) + if sz < 0 { + return nil, errno + } + dest = make([]byte, sz) + destBytes := unsafe.Pointer(&dest[0]) + _sz, _, errno = syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(destBytes), uintptr(len(dest)), 0, 0) + if errno != 0 { + return nil, errno + } + case errno != 0: + return nil, errno + } + sz = int(_sz) + return dest[:sz], nil +} + +func lsetxattr(path string, attr string, data []byte, flags int) error { + pathBytes, err := syscall.BytePtrFromString(path) + if err != nil { + return err + } + attrBytes, err := syscall.BytePtrFromString(attr) + if err != nil { + return err + } + var dataBytes unsafe.Pointer + if len(data) > 0 { + dataBytes = unsafe.Pointer(&data[0]) + } else { + dataBytes = unsafe.Pointer(&_zero) + } + _, _, errno := syscall.Syscall6(syscall.SYS_LSETXATTR, uintptr(unsafe.Pointer(pathBytes)), uintptr(unsafe.Pointer(attrBytes)), uintptr(dataBytes), uintptr(len(data)), uintptr(flags), 0) + if errno != 0 { + return errno + } + return nil +} From c3648a9c9400d45524cc71b8fca4085b192c626f Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Thu, 6 Apr 2017 13:33:56 +0100 Subject: [PATCH 21/21] Add `docker build --iidfile=FILE` This is synonymous with `docker run --cidfile=FILE` and writes the digest of the newly built image to the named file. This is intended to be used by build systems which want to avoid tagging (perhaps because they are in CI or otherwise want to avoid fixed names which can clash) by enabling e.g. Makefile constructs like: image.id: Dockerfile docker build --iidfile=image.id . do-some-more-stuff: image.id do-stuff-with = v1.29 the caller will now see a `JSONMessage` with the `Aux` field containing a `types.BuildResult` in the output stream for each image/layer produced during the build, with the final one being the end product. Having all of the intermediate images might be interesting in some cases. In silent mode (with `-q`) there is no change, on success the only output will be the resulting image digest as it was previosuly. There was no wrapper to just output an Aux section without enclosing it in a Progress, so add one here. Added some tests to integration cli tests. Signed-off-by: Ian Campbell --- cli/command/image/build.go | 33 +++++++++++++++++-- vendor.conf | 2 +- .../docker/docker/api/types/types.go | 5 +++ .../docker/pkg/jsonmessage/jsonmessage.go | 2 +- .../pkg/streamformatter/streamformatter.go | 25 ++++++++++++++ vendor/github.com/docker/docker/vendor.conf | 4 +-- 6 files changed, 65 insertions(+), 6 deletions(-) diff --git a/cli/command/image/build.go b/cli/command/image/build.go index 5ab3829bea..cfb4fb8829 100644 --- a/cli/command/image/build.go +++ b/cli/command/image/build.go @@ -4,8 +4,10 @@ import ( "archive/tar" "bufio" "bytes" + "encoding/json" "fmt" "io" + "io/ioutil" "os" "regexp" "runtime" @@ -59,6 +61,7 @@ type buildOptions struct { networkMode string squash bool target string + imageIDFile string } // dockerfileFromStdin returns true when the user specified that the Dockerfile @@ -123,6 +126,7 @@ func NewBuildCommand(dockerCli *command.DockerCli) *cobra.Command { flags.SetAnnotation("network", "version", []string{"1.25"}) flags.Var(&options.extraHosts, "add-host", "Add a custom host-to-IP mapping (host:ip)") flags.StringVar(&options.target, "target", "", "Set the target build stage to build.") + flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file") command.AddTrustVerificationFlags(flags) @@ -176,6 +180,12 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error { progBuff = bytes.NewBuffer(nil) buildBuff = bytes.NewBuffer(nil) } + if options.imageIDFile != "" { + // Avoid leaving a stale file if we eventually fail + if err := os.Remove(options.imageIDFile); err != nil && !os.IsNotExist(err) { + return errors.Wrap(err, "Removing image ID file") + } + } switch { case options.contextFromStdin(): @@ -301,7 +311,17 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error { } defer response.Body.Close() - err = jsonmessage.DisplayJSONMessagesStream(response.Body, buildBuff, dockerCli.Out().FD(), dockerCli.Out().IsTerminal(), nil) + imageID := "" + aux := func(auxJSON *json.RawMessage) { + var result types.BuildResult + if err := json.Unmarshal(*auxJSON, &result); err != nil { + fmt.Fprintf(dockerCli.Err(), "Failed to parse aux message: %s", err) + } else { + imageID = result.ID + } + } + + err = jsonmessage.DisplayJSONMessagesStream(response.Body, buildBuff, dockerCli.Out().FD(), dockerCli.Out().IsTerminal(), aux) if err != nil { if jerr, ok := err.(*jsonmessage.JSONError); ok { // If no error code is set, default to 1 @@ -329,9 +349,18 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error { // Everything worked so if -q was provided the output from the daemon // should be just the image ID and we'll print that to stdout. if options.quiet { - fmt.Fprintf(dockerCli.Out(), "%s", buildBuff) + imageID = fmt.Sprintf("%s", buildBuff) + fmt.Fprintf(dockerCli.Out(), imageID) } + if options.imageIDFile != "" { + if imageID == "" { + return errors.Errorf("Server did not provide an image ID. Cannot write %s", options.imageIDFile) + } + if err := ioutil.WriteFile(options.imageIDFile, []byte(imageID), 0666); err != nil { + return err + } + } if command.IsTrusted() { // Since the build was successful, now we must tag any of the resolved // images from the above Dockerfile rewrite. diff --git a/vendor.conf b/vendor.conf index b478cd8023..1d4e94439d 100644 --- a/vendor.conf +++ b/vendor.conf @@ -6,7 +6,7 @@ github.com/agl/ed25519 d2b94fd789ea21d12fac1a4443dd3a3f79cda72c github.com/coreos/etcd 824277cb3a577a0e8c829ca9ec557b973fe06d20 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621 -github.com/docker/docker bf5cf84534f1eac9805c6f1bffff341358806b57 +github.com/docker/docker d624f9a7b00419179eb0e7e81f2894dde0752873 github.com/docker/docker-credential-helpers v0.5.0 github.com/docker/go d30aec9fd63c35133f8f79c3412ad91a3b08be06 github.com/docker/go-connections e15c02316c12de00874640cd76311849de2aeed5 diff --git a/vendor/github.com/docker/docker/api/types/types.go b/vendor/github.com/docker/docker/api/types/types.go index 9493bd95e2..75aaab157d 100644 --- a/vendor/github.com/docker/docker/api/types/types.go +++ b/vendor/github.com/docker/docker/api/types/types.go @@ -530,3 +530,8 @@ type PushResult struct { Digest string Size int } + +// BuildResult contains the image id of a successful build +type BuildResult struct { + ID string +} diff --git a/vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go b/vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go index c3b1371cde..2b8e98c429 100644 --- a/vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go +++ b/vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go @@ -109,7 +109,7 @@ type JSONMessage struct { TimeNano int64 `json:"timeNano,omitempty"` Error *JSONError `json:"errorDetail,omitempty"` ErrorMessage string `json:"error,omitempty"` //deprecated - // Aux contains out-of-band data, such as digests for push signing. + // Aux contains out-of-band data, such as digests for push signing and image id after building. Aux *json.RawMessage `json:"aux,omitempty"` } diff --git a/vendor/github.com/docker/docker/pkg/streamformatter/streamformatter.go b/vendor/github.com/docker/docker/pkg/streamformatter/streamformatter.go index fa79828172..48ba65503c 100644 --- a/vendor/github.com/docker/docker/pkg/streamformatter/streamformatter.go +++ b/vendor/github.com/docker/docker/pkg/streamformatter/streamformatter.go @@ -132,3 +132,28 @@ func (out *progressOutput) WriteProgress(prog progress.Progress) error { return nil } + +// AuxFormatter is a streamFormatter that writes aux progress messages +type AuxFormatter struct { + io.Writer +} + +// Emit emits the given interface as an aux progress message +func (sf *AuxFormatter) Emit(aux interface{}) error { + auxJSONBytes, err := json.Marshal(aux) + if err != nil { + return err + } + auxJSON := new(json.RawMessage) + *auxJSON = auxJSONBytes + msgJSON, err := json.Marshal(&jsonmessage.JSONMessage{Aux: auxJSON}) + if err != nil { + return err + } + msgJSON = appendNewline(msgJSON) + n, err := sf.Writer.Write(msgJSON) + if n != len(msgJSON) { + return io.ErrShortWrite + } + return err +} diff --git a/vendor/github.com/docker/docker/vendor.conf b/vendor/github.com/docker/docker/vendor.conf index 021c45b77d..23512ce23d 100644 --- a/vendor/github.com/docker/docker/vendor.conf +++ b/vendor/github.com/docker/docker/vendor.conf @@ -41,7 +41,7 @@ github.com/vishvananda/netlink 1e86b2bee5b6a7d377e4c02bb7f98209d6a7297c github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060 github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374 github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d -github.com/coreos/etcd 824277cb3a577a0e8c829ca9ec557b973fe06d20 +github.com/coreos/etcd ea5389a79f40206170582c1ea076191b8622cb8e https://github.com/aaronlehmann/etcd # for https://github.com/coreos/etcd/pull/7830 github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065 github.com/hashicorp/consul v0.5.2 github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904 @@ -108,7 +108,7 @@ github.com/docker/containerd 9048e5e50717ea4497b757314bad98ea3763c145 github.com/tonistiigi/fifo 1405643975692217d6720f8b54aeee1bf2cd5cf4 # cluster -github.com/docker/swarmkit 61a92e8ec074df5769decda985df4a3ab43c77eb +github.com/docker/swarmkit 8f053c2030ebfc90f19f241fb7880e95b9761b7a github.com/gogo/protobuf 8d70fb3182befc465c4a1eac8ad4d38ff49778e2 github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a github.com/google/certificate-transparency d90e65c3a07988180c5b1ece71791c0b6506826e