From 0e3e1012acbbc77dfc61133e94df792a72519e49 Mon Sep 17 00:00:00 2001 From: Vincent Demeester Date: Thu, 31 May 2018 13:38:58 +0200 Subject: [PATCH 1/4] Allow `x-*` extension on 3rd level objects As for top-level key, any 3rd-level key which starts with `x-` will be ignored by compose. This allows for users to: * include additional metadata in their compose files * create YAML anchor objects that can be re-used in other parts of the config This matches a similar feature in the swagger spec definition: https://swagger.io/specification/#specificationExtensions This means a composefile like the following is valid ``` verison: "3.7" services: foo: image: foo/bar x-foo: bar network: bar: x-bar: baz ``` It concerns services, volumes, networks, configs and secrets. Signed-off-by: Vincent Demeester Upstream-commit: 6bcfa8806c12637d7956ef8ffc4e3020a0cd1be7 Component: cli --- components/cli/cli/compose/schema/bindata.go | 72 +++++++++---------- .../schema/data/config_schema_v3.7.json | 5 ++ .../cli/cli/compose/schema/schema_test.go | 33 +++++++++ 3 files changed, 74 insertions(+), 36 deletions(-) diff --git a/components/cli/cli/compose/schema/bindata.go b/components/cli/cli/compose/schema/bindata.go index 9649d03070..e56e243201 100644 --- a/components/cli/cli/compose/schema/bindata.go +++ b/components/cli/cli/compose/schema/bindata.go @@ -467,44 +467,44 @@ DoTuq9lAU9Q4O1xV/59X/w8AAP//zRo7vm9CAAA= "/data/config_schema_v3.7.json": { local: "data/config_schema_v3.7.json", - size: 17540, + size: 17740, modtime: 1518458244, compressed: ` -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 `, }, diff --git a/components/cli/cli/compose/schema/data/config_schema_v3.7.json b/components/cli/cli/compose/schema/data/config_schema_v3.7.json index 4c3d24dcd2..05566cf819 100644 --- a/components/cli/cli/compose/schema/data/config_schema_v3.7.json +++ b/components/cli/cli/compose/schema/data/config_schema_v3.7.json @@ -319,6 +319,7 @@ }, "working_dir": {"type": "string"} }, + "patternProperties": {"^x-": {}}, "additionalProperties": false }, @@ -489,6 +490,7 @@ "attachable": {"type": "boolean"}, "labels": {"$ref": "#/definitions/list_or_dict"} }, + "patternProperties": {"^x-": {}}, "additionalProperties": false }, @@ -513,6 +515,7 @@ }, "labels": {"$ref": "#/definitions/list_or_dict"} }, + "patternProperties": {"^x-": {}}, "additionalProperties": false }, @@ -530,6 +533,7 @@ }, "labels": {"$ref": "#/definitions/list_or_dict"} }, + "patternProperties": {"^x-": {}}, "additionalProperties": false }, @@ -547,6 +551,7 @@ }, "labels": {"$ref": "#/definitions/list_or_dict"} }, + "patternProperties": {"^x-": {}}, "additionalProperties": false }, diff --git a/components/cli/cli/compose/schema/schema_test.go b/components/cli/cli/compose/schema/schema_test.go index c029f2f725..17ffb203f6 100644 --- a/components/cli/cli/compose/schema/schema_test.go +++ b/components/cli/cli/compose/schema/schema_test.go @@ -45,6 +45,39 @@ func TestValidateAllowsXTopLevelFields(t *testing.T) { assert.NilError(t, err) } +func TestValidateAllowsXFields(t *testing.T) { + config := dict{ + "version": "3.7", + "services": dict{ + "bar": dict{ + "x-extra-stuff": dict{}, + }, + }, + "volumes": dict{ + "bar": dict{ + "x-extra-stuff": dict{}, + }, + }, + "networks": dict{ + "bar": dict{ + "x-extra-stuff": dict{}, + }, + }, + "configs": dict{ + "bar": dict{ + "x-extra-stuff": dict{}, + }, + }, + "secrets": dict{ + "bar": dict{ + "x-extra-stuff": dict{}, + }, + }, + } + err := Validate(config, "3.7") + assert.NilError(t, err) +} + func TestValidateSecretConfigNames(t *testing.T) { config := dict{ "version": "3.5", From ee64293c176b9fcfc045dcb88e5a78f86b9abf90 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 30 May 2018 23:14:59 +0200 Subject: [PATCH 2/4] Mark checkpoint feature as Linux-only, and homogenize error messages This patch adds annotations to mark the checkpoint commands as Linux only, which hides them if the daemon is running a non-matching operating-system type; Before: docker Usage: docker COMMAND A self-sufficient runtime for containers ... Management Commands: config Manage Docker configs container Manage containers image Manage images After: docker Usage: docker COMMAND A self-sufficient runtime for containers ... Management Commands: checkpoint Manage checkpoints config Manage Docker configs container Manage containers image Manage images This change also prints errors when attempting to use checkpoint commands or flags if the feature is not supported by the Daemon's operating system; $ docker checkpoint --help docker checkpoint is only supported on a Docker daemon running on linux, but the Docker daemon is running on windows $ docker checkpoint create --help docker checkpoint create is only supported on a Docker daemon running on linux, but the Docker daemon is running on windows $ docker checkpoint ls --help docker checkpoint ls is only supported on a Docker daemon running on linux, but the Docker daemon is running on windows $ docker checkpoint rm --help docker checkpoint rm is only supported on a Docker daemon running on linux, but the Docker daemon is running on windows $ docker container start --checkpoint=foo mycontainer "--checkpoint" requires the Docker daemon to run on linux, but the Docker daemon is running on windows $ docker container start --checkpoint-dir=/foo/bar mycontainer "--checkpoint-dir" requires the Docker daemon to run on linux, but the Docker daemon is running on windows Signed-off-by: Sebastiaan van Stijn Upstream-commit: 1df6b68111cff2ba69d9b132b99c69d0b6514772 Component: cli --- components/cli/cli/command/checkpoint/cmd.go | 14 +++++++++----- components/cli/cli/command/container/start.go | 2 ++ components/cli/cmd/docker/docker.go | 13 ++++++++++--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/components/cli/cli/command/checkpoint/cmd.go b/components/cli/cli/command/checkpoint/cmd.go index 097d4dbf90..2a698e74e5 100644 --- a/components/cli/cli/command/checkpoint/cmd.go +++ b/components/cli/cli/command/checkpoint/cmd.go @@ -9,11 +9,15 @@ import ( // NewCheckpointCommand returns the `checkpoint` subcommand (only in experimental) func NewCheckpointCommand(dockerCli command.Cli) *cobra.Command { cmd := &cobra.Command{ - Use: "checkpoint", - Short: "Manage checkpoints", - Args: cli.NoArgs, - RunE: command.ShowHelp(dockerCli.Err()), - Annotations: map[string]string{"experimental": "", "version": "1.25"}, + Use: "checkpoint", + Short: "Manage checkpoints", + Args: cli.NoArgs, + RunE: command.ShowHelp(dockerCli.Err()), + Annotations: map[string]string{ + "experimental": "", + "ostype": "linux", + "version": "1.25", + }, } cmd.AddCommand( newCreateCommand(dockerCli), diff --git a/components/cli/cli/command/container/start.go b/components/cli/cli/command/container/start.go index 243097c0cd..e388302841 100644 --- a/components/cli/cli/command/container/start.go +++ b/components/cli/cli/command/container/start.go @@ -47,8 +47,10 @@ func NewStartCommand(dockerCli command.Cli) *cobra.Command { flags.StringVar(&opts.checkpoint, "checkpoint", "", "Restore from this checkpoint") flags.SetAnnotation("checkpoint", "experimental", nil) + flags.SetAnnotation("checkpoint", "ostype", []string{"linux"}) flags.StringVar(&opts.checkpointDir, "checkpoint-dir", "", "Use a custom checkpoint storage directory") flags.SetAnnotation("checkpoint-dir", "experimental", nil) + flags.SetAnnotation("checkpoint-dir", "ostype", []string{"linux"}) return cmd } diff --git a/components/cli/cmd/docker/docker.go b/components/cli/cmd/docker/docker.go index f20530dac5..4fe8bf738e 100644 --- a/components/cli/cmd/docker/docker.go +++ b/components/cli/cmd/docker/docker.go @@ -256,6 +256,9 @@ func hideUnsupportedFeatures(cmd *cobra.Command, details versionDetails) { if subcmdVersion, ok := subcmd.Annotations["version"]; ok && versions.LessThan(clientVersion, subcmdVersion) { subcmd.Hidden = true } + if v, ok := subcmd.Annotations["ostype"]; ok && v != osType { + subcmd.Hidden = true + } } } @@ -282,14 +285,14 @@ func areFlagsSupported(cmd *cobra.Command, details versionDetails) error { return } if !isOSTypeSupported(f, osType) { - errs = append(errs, fmt.Sprintf("\"--%s\" requires the Docker daemon to run on %s, but the Docker daemon is running on %s", f.Name, getFlagAnnotation(f, "ostype"), osType)) + errs = append(errs, fmt.Sprintf("\"--%s\" is only supported on a Docker daemon running on %s, but the Docker daemon is running on %s", f.Name, getFlagAnnotation(f, "ostype"), osType)) return } if _, ok := f.Annotations["experimental"]; ok && !hasExperimental { errs = append(errs, fmt.Sprintf("\"--%s\" is only supported on a Docker daemon with experimental features enabled", f.Name)) } if _, ok := f.Annotations["experimentalCLI"]; ok && !hasExperimentalCLI { - errs = append(errs, fmt.Sprintf("\"--%s\" is only supported when experimental cli features are enabled", f.Name)) + errs = append(errs, fmt.Sprintf("\"--%s\" is on a Docker cli with experimental cli features enabled", f.Name)) } _, isKubernetesAnnotated := f.Annotations["kubernetes"] _, isSwarmAnnotated := f.Annotations["swarm"] @@ -310,6 +313,7 @@ func areFlagsSupported(cmd *cobra.Command, details versionDetails) error { // Check recursively so that, e.g., `docker stack ls` returns the same output as `docker stack` func areSubcommandsSupported(cmd *cobra.Command, details versionDetails) error { clientVersion := details.Client().ClientVersion() + osType := details.ServerInfo().OSType hasExperimental := details.ServerInfo().HasExperimental hasExperimentalCLI := details.ClientInfo().HasExperimental hasKubernetes := details.ClientInfo().HasKubernetes() @@ -319,11 +323,14 @@ func areSubcommandsSupported(cmd *cobra.Command, details versionDetails) error { if cmdVersion, ok := curr.Annotations["version"]; ok && versions.LessThan(clientVersion, cmdVersion) { return fmt.Errorf("%s requires API version %s, but the Docker daemon API version is %s", cmd.CommandPath(), cmdVersion, clientVersion) } + if os, ok := curr.Annotations["ostype"]; ok && os != osType { + return fmt.Errorf("%s is only supported on a Docker daemon running on %s, but the Docker daemon is running on %s", cmd.CommandPath(), os, osType) + } if _, ok := curr.Annotations["experimental"]; ok && !hasExperimental { return fmt.Errorf("%s is only supported on a Docker daemon with experimental features enabled", cmd.CommandPath()) } if _, ok := curr.Annotations["experimentalCLI"]; ok && !hasExperimentalCLI { - return fmt.Errorf("%s is only supported when experimental cli features are enabled", cmd.CommandPath()) + return fmt.Errorf("%s is only supported on a Docker cli with experimental cli features enabled", cmd.CommandPath()) } _, isKubernetesAnnotated := curr.Annotations["kubernetes"] _, isSwarmAnnotated := curr.Annotations["swarm"] From a76ed6f9178dd2d98626863e0929a84d988bcf89 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 30 May 2018 23:17:06 +0200 Subject: [PATCH 3/4] YAML docs: add os_type property on flags and (sub)commands This patch adds an `os_type` property in the generated YAML docs, both for commands, and for flags; Note that the ostype annotation on flags can have multiple values set, however, multiple values are currently not used (and unlikely will). To simplify usage of the os_type property in the YAML, and for consistency with the same property for commands, we're only using the first ostype that's set. ```yaml command: docker checkpoint create short: Create a checkpoint from a running container long: Create a checkpoint from a running container usage: docker checkpoint create [OPTIONS] CONTAINER CHECKPOINT [flags] pname: docker checkpoint plink: docker_checkpoint.yaml options: - option: checkpoint-dir value_type: string description: Use a custom checkpoint storage directory deprecated: false experimental: false experimentalcli: false kubernetes: false swarm: false - option: leave-running value_type: bool default_value: "false" description: Leave the container running after checkpoint deprecated: false experimental: false experimentalcli: false kubernetes: false swarm: false deprecated: false min_api_version: "1.25" experimental: true experimentalcli: false kubernetes: false swarm: false os_type: windows ``` ```yaml command: docker container start short: Start one or more stopped containers long: Start one or more stopped containers usage: docker container start [OPTIONS] CONTAINER [CONTAINER...] [flags] pname: docker container plink: docker_container.yaml options: - option: attach shorthand: a value_type: bool default_value: "false" description: Attach STDOUT/STDERR and forward signals deprecated: false experimental: false experimentalcli: false kubernetes: false swarm: false - option: checkpoint value_type: string description: Restore from this checkpoint deprecated: false experimental: true experimentalcli: false kubernetes: false swarm: false os_type: linux - option: checkpoint-dir value_type: string description: Use a custom checkpoint storage directory deprecated: false experimental: true experimentalcli: false kubernetes: false swarm: false os_type: linux - option: detach-keys value_type: string description: Override the key sequence for detaching a container deprecated: false experimental: false experimentalcli: false kubernetes: false swarm: false - option: interactive shorthand: i value_type: bool default_value: "false" description: Attach container's STDIN deprecated: false experimental: false experimentalcli: false kubernetes: false swarm: false deprecated: false experimental: false experimentalcli: false kubernetes: false swarm: false ``` Signed-off-by: Sebastiaan van Stijn Upstream-commit: be035a0272fac8416848934ca4be6d8869d91cae Component: cli --- components/cli/docs/yaml/yaml.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/components/cli/docs/yaml/yaml.go b/components/cli/docs/yaml/yaml.go index 5dc7164192..82c90973c0 100644 --- a/components/cli/docs/yaml/yaml.go +++ b/components/cli/docs/yaml/yaml.go @@ -25,6 +25,7 @@ type cmdOption struct { ExperimentalCLI bool Kubernetes bool Swarm bool + OSType string `yaml:"os_type,omitempty"` } type cmdDoc struct { @@ -48,6 +49,7 @@ type cmdDoc struct { ExperimentalCLI bool Kubernetes bool Swarm bool + OSType string `yaml:"os_type,omitempty"` } // GenYamlTree creates yaml structured ref files @@ -121,6 +123,9 @@ func GenYamlCustom(cmd *cobra.Command, w io.Writer) error { if _, ok := curr.Annotations["swarm"]; ok && !cliDoc.Swarm { cliDoc.Swarm = true } + if os, ok := curr.Annotations["ostype"]; ok && cliDoc.OSType == "" { + cliDoc.OSType = os + } } flags := cmd.NonInheritedFlags() @@ -207,6 +212,15 @@ func genFlagResult(flags *pflag.FlagSet) []cmdOption { opt.Swarm = true } + // Note that the annotation can have multiple ostypes set, however, multiple + // values are currently not used (and unlikely will). + // + // To simplify usage of the os_type property in the YAML, and for consistency + // with the same property for commands, we're only using the first ostype that's set. + if ostypes, ok := flag.Annotations["ostype"]; ok && len(opt.OSType) == 0 && len(ostypes) > 0 { + opt.OSType = ostypes[0] + } + result = append(result, opt) }) From 14b4abdabde93dea98e4c97eff0ad586f4fcdcbb Mon Sep 17 00:00:00 2001 From: Silvin Lubecki Date: Fri, 1 Jun 2018 00:18:26 +0200 Subject: [PATCH 4/4] Fix service filtering by name on Kubernetes to mimic Swarm filtering * Kubernetes native filtering (server side) is an exact match, now filtering on name is made client-side to add prefix-matching Signed-off-by: Silvin Lubecki Upstream-commit: b1ff1991675a9b0a392b6b7853e39dc5eb58d018 Component: cli --- .../cli/command/stack/kubernetes/services.go | 36 +++++--- .../command/stack/kubernetes/services_test.go | 86 ++++++++++++------- 2 files changed, 80 insertions(+), 42 deletions(-) diff --git a/components/cli/cli/command/stack/kubernetes/services.go b/components/cli/cli/command/stack/kubernetes/services.go index 5a1db529dd..a2d13e5672 100644 --- a/components/cli/cli/command/stack/kubernetes/services.go +++ b/components/cli/cli/command/stack/kubernetes/services.go @@ -2,13 +2,13 @@ package kubernetes import ( "fmt" - "sort" "strings" "github.com/docker/cli/cli/command/formatter" "github.com/docker/cli/cli/command/stack/options" "github.com/docker/cli/kubernetes/labels" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/swarm" appsv1beta2 "k8s.io/api/apps/v1beta2" corev1 "k8s.io/api/core/v1" apierrs "k8s.io/apimachinery/pkg/api/errors" @@ -49,15 +49,7 @@ func parseLabelFilters(rawFilters []string) map[string][]string { } func generateLabelSelector(f filters.Args, stackName string) string { - names := f.Get("name") - sort.Strings(names) - for _, n := range names { - if strings.HasPrefix(n, stackName+"_") { - // also accepts with unprefixed service name (for compat with existing swarm scripts where service names are prefixed by stack names) - names = append(names, strings.TrimPrefix(n, stackName+"_")) - } - } - selectors := append(generateSelector(parseLabelFilters(f.Get("label"))), labels.SelectorForStack(stackName, names...)) + selectors := append(generateSelector(parseLabelFilters(f.Get("label"))), labels.SelectorForStack(stackName)) return strings.Join(selectors, ",") } @@ -120,6 +112,7 @@ func RunServices(dockerCli *KubeCli, opts options.Services) error { if err != nil { return err } + services = filterServicesByName(services, filters.Get("name"), stackName) if opts.Quiet { info = map[string]formatter.ServiceListInfo{} @@ -140,3 +133,26 @@ func RunServices(dockerCli *KubeCli, opts options.Services) error { } return formatter.ServiceListWrite(servicesCtx, services, info) } + +func filterServicesByName(services []swarm.Service, names []string, stackName string) []swarm.Service { + if len(names) == 0 { + return services + } + prefix := stackName + "_" + // Accepts unprefixed service name (for compatibility with existing swarm scripts where service names are prefixed by stack names) + for i, n := range names { + if !strings.HasPrefix(n, prefix) { + names[i] = stackName + "_" + n + } + } + // Filter services + result := []swarm.Service{} + for _, s := range services { + for _, n := range names { + if strings.HasPrefix(s.Spec.Name, n) { + result = append(result, s) + } + } + } + return result +} diff --git a/components/cli/cli/command/stack/kubernetes/services_test.go b/components/cli/cli/command/stack/kubernetes/services_test.go index 41a3348e0b..89cb9b7e4e 100644 --- a/components/cli/cli/command/stack/kubernetes/services_test.go +++ b/components/cli/cli/command/stack/kubernetes/services_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/swarm" "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/assert/cmp" ) @@ -23,27 +24,6 @@ func TestServiceFiltersLabelSelectorGen(t *testing.T) { "com.docker.stack.namespace=test", }, }, - { - name: "single-name filter", - stackName: "test", - filters: filters.NewArgs(filters.KeyValuePair{Key: "name", Value: "svc-test"}), - expectedSelectorParts: []string{ - "com.docker.stack.namespace=test", - "com.docker.service.name=svc-test", - }, - }, - { - name: "multi-name filter", - stackName: "test", - filters: filters.NewArgs( - filters.KeyValuePair{Key: "name", Value: "svc-test"}, - filters.KeyValuePair{Key: "name", Value: "svc-test2"}, - ), - expectedSelectorParts: []string{ - "com.docker.stack.namespace=test", - "com.docker.service.name in (svc-test,svc-test2)", - }, - }, { name: "label present filter", stackName: "test", @@ -92,17 +72,6 @@ func TestServiceFiltersLabelSelectorGen(t *testing.T) { "label2=test2", }, }, - { - name: "name filter with stackName prefix", - stackName: "test", - filters: filters.NewArgs( - filters.KeyValuePair{Key: "name", Value: "test_svc1"}, - ), - expectedSelectorParts: []string{ - "com.docker.stack.namespace=test", - "com.docker.service.name in (test_svc1,svc1)", - }, - }, } for _, c := range cases { @@ -114,3 +83,56 @@ func TestServiceFiltersLabelSelectorGen(t *testing.T) { }) } } +func TestServiceFiltersServiceByName(t *testing.T) { + cases := []struct { + name string + filters []string + services []swarm.Service + expectedServices []swarm.Service + }{ + { + name: "no filter", + filters: []string{}, + services: makeServices("s1", "s2"), + expectedServices: makeServices("s1", "s2"), + }, + { + name: "single-name filter", + filters: []string{"s1"}, + services: makeServices("s1", "s2"), + expectedServices: makeServices("s1"), + }, + { + name: "filter by prefix", + filters: []string{"prefix"}, + services: makeServices("prefix-s1", "prefix-s2", "s2"), + expectedServices: makeServices("prefix-s1", "prefix-s2"), + }, + { + name: "multi-name filter", + filters: []string{"s1", "s2"}, + services: makeServices("s1", "s2", "s3"), + expectedServices: makeServices("s1", "s2"), + }, + { + name: "stack name prefix is valid", + filters: []string{"stack_s1"}, + services: makeServices("s1", "s11", "s2"), + expectedServices: makeServices("s1", "s11"), + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + result := filterServicesByName(c.services, c.filters, "stack") + assert.DeepEqual(t, c.expectedServices, result) + }) + } +} + +func makeServices(names ...string) []swarm.Service { + result := make([]swarm.Service, len(names)) + for i, n := range names { + result[i] = swarm.Service{Spec: swarm.ServiceSpec{Annotations: swarm.Annotations{Name: "stack_" + n}}} + } + return result +}