From db31142a6abdf12c0f0e4b37209e32dffe2a45f3 Mon Sep 17 00:00:00 2001 From: Mohini Anne Dsouza Date: Wed, 19 Dec 2018 16:10:22 -0800 Subject: [PATCH 01/60] Add quiet flag to bash completion for docker pull Signed-off-by: Mohini Anne Dsouza --- contrib/completion/bash/docker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index d4831387c0e3..c97d7949f3c6 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -2803,7 +2803,7 @@ _docker_image_pull() { case "$cur" in -*) - local options="--all-tags -a --disable-content-trust=false --help" + local options="--all-tags -a --disable-content-trust=false --help --quiet -q" __docker_server_is_experimental && options+=" --platform" COMPREPLY=( $( compgen -W "$options" -- "$cur" ) ) From 4c0aa94698ac90a94e686e9c7caecd473be9b9cc Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 7 Jan 2019 17:24:35 +0100 Subject: [PATCH 02/60] Hide legacy top-level "deploy" command with DOCKER_HIDE_LEGACY_COMMANDS=1 The `DOCKER_HIDE_LEGACY_COMMANDS` environment variable allows hiding legacy top-level commands that are now available under `docker `. The `docker deploy` top-level command is experimental, and replaced by `docker stack deploy`. This patch hides the top-level `docker deploy` if the `DOCKER_HIDE_LEGACY_COMMANDS` environment variable is set. Before this change: DOCKER_HIDE_LEGACY_COMMANDS=1 docker --help ... Commands: build Build an image from a Dockerfile deploy Deploy a new stack or update an existing stack login Log in to a Docker registry logout Log out from a Docker registry run Run a command in a new container search Search the Docker Hub for images version Show the Docker version information ... With this patch applied: DOCKER_HIDE_LEGACY_COMMANDS=1 docker --help ... Commands: build Build an image from a Dockerfile login Log in to a Docker registry logout Log out from a Docker registry run Run a command in a new container search Search the Docker Hub for images version Show the Docker version information ... Signed-off-by: Sebastiaan van Stijn --- cli/command/commands/commands.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/command/commands/commands.go b/cli/command/commands/commands.go index ca2f6ad0966f..f3e90e1cc769 100644 --- a/cli/command/commands/commands.go +++ b/cli/command/commands/commands.go @@ -75,7 +75,6 @@ func AddCommands(cmd *cobra.Command, dockerCli command.Cli) { // stack stack.NewStackCommand(dockerCli), - stack.NewTopLevelDeployCommand(dockerCli), // swarm swarm.NewSwarmCommand(dockerCli), @@ -87,6 +86,7 @@ func AddCommands(cmd *cobra.Command, dockerCli command.Cli) { volume.NewVolumeCommand(dockerCli), // legacy commands may be hidden + hide(stack.NewTopLevelDeployCommand(dockerCli)), hide(system.NewEventsCommand(dockerCli)), hide(system.NewInfoCommand(dockerCli)), hide(system.NewInspectCommand(dockerCli)), From 91bc4ddde205fca60c9932322b34c4808fd0733c Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 13 Dec 2018 22:19:46 +0100 Subject: [PATCH 03/60] Fix: proxy-configuration being ignored on docker create Proxies configured in config.json were only taking effect when using `docker run`, but were being ignored when using `docker create`. Before this change: echo '{"proxies":{"default":{"httpProxy":"httpProxy","httpsProxy":"httpsProxy","noProxy":"noProxy","ftpProxy":"ftpProxy"}}}' > config.json docker inspect --format '{{.Config.Env}}' $(docker --config=./ create busybox) [PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin] With this change applied: echo '{"proxies":{"default":{"httpProxy":"httpProxy","httpsProxy":"httpsProxy","noProxy":"noProxy","ftpProxy":"ftpProxy"}}}' > config.json docker inspect --format '{{.Config.Env}}' $(docker --config=./ create busybox) [NO_PROXY=noProxy no_proxy=noProxy FTP_PROXY=ftpProxy ftp_proxy=ftpProxy HTTP_PROXY=httpProxy http_proxy=httpProxy HTTPS_PROXY=httpsProxy https_proxy=httpsProxy PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin] Reported-by: Silvano Cirujano Cuesta Signed-off-by: Sebastiaan van Stijn --- cli/command/container/create.go | 15 ++++++++-- cli/command/container/create_test.go | 43 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/cli/command/container/create.go b/cli/command/container/create.go index 69ebd8ce297e..8f302056c456 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -10,6 +10,7 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/image" + "github.com/docker/cli/opts" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" @@ -60,7 +61,17 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command { return cmd } -func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, opts *createOptions, copts *containerOptions) error { +func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, options *createOptions, copts *containerOptions) error { + proxyConfig := dockerCli.ConfigFile().ParseProxyConfig(dockerCli.Client().DaemonHost(), copts.env.GetAll()) + newEnv := []string{} + for k, v := range proxyConfig { + if v == nil { + newEnv = append(newEnv, k) + } else { + newEnv = append(newEnv, fmt.Sprintf("%s=%s", k, *v)) + } + } + copts.env = *opts.NewListOptsRef(&newEnv, nil) containerConfig, err := parse(flags, copts) if err != nil { reportError(dockerCli.Err(), "create", err.Error(), true) @@ -70,7 +81,7 @@ func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, opts *createOptions, reportError(dockerCli.Err(), "create", err.Error(), true) return cli.StatusError{StatusCode: 125} } - response, err := createContainer(context.Background(), dockerCli, containerConfig, opts) + response, err := createContainer(context.Background(), dockerCli, containerConfig, options) if err != nil { return err } diff --git a/cli/command/container/create_test.go b/cli/command/container/create_test.go index 2e3bdd529dd9..29912d44dcc1 100644 --- a/cli/command/container/create_test.go +++ b/cli/command/container/create_test.go @@ -7,9 +7,11 @@ import ( "io/ioutil" "os" "runtime" + "sort" "strings" "testing" + "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/internal/test" "github.com/docker/cli/internal/test/notary" "github.com/docker/docker/api/types" @@ -231,6 +233,47 @@ func TestNewCreateCommandWithWarnings(t *testing.T) { } } +func TestCreateContainerWithProxyConfig(t *testing.T) { + expected := []string{ + "HTTP_PROXY=httpProxy", + "http_proxy=httpProxy", + "HTTPS_PROXY=httpsProxy", + "https_proxy=httpsProxy", + "NO_PROXY=noProxy", + "no_proxy=noProxy", + "FTP_PROXY=ftpProxy", + "ftp_proxy=ftpProxy", + } + sort.Strings(expected) + + cli := test.NewFakeCli(&fakeClient{ + createContainerFunc: func(config *container.Config, + hostConfig *container.HostConfig, + networkingConfig *network.NetworkingConfig, + containerName string, + ) (container.ContainerCreateCreatedBody, error) { + sort.Strings(config.Env) + assert.DeepEqual(t, config.Env, expected) + return container.ContainerCreateCreatedBody{}, nil + }, + }) + cli.SetConfigFile(&configfile.ConfigFile{ + Proxies: map[string]configfile.ProxyConfig{ + "default": { + HTTPProxy: "httpProxy", + HTTPSProxy: "httpsProxy", + NoProxy: "noProxy", + FTPProxy: "ftpProxy", + }, + }, + }) + cmd := NewCreateCommand(cli) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs([]string{"image:tag"}) + err := cmd.Execute() + assert.NilError(t, err) +} + type fakeNotFound struct{} func (f fakeNotFound) NotFound() bool { return true } From 41bd8dad8c92cd341563832860001f7292e6d704 Mon Sep 17 00:00:00 2001 From: Harald Albers Date: Thu, 17 Jan 2019 09:40:19 +0100 Subject: [PATCH 04/60] Add bash completion for the `context` command family Signed-off-by: Harald Albers --- contrib/completion/bash/docker | 192 +++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index d4831387c0e3..8be6d2511108 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -178,6 +178,31 @@ __docker_complete_container_ids() { COMPREPLY=( $(compgen -W "${containers[*]}" -- "$cur") ) } +# __docker_contexts returns a list of contexts without the special "default" context. +# Completions may be added with `--add`, e.g. `--add default`. +__docker_contexts() { + local add=() + while true ; do + case "$1" in + --add) + add+=("$2") + shift 2 + ;; + *) + break + ;; + esac + done + __docker_q context ls -q + echo "${add[@]}" +} + +__docker_complete_contexts() { + local contexts=( $(__docker_contexts "$@") ) + COMPREPLY=( $(compgen -W "${contexts[*]}" -- "$cur") ) +} + + # __docker_images returns a list of images. For each image, up to three representations # can be generated: the repository (e.g. busybox), repository:tag (e.g. busybox:latest) # and the ID (e.g. sha256:ee22cbbd4ea3dff63c86ba60c7691287c321e93adfc1009604eb1dde7ec88645). @@ -2234,6 +2259,172 @@ _docker_container_wait() { } +_docker_context() { + local subcommands=" + create + export + import + inspect + ls + rm + update + use + " + local aliases=" + list + remove + " + __docker_subcommands "$subcommands $aliases" && return + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) + ;; + *) + COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) ) + ;; + esac +} + +_docker_context_create() { + case "$prev" in + --default-stack-orchestrator) + COMPREPLY=( $( compgen -W "all kubernetes swarm" -- "$cur" ) ) + return + ;; + --description|--docker|--kubernetes) + return + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--default-stack-orchestrator --description --docker --help --kubernetes" -- "$cur" ) ) + ;; + esac +} + +_docker_context_export() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help --kubeconfig" -- "$cur" ) ) + ;; + *) + local counter=$(__docker_pos_first_nonflag) + if [ "$cword" -eq "$counter" ]; then + __docker_complete_contexts + elif [ "$cword" -eq "$((counter + 1))" ]; then + _filedir + fi + ;; + esac +} + +_docker_context_import() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) + ;; + *) + local counter=$(__docker_pos_first_nonflag) + if [ "$cword" -eq "$counter" ]; then + : + elif [ "$cword" -eq "$((counter + 1))" ]; then + _filedir + fi + ;; + esac +} + +_docker_context_inspect() { + case "$prev" in + --format|-f) + return + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--format -f --help" -- "$cur" ) ) + ;; + *) + __docker_complete_contexts + ;; + esac +} + +_docker_context_list() { + _docker_context_ls +} + +_docker_context_ls() { + case "$prev" in + --format|-f) + return + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--format -f --help --quiet -q" -- "$cur" ) ) + ;; + esac +} + +_docker_context_remove() { + _docker_context_rm +} + +_docker_context_rm() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--force -f --help" -- "$cur" ) ) + ;; + *) + __docker_complete_contexts + ;; + esac +} + +_docker_context_update() { + case "$prev" in + --default-stack-orchestrator) + COMPREPLY=( $( compgen -W "all kubernetes swarm" -- "$cur" ) ) + return + ;; + --description|--docker|--kubernetes) + return + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--default-stack-orchestrator --description --docker --help --kubernetes" -- "$cur" ) ) + ;; + *) + local counter=$(__docker_pos_first_nonflag) + if [ "$cword" -eq "$counter" ]; then + __docker_complete_contexts + fi + ;; + esac +} + +_docker_context_use() { + case "$cur" in + -*) + COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) + ;; + *) + local counter=$(__docker_pos_first_nonflag) + if [ "$cword" -eq "$counter" ]; then + __docker_complete_contexts --add default + fi + ;; + esac +} + + _docker_commit() { _docker_container_commit } @@ -5147,6 +5338,7 @@ _docker() { local management_commands=( config container + context image network node From 33e0bce89fdd804b0c6eb2d633450b87507eb03b Mon Sep 17 00:00:00 2001 From: Harald Albers Date: Sun, 20 Jan 2019 21:41:36 +0100 Subject: [PATCH 05/60] Complete paused containers on `docker stop` Signed-off-by: Harald Albers --- contrib/completion/bash/docker | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index d4831387c0e3..9f5bb0307116 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -156,6 +156,11 @@ __docker_complete_containers_running() { __docker_complete_containers "$@" --filter status=running } +# shellcheck disable=SC2120 +__docker_complete_containers_stoppable() { + __docker_complete_containers "$@" --filter status=running --filter status=paused +} + # shellcheck disable=SC2120 __docker_complete_containers_stopped() { __docker_complete_containers "$@" --filter status=exited @@ -2147,7 +2152,7 @@ _docker_container_stop() { COMPREPLY=( $( compgen -W "--help --time -t" -- "$cur" ) ) ;; *) - __docker_complete_containers_running + __docker_complete_containers_stoppable ;; esac } From 884d0783bdc5c2c4bf9e606572528aed3013e899 Mon Sep 17 00:00:00 2001 From: Harald Albers Date: Tue, 22 Jan 2019 22:27:01 +0100 Subject: [PATCH 06/60] Add bash completion for global `--context|-c` option Signed-off-by: Harald Albers --- contrib/completion/bash/docker | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index 8be6d2511108..b40fc4141da2 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -68,7 +68,7 @@ __docker_previous_extglob_setting=$(shopt -p extglob) shopt -s extglob __docker_q() { - docker ${host:+-H "$host"} ${config:+--config "$config"} 2>/dev/null "$@" + docker ${host:+--host "$host"} ${config:+--config "$config"} ${context:+--context "$context"} 2>/dev/null "$@" } # __docker_configs returns a list of configs. Additional options to @@ -1158,6 +1158,10 @@ _docker_docker() { _filedir -d return ;; + --context|-c) + __docker_complete_contexts + return + ;; --log-level|-l) __docker_complete_log_levels return @@ -5419,6 +5423,7 @@ _docker() { " local global_options_with_args=" --config + --context -c --host -H --log-level -l --tlscacert @@ -5431,7 +5436,7 @@ _docker() { # variables to cache client info, populated on demand for performance reasons local client_experimental stack_orchestrator_is_kubernetes stack_orchestrator_is_swarm - local host config + local host config context COMPREPLY=() local cur prev words cword @@ -5454,6 +5459,11 @@ _docker() { (( counter++ )) config="${words[$counter]}" ;; + # save context so that completion can use custom daemon + --context|-c) + (( counter++ )) + context="${words[$counter]}" + ;; $(__docker_to_extglob "$global_options_with_args") ) (( counter++ )) ;; From c863dbabf7d84bba8c1eeb27a547632ba4901ad3 Mon Sep 17 00:00:00 2001 From: Simon Ferquel Date: Wed, 16 Jan 2019 12:56:15 +0100 Subject: [PATCH 07/60] Vendoring update for v1alpha3 Signed-off-by: Simon Ferquel --- vendor.conf | 2 +- .../docker/compose-on-kubernetes/README.md | 2 + .../docker/compose-on-kubernetes/api/check.go | 55 - .../api/client/clientset/clientset.go | 23 + .../api/client/clientset/scheme/register.go | 2 + .../typed/compose/v1alpha3/compose_client.go | 74 ++ .../clientset/typed/compose/v1alpha3/stack.go | 172 +++ .../api/client/informers/compose/interface.go | 7 + .../informers/compose/v1alpha3/interface.go | 25 + .../informers/compose/v1alpha3/stack.go | 51 + .../api/client/informers/generic.go | 4 +- .../compose/v1alpha3/expansion_generated.go | 9 + .../client/listers/compose/v1alpha3/stack.go | 78 ++ .../v1alpha3/composefile_stack_types.go | 26 + .../compose/v1alpha3/conversion_generated.go | 1158 +++++++++++++++++ .../compose/v1alpha3/deepcopy_generated.go | 660 ++++++++++ .../api/compose/v1alpha3/doc.go | 8 + .../api/compose/v1alpha3/owner.go | 30 + .../api/compose/v1alpha3/register.go | 42 + .../api/compose/v1alpha3/scale.go | 29 + .../api/compose/v1alpha3/stack.go | 270 ++++ 21 files changed, 2670 insertions(+), 57 deletions(-) delete mode 100644 vendor/github.com/docker/compose-on-kubernetes/api/check.go create mode 100644 vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3/compose_client.go create mode 100644 vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3/stack.go create mode 100644 vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1alpha3/interface.go create mode 100644 vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1alpha3/stack.go create mode 100644 vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1alpha3/expansion_generated.go create mode 100644 vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1alpha3/stack.go create mode 100644 vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/composefile_stack_types.go create mode 100644 vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/conversion_generated.go create mode 100644 vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/deepcopy_generated.go create mode 100644 vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/doc.go create mode 100644 vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/owner.go create mode 100644 vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/register.go create mode 100644 vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/scale.go create mode 100644 vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/stack.go diff --git a/vendor.conf b/vendor.conf index 73e1fe0bad3d..d026a893f976 100755 --- a/vendor.conf +++ b/vendor.conf @@ -14,7 +14,7 @@ github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 # v1.1.0 github.com/dgrijalva/jwt-go a2c85815a77d0f951e33ba4db5ae93629a1530af github.com/docker/distribution 83389a148052d74ac602f5f1d62f86ff2f3c4aa5 github.com/docker/docker f76d6a078d881f410c00e8d900dcdfc2e026c841 -github.com/docker/compose-on-kubernetes a6086e2369e39c2058a003a7eb42e567ecfd1f03 # v0.4.17 +github.com/docker/compose-on-kubernetes 1559927c6b456d56cc9c9b05438252ebb646640b # master w/ v1alpha3 github.com/docker/docker-credential-helpers 5241b46610f2491efdf9d1c85f1ddf5b02f6d962 # the docker/go package contains a customized version of canonical/json # and is used by Notary. The package is periodically rebased on current Go versions. diff --git a/vendor/github.com/docker/compose-on-kubernetes/README.md b/vendor/github.com/docker/compose-on-kubernetes/README.md index 683fae428d98..39c7087b2225 100644 --- a/vendor/github.com/docker/compose-on-kubernetes/README.md +++ b/vendor/github.com/docker/compose-on-kubernetes/README.md @@ -185,3 +185,5 @@ See the [contributing](./CONTRIBUTING.md) and [debugging](./DEBUGGING.md) guides # Deploying Compose on Kubernetes - Guide for [Azure AKS](./docs/install-on-aks.md). +- Guide for [GKE](./docs/install-on-gke.md). +- Guide for [Minikube](./docs/install-on-minikube.md). diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/check.go b/vendor/github.com/docker/compose-on-kubernetes/api/check.go deleted file mode 100644 index ad36159426fe..000000000000 --- a/vendor/github.com/docker/compose-on-kubernetes/api/check.go +++ /dev/null @@ -1,55 +0,0 @@ -package apis - -import ( - apiv1beta1 "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" - apiv1beta2 "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" - "github.com/pkg/errors" - apimachinerymetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/kubernetes" -) - -// StackVersion represents the detected Compose Component on Kubernetes side. -type StackVersion string - -const ( - // StackAPIV1Beta1 is returned if it's the most recent version available. - StackAPIV1Beta1 = StackVersion("v1beta1") - // StackAPIV1Beta2 is returned if it's the most recent version available. - StackAPIV1Beta2 = StackVersion("v1beta2") -) - -// GetStackAPIVersion returns the most recent stack API installed. -func GetStackAPIVersion(clientSet *kubernetes.Clientset) (StackVersion, error) { - groups, err := clientSet.Discovery().ServerGroups() - if err != nil { - return "", err - } - - return getAPIVersion(groups) -} - -func getAPIVersion(groups *metav1.APIGroupList) (StackVersion, error) { - switch { - case findVersion(apiv1beta2.SchemeGroupVersion, groups.Groups): - return StackAPIV1Beta2, nil - case findVersion(apiv1beta1.SchemeGroupVersion, groups.Groups): - return StackAPIV1Beta1, nil - default: - return "", errors.Errorf("failed to find a Stack API version") - } -} - -func findVersion(stackAPI schema.GroupVersion, groups []apimachinerymetav1.APIGroup) bool { - for _, group := range groups { - if group.Name == stackAPI.Group { - for _, version := range group.Versions { - if version.Version == stackAPI.Version { - return true - } - } - } - } - return false -} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/clientset.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/clientset.go index 65e8d4fab344..80bccc010473 100644 --- a/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/clientset.go +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/clientset.go @@ -1,6 +1,7 @@ package clientset import ( + composev1alpha3 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3" composev1beta1 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1" composev1beta2 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2" glog "github.com/golang/glog" @@ -13,20 +14,36 @@ import ( // FIXME(vdemeester) is it required ? type Interface interface { Discovery() discovery.DiscoveryInterface + ComposeV1alpha3() composev1alpha3.ComposeV1alpha3Interface ComposeV1beta2() composev1beta2.ComposeV1beta2Interface ComposeV1beta1() composev1beta1.ComposeV1beta1Interface // Deprecated: please explicitly pick a version if possible. Compose() composev1beta1.ComposeV1beta1Interface + ComposeLatest() composev1alpha3.ComposeV1alpha3Interface } // Clientset contains the clients for groups. Each group has exactly one // version included in a Clientset. type Clientset struct { *discovery.DiscoveryClient + *composev1alpha3.ComposeV1alpha3Client *composev1beta2.ComposeV1beta2Client *composev1beta1.ComposeV1beta1Client } +// ComposeV1alpha3 retrieves the ComposeV1alpha3Client +func (c *Clientset) ComposeV1alpha3() composev1alpha3.ComposeV1alpha3Interface { + if c == nil { + return nil + } + return c.ComposeV1alpha3Client +} + +// ComposeLatest retrieves the latest version of the client +func (c *Clientset) ComposeLatest() composev1alpha3.ComposeV1alpha3Interface { + return c.ComposeV1alpha3() +} + // ComposeV1beta2 retrieves the ComposeV1beta2Client func (c *Clientset) ComposeV1beta2() composev1beta2.ComposeV1beta2Interface { if c == nil { @@ -68,6 +85,10 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { } var cs Clientset var err error + cs.ComposeV1alpha3Client, err = composev1alpha3.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } cs.ComposeV1beta2Client, err = composev1beta2.NewForConfig(&configShallowCopy) if err != nil { return nil, err @@ -89,6 +110,7 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { // panics if there is an error in the config. func NewForConfigOrDie(c *rest.Config) *Clientset { var cs Clientset + cs.ComposeV1alpha3Client = composev1alpha3.NewForConfigOrDie(c) cs.ComposeV1beta2Client = composev1beta2.NewForConfigOrDie(c) cs.ComposeV1beta1Client = composev1beta1.NewForConfigOrDie(c) @@ -99,6 +121,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset { // New creates a new Clientset for the given RESTClient. func New(c rest.Interface) *Clientset { var cs Clientset + cs.ComposeV1alpha3Client = composev1alpha3.New(c) cs.ComposeV1beta2Client = composev1beta2.New(c) cs.ComposeV1beta1Client = composev1beta1.New(c) diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/scheme/register.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/scheme/register.go index 2b045a6fd1aa..813833383413 100644 --- a/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/scheme/register.go +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/scheme/register.go @@ -1,6 +1,7 @@ package scheme import ( + composev1alpha3 "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" composev1beta1 "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" composev1beta2 "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -39,6 +40,7 @@ func init() { // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. func AddToScheme(scheme *runtime.Scheme) { + composev1alpha3.AddToScheme(scheme) composev1beta2.AddToScheme(scheme) composev1beta1.AddToScheme(scheme) diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3/compose_client.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3/compose_client.go new file mode 100644 index 000000000000..6bda71f087a3 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3/compose_client.go @@ -0,0 +1,74 @@ +package v1alpha3 + +import ( + "github.com/docker/compose-on-kubernetes/api/client/clientset/scheme" + v1alpha3 "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + rest "k8s.io/client-go/rest" +) + +// ComposeV1alpha3Interface defines the methods a compose v1alpha3 client has +type ComposeV1alpha3Interface interface { + RESTClient() rest.Interface + StacksGetter +} + +// ComposeV1alpha3Client is used to interact with features provided by the compose.docker.com group. +type ComposeV1alpha3Client struct { + restClient rest.Interface +} + +// Stacks returns a stack client +func (c *ComposeV1alpha3Client) Stacks(namespace string) StackInterface { + return newStacks(c, namespace) +} + +// NewForConfig creates a new ComposeV1alpha3Client for the given config. +func NewForConfig(c *rest.Config) (*ComposeV1alpha3Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &ComposeV1alpha3Client{client}, nil +} + +// NewForConfigOrDie creates a new ComposeV1alpha3Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *ComposeV1alpha3Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new ComposeV1alpha3Client for the given RESTClient. +func New(c rest.Interface) *ComposeV1alpha3Client { + return &ComposeV1alpha3Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha3.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *ComposeV1alpha3Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3/stack.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3/stack.go new file mode 100644 index 000000000000..2de253b9d546 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3/stack.go @@ -0,0 +1,172 @@ +package v1alpha3 + +import ( + scheme "github.com/docker/compose-on-kubernetes/api/client/clientset/scheme" + v1alpha3 "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// StacksGetter has a method to return a StackInterface. +// A group's client should implement this interface. +type StacksGetter interface { + Stacks(namespace string) StackInterface +} + +// StackInterface has methods to work with Stack resources. +type StackInterface interface { + Create(*v1alpha3.Stack) (*v1alpha3.Stack, error) + Update(*v1alpha3.Stack) (*v1alpha3.Stack, error) + UpdateStatus(*v1alpha3.Stack) (*v1alpha3.Stack, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha3.Stack, error) + List(opts v1.ListOptions) (*v1alpha3.StackList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*v1alpha3.Stack, error) + WithSkipValidation() StackInterface +} + +// stacks implements StackInterface +type stacks struct { + skipValidation bool + client rest.Interface + ns string +} + +// newStacks returns a Stacks +func newStacks(c *ComposeV1alpha3Client, namespace string) *stacks { + return &stacks{ + client: c.RESTClient(), + ns: namespace, + } +} + +func (c *stacks) handleSkipValidation(req *rest.Request) *rest.Request { + if !c.skipValidation { + return req + } + return req.Param("skip-validation", "1") +} + +// Create takes the representation of a stack and creates it. Returns the server's representation of the stack, and an error, if there is any. +func (c *stacks) Create(stack *v1alpha3.Stack) (*v1alpha3.Stack, error) { + result := &v1alpha3.Stack{} + err := c.handleSkipValidation(c.client.Post(). + Namespace(c.ns). + Resource("stacks"). + Body(stack)). + Do(). + Into(result) + return result, err +} + +// Update takes the representation of a stack and updates it. Returns the server's representation of the stack, and an error, if there is any. +func (c *stacks) Update(stack *v1alpha3.Stack) (*v1alpha3.Stack, error) { + result := &v1alpha3.Stack{} + err := c.handleSkipValidation(c.client.Put(). + Namespace(c.ns). + Resource("stacks"). + Name(stack.Name). + Body(stack)). + Do(). + Into(result) + return result, err +} + +// UpdateStatus was generated because the type contains a Status member. + +func (c *stacks) UpdateStatus(stack *v1alpha3.Stack) (*v1alpha3.Stack, error) { + result := &v1alpha3.Stack{} + err := c.handleSkipValidation(c.client.Put(). + Namespace(c.ns). + Resource("stacks"). + Name(stack.Name). + SubResource("status"). + Body(stack)). + Do(). + Into(result) + return result, err +} + +// Delete takes name of the stack and deletes it. Returns an error if one occurs. +func (c *stacks) Delete(name string, options *v1.DeleteOptions) error { + return c.handleSkipValidation(c.client.Delete(). + Namespace(c.ns). + Resource("stacks"). + Name(name). + Body(options)). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *stacks) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + return c.handleSkipValidation(c.client.Delete(). + Namespace(c.ns). + Resource("stacks"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options)). + Do(). + Error() +} + +// Get takes name of the stack, and returns the corresponding stack object, and an error if there is any. +func (c *stacks) Get(name string, options v1.GetOptions) (*v1alpha3.Stack, error) { + result := &v1alpha3.Stack{} + err := c.client.Get(). + Namespace(c.ns). + Resource("stacks"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return result, err +} + +// List takes label and field selectors, and returns the list of Stacks that match those selectors. +func (c *stacks) List(opts v1.ListOptions) (*v1alpha3.StackList, error) { + result := &v1alpha3.StackList{} + err := c.client.Get(). + Namespace(c.ns). + Resource("stacks"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return result, err +} + +// Watch returns a watch.Interface that watches the requested stacks. +func (c *stacks) Watch(opts v1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("stacks"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Patch applies the patch and returns the patched stack. +func (c *stacks) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*v1alpha3.Stack, error) { + result := &v1alpha3.Stack{} + err := c.handleSkipValidation(c.client.Patch(pt). + Namespace(c.ns). + Resource("stacks"). + SubResource(subresources...). + Name(name). + Body(data)). + Do(). + Into(result) + return result, err +} + +// WithSkipValidation creates a new Stack Client interface with validation disabled +func (c *stacks) WithSkipValidation() StackInterface { + return &stacks{ + skipValidation: true, + client: c.client, + ns: c.ns, + } +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/interface.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/interface.go index d44d1c0db537..f99a8a21ff61 100644 --- a/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/interface.go +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/interface.go @@ -1,6 +1,7 @@ package compose import ( + "github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1alpha3" "github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1beta2" "github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces" ) @@ -8,6 +9,7 @@ import ( // Interface provides access to each of this group's versions. type Interface interface { V1beta2() v1beta2.Interface + V1alpha3() v1alpha3.Interface } type group struct { @@ -23,3 +25,8 @@ func New(f internalinterfaces.SharedInformerFactory) Interface { func (g *group) V1beta2() v1beta2.Interface { return v1beta2.New(g.SharedInformerFactory) } + +// V1alpha3 returns a new V1alpha3.Interface. +func (g *group) V1alpha3() v1alpha3.Interface { + return v1alpha3.New(g.SharedInformerFactory) +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1alpha3/interface.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1alpha3/interface.go new file mode 100644 index 000000000000..d2d84f0be911 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1alpha3/interface.go @@ -0,0 +1,25 @@ +package v1alpha3 + +import ( + "github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // Stacks returns a StackInformer. + Stacks() StackInformer +} + +type version struct { + internalinterfaces.SharedInformerFactory +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory) Interface { + return &version{f} +} + +// Stacks returns a StackInformer. +func (v *version) Stacks() StackInformer { + return &stackInformer{factory: v.SharedInformerFactory} +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1alpha3/stack.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1alpha3/stack.go new file mode 100644 index 000000000000..31d3a30f5caf --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/compose/v1alpha3/stack.go @@ -0,0 +1,51 @@ +package v1alpha3 + +import ( + "time" + + "github.com/docker/compose-on-kubernetes/api/client/clientset" + "github.com/docker/compose-on-kubernetes/api/client/informers/internalinterfaces" + "github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1alpha3" + compose_v1alpha3 "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" + "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/tools/cache" +) + +// StackInformer provides access to a shared informer and lister for +// Stacks. +type StackInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha3.StackLister +} + +type stackInformer struct { + factory internalinterfaces.SharedInformerFactory +} + +func newStackInformer(client clientset.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + sharedIndexInformer := cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + return client.ComposeV1alpha3().Stacks(v1.NamespaceAll).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + return client.ComposeV1alpha3().Stacks(v1.NamespaceAll).Watch(options) + }, + }, + &compose_v1alpha3.Stack{}, + resyncPeriod, + cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, + ) + + return sharedIndexInformer +} + +func (f *stackInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&compose_v1alpha3.Stack{}, newStackInformer) +} + +func (f *stackInformer) Lister() v1alpha3.StackLister { + return v1alpha3.NewStackLister(f.Informer().GetIndexer()) +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/generic.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/generic.go index 060556578673..494ef070f367 100644 --- a/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/generic.go +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/informers/generic.go @@ -3,6 +3,7 @@ package informers import ( "fmt" + "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/tools/cache" @@ -37,7 +38,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource // Group=Compose, Version=V1beta1 case v1beta2.SchemeGroupVersion.WithResource("stacks"): return &genericInformer{resource: resource.GroupResource(), informer: f.Compose().V1beta2().Stacks().Informer()}, nil - + case v1alpha3.SchemeGroupVersion.WithResource("stacks"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Compose().V1alpha3().Stacks().Informer()}, nil } return nil, fmt.Errorf("no informer found for %v", resource) diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1alpha3/expansion_generated.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1alpha3/expansion_generated.go new file mode 100644 index 000000000000..fa6a7b24ac74 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1alpha3/expansion_generated.go @@ -0,0 +1,9 @@ +package v1alpha3 + +// StackListerExpansion allows custom methods to be added to +// StackLister. +type StackListerExpansion interface{} + +// StackNamespaceListerExpansion allows custom methods to be added to +// StackNamespaceLister. +type StackNamespaceListerExpansion interface{} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1alpha3/stack.go b/vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1alpha3/stack.go new file mode 100644 index 000000000000..8b681553c3dc --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/client/listers/compose/v1alpha3/stack.go @@ -0,0 +1,78 @@ +package v1alpha3 + +import ( + "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// StackLister helps list Stacks. +type StackLister interface { + // List lists all Stacks in the indexer. + List(selector labels.Selector) ([]*v1alpha3.Stack, error) + // Stacks returns an object that can list and get Stacks. + Stacks(namespace string) StackNamespaceLister + StackListerExpansion +} + +// stackLister implements the StackLister interface. +type stackLister struct { + indexer cache.Indexer +} + +// NewStackLister returns a new StackLister. +func NewStackLister(indexer cache.Indexer) StackLister { + return &stackLister{indexer: indexer} +} + +// List lists all Stacks in the indexer. +func (s *stackLister) List(selector labels.Selector) ([]*v1alpha3.Stack, error) { + stacks := []*v1alpha3.Stack{} + err := cache.ListAll(s.indexer, selector, func(m interface{}) { + stacks = append(stacks, m.(*v1alpha3.Stack)) + }) + return stacks, err +} + +// Stacks returns an object that can list and get Stacks. +func (s *stackLister) Stacks(namespace string) StackNamespaceLister { + return stackNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// StackNamespaceLister helps list and get Stacks. +type StackNamespaceLister interface { + // List lists all Stacks in the indexer for a given namespace. + List(selector labels.Selector) ([]*v1alpha3.Stack, error) + // Get retrieves the Stack from the indexer for a given namespace and name. + Get(name string) (*v1alpha3.Stack, error) + StackNamespaceListerExpansion +} + +// stackNamespaceLister implements the StackNamespaceLister +// interface. +type stackNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Stacks in the indexer for a given namespace. +func (s stackNamespaceLister) List(selector labels.Selector) ([]*v1alpha3.Stack, error) { + stacks := []*v1alpha3.Stack{} + err := cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + stacks = append(stacks, m.(*v1alpha3.Stack)) + }) + return stacks, err +} + +// Get retrieves the Stack from the indexer for a given namespace and name. +func (s stackNamespaceLister) Get(name string) (*v1alpha3.Stack, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha3.GroupResource("stack"), name) + } + return obj.(*v1alpha3.Stack), nil +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/composefile_stack_types.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/composefile_stack_types.go new file mode 100644 index 000000000000..8d03816a8444 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/composefile_stack_types.go @@ -0,0 +1,26 @@ +package v1alpha3 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// ComposeFile is the content of a stack's compose file if any +type ComposeFile struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + ComposeFile string `json:"composeFile,omitempty"` +} + +func (c *ComposeFile) clone() *ComposeFile { + if c == nil { + return nil + } + res := *c + return &res +} + +// DeepCopyObject clones the ComposeFile +func (c *ComposeFile) DeepCopyObject() runtime.Object { + return c.clone() +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/conversion_generated.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/conversion_generated.go new file mode 100644 index 000000000000..718aac8d8e3f --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/conversion_generated.go @@ -0,0 +1,1158 @@ +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +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. +*/ + +// Code generated by conversion-gen. DO NOT EDIT. + +package v1alpha3 + +import ( + v1beta2 "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" + conversion "k8s.io/apimachinery/pkg/conversion" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +func init() { + localSchemeBuilder.Register(RegisterConversions) +} + +// RegisterConversions adds conversion functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterConversions(scheme *runtime.Scheme) error { + return scheme.AddGeneratedConversionFuncs( + Convert_v1alpha3_ComposeFile_To_v1beta2_ComposeFile, + Convert_v1beta2_ComposeFile_To_v1alpha3_ComposeFile, + Convert_v1alpha3_ConfigObjConfig_To_v1beta2_ConfigObjConfig, + Convert_v1beta2_ConfigObjConfig_To_v1alpha3_ConfigObjConfig, + Convert_v1alpha3_Constraint_To_v1beta2_Constraint, + Convert_v1beta2_Constraint_To_v1alpha3_Constraint, + Convert_v1alpha3_Constraints_To_v1beta2_Constraints, + Convert_v1beta2_Constraints_To_v1alpha3_Constraints, + Convert_v1alpha3_DeployConfig_To_v1beta2_DeployConfig, + Convert_v1beta2_DeployConfig_To_v1alpha3_DeployConfig, + Convert_v1alpha3_External_To_v1beta2_External, + Convert_v1beta2_External_To_v1alpha3_External, + Convert_v1alpha3_FileObjectConfig_To_v1beta2_FileObjectConfig, + Convert_v1beta2_FileObjectConfig_To_v1alpha3_FileObjectConfig, + Convert_v1alpha3_FileReferenceConfig_To_v1beta2_FileReferenceConfig, + Convert_v1beta2_FileReferenceConfig_To_v1alpha3_FileReferenceConfig, + Convert_v1alpha3_HealthCheckConfig_To_v1beta2_HealthCheckConfig, + Convert_v1beta2_HealthCheckConfig_To_v1alpha3_HealthCheckConfig, + Convert_v1alpha3_Owner_To_v1beta2_Owner, + Convert_v1beta2_Owner_To_v1alpha3_Owner, + Convert_v1alpha3_Placement_To_v1beta2_Placement, + Convert_v1beta2_Placement_To_v1alpha3_Placement, + Convert_v1alpha3_Resource_To_v1beta2_Resource, + Convert_v1beta2_Resource_To_v1alpha3_Resource, + Convert_v1alpha3_Resources_To_v1beta2_Resources, + Convert_v1beta2_Resources_To_v1alpha3_Resources, + Convert_v1alpha3_RestartPolicy_To_v1beta2_RestartPolicy, + Convert_v1beta2_RestartPolicy_To_v1alpha3_RestartPolicy, + Convert_v1alpha3_Scale_To_v1beta2_Scale, + Convert_v1beta2_Scale_To_v1alpha3_Scale, + Convert_v1alpha3_SecretConfig_To_v1beta2_SecretConfig, + Convert_v1beta2_SecretConfig_To_v1alpha3_SecretConfig, + Convert_v1alpha3_ServiceConfig_To_v1beta2_ServiceConfig, + Convert_v1beta2_ServiceConfig_To_v1alpha3_ServiceConfig, + Convert_v1alpha3_ServiceConfigObjConfig_To_v1beta2_ServiceConfigObjConfig, + Convert_v1beta2_ServiceConfigObjConfig_To_v1alpha3_ServiceConfigObjConfig, + Convert_v1alpha3_ServicePortConfig_To_v1beta2_ServicePortConfig, + Convert_v1beta2_ServicePortConfig_To_v1alpha3_ServicePortConfig, + Convert_v1alpha3_ServiceSecretConfig_To_v1beta2_ServiceSecretConfig, + Convert_v1beta2_ServiceSecretConfig_To_v1alpha3_ServiceSecretConfig, + Convert_v1alpha3_ServiceVolumeConfig_To_v1beta2_ServiceVolumeConfig, + Convert_v1beta2_ServiceVolumeConfig_To_v1alpha3_ServiceVolumeConfig, + Convert_v1alpha3_Stack_To_v1beta2_Stack, + Convert_v1beta2_Stack_To_v1alpha3_Stack, + Convert_v1alpha3_StackList_To_v1beta2_StackList, + Convert_v1beta2_StackList_To_v1alpha3_StackList, + Convert_v1alpha3_StackSpec_To_v1beta2_StackSpec, + Convert_v1beta2_StackSpec_To_v1alpha3_StackSpec, + Convert_v1alpha3_StackStatus_To_v1beta2_StackStatus, + Convert_v1beta2_StackStatus_To_v1alpha3_StackStatus, + Convert_v1alpha3_UpdateConfig_To_v1beta2_UpdateConfig, + Convert_v1beta2_UpdateConfig_To_v1alpha3_UpdateConfig, + ) +} + +func autoConvert_v1alpha3_ComposeFile_To_v1beta2_ComposeFile(in *ComposeFile, out *v1beta2.ComposeFile, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + out.ComposeFile = in.ComposeFile + return nil +} + +// Convert_v1alpha3_ComposeFile_To_v1beta2_ComposeFile is an autogenerated conversion function. +func Convert_v1alpha3_ComposeFile_To_v1beta2_ComposeFile(in *ComposeFile, out *v1beta2.ComposeFile, s conversion.Scope) error { + return autoConvert_v1alpha3_ComposeFile_To_v1beta2_ComposeFile(in, out, s) +} + +func autoConvert_v1beta2_ComposeFile_To_v1alpha3_ComposeFile(in *v1beta2.ComposeFile, out *ComposeFile, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + out.ComposeFile = in.ComposeFile + return nil +} + +// Convert_v1beta2_ComposeFile_To_v1alpha3_ComposeFile is an autogenerated conversion function. +func Convert_v1beta2_ComposeFile_To_v1alpha3_ComposeFile(in *v1beta2.ComposeFile, out *ComposeFile, s conversion.Scope) error { + return autoConvert_v1beta2_ComposeFile_To_v1alpha3_ComposeFile(in, out, s) +} + +func autoConvert_v1alpha3_ConfigObjConfig_To_v1beta2_ConfigObjConfig(in *ConfigObjConfig, out *v1beta2.ConfigObjConfig, s conversion.Scope) error { + out.Name = in.Name + out.File = in.File + if err := Convert_v1alpha3_External_To_v1beta2_External(&in.External, &out.External, s); err != nil { + return err + } + out.Labels = in.Labels + return nil +} + +// Convert_v1alpha3_ConfigObjConfig_To_v1beta2_ConfigObjConfig is an autogenerated conversion function. +func Convert_v1alpha3_ConfigObjConfig_To_v1beta2_ConfigObjConfig(in *ConfigObjConfig, out *v1beta2.ConfigObjConfig, s conversion.Scope) error { + return autoConvert_v1alpha3_ConfigObjConfig_To_v1beta2_ConfigObjConfig(in, out, s) +} + +func autoConvert_v1beta2_ConfigObjConfig_To_v1alpha3_ConfigObjConfig(in *v1beta2.ConfigObjConfig, out *ConfigObjConfig, s conversion.Scope) error { + out.Name = in.Name + out.File = in.File + if err := Convert_v1beta2_External_To_v1alpha3_External(&in.External, &out.External, s); err != nil { + return err + } + out.Labels = in.Labels + return nil +} + +// Convert_v1beta2_ConfigObjConfig_To_v1alpha3_ConfigObjConfig is an autogenerated conversion function. +func Convert_v1beta2_ConfigObjConfig_To_v1alpha3_ConfigObjConfig(in *v1beta2.ConfigObjConfig, out *ConfigObjConfig, s conversion.Scope) error { + return autoConvert_v1beta2_ConfigObjConfig_To_v1alpha3_ConfigObjConfig(in, out, s) +} + +func autoConvert_v1alpha3_Constraint_To_v1beta2_Constraint(in *Constraint, out *v1beta2.Constraint, s conversion.Scope) error { + out.Value = in.Value + out.Operator = in.Operator + return nil +} + +// Convert_v1alpha3_Constraint_To_v1beta2_Constraint is an autogenerated conversion function. +func Convert_v1alpha3_Constraint_To_v1beta2_Constraint(in *Constraint, out *v1beta2.Constraint, s conversion.Scope) error { + return autoConvert_v1alpha3_Constraint_To_v1beta2_Constraint(in, out, s) +} + +func autoConvert_v1beta2_Constraint_To_v1alpha3_Constraint(in *v1beta2.Constraint, out *Constraint, s conversion.Scope) error { + out.Value = in.Value + out.Operator = in.Operator + return nil +} + +// Convert_v1beta2_Constraint_To_v1alpha3_Constraint is an autogenerated conversion function. +func Convert_v1beta2_Constraint_To_v1alpha3_Constraint(in *v1beta2.Constraint, out *Constraint, s conversion.Scope) error { + return autoConvert_v1beta2_Constraint_To_v1alpha3_Constraint(in, out, s) +} + +func autoConvert_v1alpha3_Constraints_To_v1beta2_Constraints(in *Constraints, out *v1beta2.Constraints, s conversion.Scope) error { + if in.OperatingSystem != nil { + in, out := &in.OperatingSystem, &out.OperatingSystem + *out = new(v1beta2.Constraint) + if err := Convert_v1alpha3_Constraint_To_v1beta2_Constraint(*in, *out, s); err != nil { + return err + } + } else { + out.OperatingSystem = nil + } + if in.Architecture != nil { + in, out := &in.Architecture, &out.Architecture + *out = new(v1beta2.Constraint) + if err := Convert_v1alpha3_Constraint_To_v1beta2_Constraint(*in, *out, s); err != nil { + return err + } + } else { + out.Architecture = nil + } + if in.Hostname != nil { + in, out := &in.Hostname, &out.Hostname + *out = new(v1beta2.Constraint) + if err := Convert_v1alpha3_Constraint_To_v1beta2_Constraint(*in, *out, s); err != nil { + return err + } + } else { + out.Hostname = nil + } + if in.MatchLabels != nil { + in, out := &in.MatchLabels, &out.MatchLabels + *out = make(map[string]v1beta2.Constraint, len(*in)) + for key, val := range *in { + newVal := new(v1beta2.Constraint) + if err := Convert_v1alpha3_Constraint_To_v1beta2_Constraint(&val, newVal, s); err != nil { + return err + } + (*out)[key] = *newVal + } + } else { + out.MatchLabels = nil + } + return nil +} + +// Convert_v1alpha3_Constraints_To_v1beta2_Constraints is an autogenerated conversion function. +func Convert_v1alpha3_Constraints_To_v1beta2_Constraints(in *Constraints, out *v1beta2.Constraints, s conversion.Scope) error { + return autoConvert_v1alpha3_Constraints_To_v1beta2_Constraints(in, out, s) +} + +func autoConvert_v1beta2_Constraints_To_v1alpha3_Constraints(in *v1beta2.Constraints, out *Constraints, s conversion.Scope) error { + if in.OperatingSystem != nil { + in, out := &in.OperatingSystem, &out.OperatingSystem + *out = new(Constraint) + if err := Convert_v1beta2_Constraint_To_v1alpha3_Constraint(*in, *out, s); err != nil { + return err + } + } else { + out.OperatingSystem = nil + } + if in.Architecture != nil { + in, out := &in.Architecture, &out.Architecture + *out = new(Constraint) + if err := Convert_v1beta2_Constraint_To_v1alpha3_Constraint(*in, *out, s); err != nil { + return err + } + } else { + out.Architecture = nil + } + if in.Hostname != nil { + in, out := &in.Hostname, &out.Hostname + *out = new(Constraint) + if err := Convert_v1beta2_Constraint_To_v1alpha3_Constraint(*in, *out, s); err != nil { + return err + } + } else { + out.Hostname = nil + } + if in.MatchLabels != nil { + in, out := &in.MatchLabels, &out.MatchLabels + *out = make(map[string]Constraint, len(*in)) + for key, val := range *in { + newVal := new(Constraint) + if err := Convert_v1beta2_Constraint_To_v1alpha3_Constraint(&val, newVal, s); err != nil { + return err + } + (*out)[key] = *newVal + } + } else { + out.MatchLabels = nil + } + return nil +} + +// Convert_v1beta2_Constraints_To_v1alpha3_Constraints is an autogenerated conversion function. +func Convert_v1beta2_Constraints_To_v1alpha3_Constraints(in *v1beta2.Constraints, out *Constraints, s conversion.Scope) error { + return autoConvert_v1beta2_Constraints_To_v1alpha3_Constraints(in, out, s) +} + +func autoConvert_v1alpha3_DeployConfig_To_v1beta2_DeployConfig(in *DeployConfig, out *v1beta2.DeployConfig, s conversion.Scope) error { + out.Mode = in.Mode + out.Replicas = in.Replicas + out.Labels = in.Labels + if in.UpdateConfig != nil { + in, out := &in.UpdateConfig, &out.UpdateConfig + *out = new(v1beta2.UpdateConfig) + if err := Convert_v1alpha3_UpdateConfig_To_v1beta2_UpdateConfig(*in, *out, s); err != nil { + return err + } + } else { + out.UpdateConfig = nil + } + if err := Convert_v1alpha3_Resources_To_v1beta2_Resources(&in.Resources, &out.Resources, s); err != nil { + return err + } + if in.RestartPolicy != nil { + in, out := &in.RestartPolicy, &out.RestartPolicy + *out = new(v1beta2.RestartPolicy) + if err := Convert_v1alpha3_RestartPolicy_To_v1beta2_RestartPolicy(*in, *out, s); err != nil { + return err + } + } else { + out.RestartPolicy = nil + } + if err := Convert_v1alpha3_Placement_To_v1beta2_Placement(&in.Placement, &out.Placement, s); err != nil { + return err + } + return nil +} + +// Convert_v1alpha3_DeployConfig_To_v1beta2_DeployConfig is an autogenerated conversion function. +func Convert_v1alpha3_DeployConfig_To_v1beta2_DeployConfig(in *DeployConfig, out *v1beta2.DeployConfig, s conversion.Scope) error { + return autoConvert_v1alpha3_DeployConfig_To_v1beta2_DeployConfig(in, out, s) +} + +func autoConvert_v1beta2_DeployConfig_To_v1alpha3_DeployConfig(in *v1beta2.DeployConfig, out *DeployConfig, s conversion.Scope) error { + out.Mode = in.Mode + out.Replicas = in.Replicas + out.Labels = in.Labels + if in.UpdateConfig != nil { + in, out := &in.UpdateConfig, &out.UpdateConfig + *out = new(UpdateConfig) + if err := Convert_v1beta2_UpdateConfig_To_v1alpha3_UpdateConfig(*in, *out, s); err != nil { + return err + } + } else { + out.UpdateConfig = nil + } + if err := Convert_v1beta2_Resources_To_v1alpha3_Resources(&in.Resources, &out.Resources, s); err != nil { + return err + } + if in.RestartPolicy != nil { + in, out := &in.RestartPolicy, &out.RestartPolicy + *out = new(RestartPolicy) + if err := Convert_v1beta2_RestartPolicy_To_v1alpha3_RestartPolicy(*in, *out, s); err != nil { + return err + } + } else { + out.RestartPolicy = nil + } + if err := Convert_v1beta2_Placement_To_v1alpha3_Placement(&in.Placement, &out.Placement, s); err != nil { + return err + } + return nil +} + +// Convert_v1beta2_DeployConfig_To_v1alpha3_DeployConfig is an autogenerated conversion function. +func Convert_v1beta2_DeployConfig_To_v1alpha3_DeployConfig(in *v1beta2.DeployConfig, out *DeployConfig, s conversion.Scope) error { + return autoConvert_v1beta2_DeployConfig_To_v1alpha3_DeployConfig(in, out, s) +} + +func autoConvert_v1alpha3_External_To_v1beta2_External(in *External, out *v1beta2.External, s conversion.Scope) error { + out.Name = in.Name + out.External = in.External + return nil +} + +// Convert_v1alpha3_External_To_v1beta2_External is an autogenerated conversion function. +func Convert_v1alpha3_External_To_v1beta2_External(in *External, out *v1beta2.External, s conversion.Scope) error { + return autoConvert_v1alpha3_External_To_v1beta2_External(in, out, s) +} + +func autoConvert_v1beta2_External_To_v1alpha3_External(in *v1beta2.External, out *External, s conversion.Scope) error { + out.Name = in.Name + out.External = in.External + return nil +} + +// Convert_v1beta2_External_To_v1alpha3_External is an autogenerated conversion function. +func Convert_v1beta2_External_To_v1alpha3_External(in *v1beta2.External, out *External, s conversion.Scope) error { + return autoConvert_v1beta2_External_To_v1alpha3_External(in, out, s) +} + +func autoConvert_v1alpha3_FileObjectConfig_To_v1beta2_FileObjectConfig(in *FileObjectConfig, out *v1beta2.FileObjectConfig, s conversion.Scope) error { + out.Name = in.Name + out.File = in.File + if err := Convert_v1alpha3_External_To_v1beta2_External(&in.External, &out.External, s); err != nil { + return err + } + out.Labels = in.Labels + return nil +} + +// Convert_v1alpha3_FileObjectConfig_To_v1beta2_FileObjectConfig is an autogenerated conversion function. +func Convert_v1alpha3_FileObjectConfig_To_v1beta2_FileObjectConfig(in *FileObjectConfig, out *v1beta2.FileObjectConfig, s conversion.Scope) error { + return autoConvert_v1alpha3_FileObjectConfig_To_v1beta2_FileObjectConfig(in, out, s) +} + +func autoConvert_v1beta2_FileObjectConfig_To_v1alpha3_FileObjectConfig(in *v1beta2.FileObjectConfig, out *FileObjectConfig, s conversion.Scope) error { + out.Name = in.Name + out.File = in.File + if err := Convert_v1beta2_External_To_v1alpha3_External(&in.External, &out.External, s); err != nil { + return err + } + out.Labels = in.Labels + return nil +} + +// Convert_v1beta2_FileObjectConfig_To_v1alpha3_FileObjectConfig is an autogenerated conversion function. +func Convert_v1beta2_FileObjectConfig_To_v1alpha3_FileObjectConfig(in *v1beta2.FileObjectConfig, out *FileObjectConfig, s conversion.Scope) error { + return autoConvert_v1beta2_FileObjectConfig_To_v1alpha3_FileObjectConfig(in, out, s) +} + +func autoConvert_v1alpha3_FileReferenceConfig_To_v1beta2_FileReferenceConfig(in *FileReferenceConfig, out *v1beta2.FileReferenceConfig, s conversion.Scope) error { + out.Source = in.Source + out.Target = in.Target + out.UID = in.UID + out.GID = in.GID + out.Mode = in.Mode + return nil +} + +// Convert_v1alpha3_FileReferenceConfig_To_v1beta2_FileReferenceConfig is an autogenerated conversion function. +func Convert_v1alpha3_FileReferenceConfig_To_v1beta2_FileReferenceConfig(in *FileReferenceConfig, out *v1beta2.FileReferenceConfig, s conversion.Scope) error { + return autoConvert_v1alpha3_FileReferenceConfig_To_v1beta2_FileReferenceConfig(in, out, s) +} + +func autoConvert_v1beta2_FileReferenceConfig_To_v1alpha3_FileReferenceConfig(in *v1beta2.FileReferenceConfig, out *FileReferenceConfig, s conversion.Scope) error { + out.Source = in.Source + out.Target = in.Target + out.UID = in.UID + out.GID = in.GID + out.Mode = in.Mode + return nil +} + +// Convert_v1beta2_FileReferenceConfig_To_v1alpha3_FileReferenceConfig is an autogenerated conversion function. +func Convert_v1beta2_FileReferenceConfig_To_v1alpha3_FileReferenceConfig(in *v1beta2.FileReferenceConfig, out *FileReferenceConfig, s conversion.Scope) error { + return autoConvert_v1beta2_FileReferenceConfig_To_v1alpha3_FileReferenceConfig(in, out, s) +} + +func autoConvert_v1alpha3_HealthCheckConfig_To_v1beta2_HealthCheckConfig(in *HealthCheckConfig, out *v1beta2.HealthCheckConfig, s conversion.Scope) error { + out.Test = in.Test + out.Timeout = in.Timeout + out.Interval = in.Interval + out.Retries = in.Retries + return nil +} + +// Convert_v1alpha3_HealthCheckConfig_To_v1beta2_HealthCheckConfig is an autogenerated conversion function. +func Convert_v1alpha3_HealthCheckConfig_To_v1beta2_HealthCheckConfig(in *HealthCheckConfig, out *v1beta2.HealthCheckConfig, s conversion.Scope) error { + return autoConvert_v1alpha3_HealthCheckConfig_To_v1beta2_HealthCheckConfig(in, out, s) +} + +func autoConvert_v1beta2_HealthCheckConfig_To_v1alpha3_HealthCheckConfig(in *v1beta2.HealthCheckConfig, out *HealthCheckConfig, s conversion.Scope) error { + out.Test = in.Test + out.Timeout = in.Timeout + out.Interval = in.Interval + out.Retries = in.Retries + return nil +} + +// Convert_v1beta2_HealthCheckConfig_To_v1alpha3_HealthCheckConfig is an autogenerated conversion function. +func Convert_v1beta2_HealthCheckConfig_To_v1alpha3_HealthCheckConfig(in *v1beta2.HealthCheckConfig, out *HealthCheckConfig, s conversion.Scope) error { + return autoConvert_v1beta2_HealthCheckConfig_To_v1alpha3_HealthCheckConfig(in, out, s) +} + +func autoConvert_v1alpha3_Owner_To_v1beta2_Owner(in *Owner, out *v1beta2.Owner, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + out.Owner = in.Owner + return nil +} + +// Convert_v1alpha3_Owner_To_v1beta2_Owner is an autogenerated conversion function. +func Convert_v1alpha3_Owner_To_v1beta2_Owner(in *Owner, out *v1beta2.Owner, s conversion.Scope) error { + return autoConvert_v1alpha3_Owner_To_v1beta2_Owner(in, out, s) +} + +func autoConvert_v1beta2_Owner_To_v1alpha3_Owner(in *v1beta2.Owner, out *Owner, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + out.Owner = in.Owner + return nil +} + +// Convert_v1beta2_Owner_To_v1alpha3_Owner is an autogenerated conversion function. +func Convert_v1beta2_Owner_To_v1alpha3_Owner(in *v1beta2.Owner, out *Owner, s conversion.Scope) error { + return autoConvert_v1beta2_Owner_To_v1alpha3_Owner(in, out, s) +} + +func autoConvert_v1alpha3_Placement_To_v1beta2_Placement(in *Placement, out *v1beta2.Placement, s conversion.Scope) error { + if in.Constraints != nil { + in, out := &in.Constraints, &out.Constraints + *out = new(v1beta2.Constraints) + if err := Convert_v1alpha3_Constraints_To_v1beta2_Constraints(*in, *out, s); err != nil { + return err + } + } else { + out.Constraints = nil + } + return nil +} + +// Convert_v1alpha3_Placement_To_v1beta2_Placement is an autogenerated conversion function. +func Convert_v1alpha3_Placement_To_v1beta2_Placement(in *Placement, out *v1beta2.Placement, s conversion.Scope) error { + return autoConvert_v1alpha3_Placement_To_v1beta2_Placement(in, out, s) +} + +func autoConvert_v1beta2_Placement_To_v1alpha3_Placement(in *v1beta2.Placement, out *Placement, s conversion.Scope) error { + if in.Constraints != nil { + in, out := &in.Constraints, &out.Constraints + *out = new(Constraints) + if err := Convert_v1beta2_Constraints_To_v1alpha3_Constraints(*in, *out, s); err != nil { + return err + } + } else { + out.Constraints = nil + } + return nil +} + +// Convert_v1beta2_Placement_To_v1alpha3_Placement is an autogenerated conversion function. +func Convert_v1beta2_Placement_To_v1alpha3_Placement(in *v1beta2.Placement, out *Placement, s conversion.Scope) error { + return autoConvert_v1beta2_Placement_To_v1alpha3_Placement(in, out, s) +} + +func autoConvert_v1alpha3_Resource_To_v1beta2_Resource(in *Resource, out *v1beta2.Resource, s conversion.Scope) error { + out.NanoCPUs = in.NanoCPUs + out.MemoryBytes = in.MemoryBytes + return nil +} + +// Convert_v1alpha3_Resource_To_v1beta2_Resource is an autogenerated conversion function. +func Convert_v1alpha3_Resource_To_v1beta2_Resource(in *Resource, out *v1beta2.Resource, s conversion.Scope) error { + return autoConvert_v1alpha3_Resource_To_v1beta2_Resource(in, out, s) +} + +func autoConvert_v1beta2_Resource_To_v1alpha3_Resource(in *v1beta2.Resource, out *Resource, s conversion.Scope) error { + out.NanoCPUs = in.NanoCPUs + out.MemoryBytes = in.MemoryBytes + return nil +} + +// Convert_v1beta2_Resource_To_v1alpha3_Resource is an autogenerated conversion function. +func Convert_v1beta2_Resource_To_v1alpha3_Resource(in *v1beta2.Resource, out *Resource, s conversion.Scope) error { + return autoConvert_v1beta2_Resource_To_v1alpha3_Resource(in, out, s) +} + +func autoConvert_v1alpha3_Resources_To_v1beta2_Resources(in *Resources, out *v1beta2.Resources, s conversion.Scope) error { + if in.Limits != nil { + in, out := &in.Limits, &out.Limits + *out = new(v1beta2.Resource) + if err := Convert_v1alpha3_Resource_To_v1beta2_Resource(*in, *out, s); err != nil { + return err + } + } else { + out.Limits = nil + } + if in.Reservations != nil { + in, out := &in.Reservations, &out.Reservations + *out = new(v1beta2.Resource) + if err := Convert_v1alpha3_Resource_To_v1beta2_Resource(*in, *out, s); err != nil { + return err + } + } else { + out.Reservations = nil + } + return nil +} + +// Convert_v1alpha3_Resources_To_v1beta2_Resources is an autogenerated conversion function. +func Convert_v1alpha3_Resources_To_v1beta2_Resources(in *Resources, out *v1beta2.Resources, s conversion.Scope) error { + return autoConvert_v1alpha3_Resources_To_v1beta2_Resources(in, out, s) +} + +func autoConvert_v1beta2_Resources_To_v1alpha3_Resources(in *v1beta2.Resources, out *Resources, s conversion.Scope) error { + if in.Limits != nil { + in, out := &in.Limits, &out.Limits + *out = new(Resource) + if err := Convert_v1beta2_Resource_To_v1alpha3_Resource(*in, *out, s); err != nil { + return err + } + } else { + out.Limits = nil + } + if in.Reservations != nil { + in, out := &in.Reservations, &out.Reservations + *out = new(Resource) + if err := Convert_v1beta2_Resource_To_v1alpha3_Resource(*in, *out, s); err != nil { + return err + } + } else { + out.Reservations = nil + } + return nil +} + +// Convert_v1beta2_Resources_To_v1alpha3_Resources is an autogenerated conversion function. +func Convert_v1beta2_Resources_To_v1alpha3_Resources(in *v1beta2.Resources, out *Resources, s conversion.Scope) error { + return autoConvert_v1beta2_Resources_To_v1alpha3_Resources(in, out, s) +} + +func autoConvert_v1alpha3_RestartPolicy_To_v1beta2_RestartPolicy(in *RestartPolicy, out *v1beta2.RestartPolicy, s conversion.Scope) error { + out.Condition = in.Condition + return nil +} + +// Convert_v1alpha3_RestartPolicy_To_v1beta2_RestartPolicy is an autogenerated conversion function. +func Convert_v1alpha3_RestartPolicy_To_v1beta2_RestartPolicy(in *RestartPolicy, out *v1beta2.RestartPolicy, s conversion.Scope) error { + return autoConvert_v1alpha3_RestartPolicy_To_v1beta2_RestartPolicy(in, out, s) +} + +func autoConvert_v1beta2_RestartPolicy_To_v1alpha3_RestartPolicy(in *v1beta2.RestartPolicy, out *RestartPolicy, s conversion.Scope) error { + out.Condition = in.Condition + return nil +} + +// Convert_v1beta2_RestartPolicy_To_v1alpha3_RestartPolicy is an autogenerated conversion function. +func Convert_v1beta2_RestartPolicy_To_v1alpha3_RestartPolicy(in *v1beta2.RestartPolicy, out *RestartPolicy, s conversion.Scope) error { + return autoConvert_v1beta2_RestartPolicy_To_v1alpha3_RestartPolicy(in, out, s) +} + +func autoConvert_v1alpha3_Scale_To_v1beta2_Scale(in *Scale, out *v1beta2.Scale, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + out.Spec = in.Spec + out.Status = in.Status + return nil +} + +// Convert_v1alpha3_Scale_To_v1beta2_Scale is an autogenerated conversion function. +func Convert_v1alpha3_Scale_To_v1beta2_Scale(in *Scale, out *v1beta2.Scale, s conversion.Scope) error { + return autoConvert_v1alpha3_Scale_To_v1beta2_Scale(in, out, s) +} + +func autoConvert_v1beta2_Scale_To_v1alpha3_Scale(in *v1beta2.Scale, out *Scale, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + out.Spec = in.Spec + out.Status = in.Status + return nil +} + +// Convert_v1beta2_Scale_To_v1alpha3_Scale is an autogenerated conversion function. +func Convert_v1beta2_Scale_To_v1alpha3_Scale(in *v1beta2.Scale, out *Scale, s conversion.Scope) error { + return autoConvert_v1beta2_Scale_To_v1alpha3_Scale(in, out, s) +} + +func autoConvert_v1alpha3_SecretConfig_To_v1beta2_SecretConfig(in *SecretConfig, out *v1beta2.SecretConfig, s conversion.Scope) error { + out.Name = in.Name + out.File = in.File + if err := Convert_v1alpha3_External_To_v1beta2_External(&in.External, &out.External, s); err != nil { + return err + } + out.Labels = in.Labels + return nil +} + +// Convert_v1alpha3_SecretConfig_To_v1beta2_SecretConfig is an autogenerated conversion function. +func Convert_v1alpha3_SecretConfig_To_v1beta2_SecretConfig(in *SecretConfig, out *v1beta2.SecretConfig, s conversion.Scope) error { + return autoConvert_v1alpha3_SecretConfig_To_v1beta2_SecretConfig(in, out, s) +} + +func autoConvert_v1beta2_SecretConfig_To_v1alpha3_SecretConfig(in *v1beta2.SecretConfig, out *SecretConfig, s conversion.Scope) error { + out.Name = in.Name + out.File = in.File + if err := Convert_v1beta2_External_To_v1alpha3_External(&in.External, &out.External, s); err != nil { + return err + } + out.Labels = in.Labels + return nil +} + +// Convert_v1beta2_SecretConfig_To_v1alpha3_SecretConfig is an autogenerated conversion function. +func Convert_v1beta2_SecretConfig_To_v1alpha3_SecretConfig(in *v1beta2.SecretConfig, out *SecretConfig, s conversion.Scope) error { + return autoConvert_v1beta2_SecretConfig_To_v1alpha3_SecretConfig(in, out, s) +} + +func autoConvert_v1alpha3_ServiceConfig_To_v1beta2_ServiceConfig(in *ServiceConfig, out *v1beta2.ServiceConfig, s conversion.Scope) error { + out.Name = in.Name + out.CapAdd = in.CapAdd + out.CapDrop = in.CapDrop + out.Command = in.Command + if in.Configs != nil { + in, out := &in.Configs, &out.Configs + *out = make([]v1beta2.ServiceConfigObjConfig, len(*in)) + for i := range *in { + if err := Convert_v1alpha3_ServiceConfigObjConfig_To_v1beta2_ServiceConfigObjConfig(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Configs = nil + } + if err := Convert_v1alpha3_DeployConfig_To_v1beta2_DeployConfig(&in.Deploy, &out.Deploy, s); err != nil { + return err + } + out.Entrypoint = in.Entrypoint + out.Environment = in.Environment + out.ExtraHosts = in.ExtraHosts + out.Hostname = in.Hostname + if in.HealthCheck != nil { + in, out := &in.HealthCheck, &out.HealthCheck + *out = new(v1beta2.HealthCheckConfig) + if err := Convert_v1alpha3_HealthCheckConfig_To_v1beta2_HealthCheckConfig(*in, *out, s); err != nil { + return err + } + } else { + out.HealthCheck = nil + } + out.Image = in.Image + out.Ipc = in.Ipc + out.Labels = in.Labels + out.Pid = in.Pid + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]v1beta2.ServicePortConfig, len(*in)) + for i := range *in { + if err := Convert_v1alpha3_ServicePortConfig_To_v1beta2_ServicePortConfig(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Ports = nil + } + out.Privileged = in.Privileged + out.ReadOnly = in.ReadOnly + if in.Secrets != nil { + in, out := &in.Secrets, &out.Secrets + *out = make([]v1beta2.ServiceSecretConfig, len(*in)) + for i := range *in { + if err := Convert_v1alpha3_ServiceSecretConfig_To_v1beta2_ServiceSecretConfig(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Secrets = nil + } + out.StdinOpen = in.StdinOpen + out.StopGracePeriod = in.StopGracePeriod + out.Tmpfs = in.Tmpfs + out.Tty = in.Tty + out.User = in.User + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]v1beta2.ServiceVolumeConfig, len(*in)) + for i := range *in { + if err := Convert_v1alpha3_ServiceVolumeConfig_To_v1beta2_ServiceVolumeConfig(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Volumes = nil + } + out.WorkingDir = in.WorkingDir + return nil +} + +// Convert_v1alpha3_ServiceConfig_To_v1beta2_ServiceConfig is an autogenerated conversion function. +func Convert_v1alpha3_ServiceConfig_To_v1beta2_ServiceConfig(in *ServiceConfig, out *v1beta2.ServiceConfig, s conversion.Scope) error { + return autoConvert_v1alpha3_ServiceConfig_To_v1beta2_ServiceConfig(in, out, s) +} + +func autoConvert_v1beta2_ServiceConfig_To_v1alpha3_ServiceConfig(in *v1beta2.ServiceConfig, out *ServiceConfig, s conversion.Scope) error { + out.Name = in.Name + out.CapAdd = in.CapAdd + out.CapDrop = in.CapDrop + out.Command = in.Command + if in.Configs != nil { + in, out := &in.Configs, &out.Configs + *out = make([]ServiceConfigObjConfig, len(*in)) + for i := range *in { + if err := Convert_v1beta2_ServiceConfigObjConfig_To_v1alpha3_ServiceConfigObjConfig(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Configs = nil + } + if err := Convert_v1beta2_DeployConfig_To_v1alpha3_DeployConfig(&in.Deploy, &out.Deploy, s); err != nil { + return err + } + out.Entrypoint = in.Entrypoint + out.Environment = in.Environment + out.ExtraHosts = in.ExtraHosts + out.Hostname = in.Hostname + if in.HealthCheck != nil { + in, out := &in.HealthCheck, &out.HealthCheck + *out = new(HealthCheckConfig) + if err := Convert_v1beta2_HealthCheckConfig_To_v1alpha3_HealthCheckConfig(*in, *out, s); err != nil { + return err + } + } else { + out.HealthCheck = nil + } + out.Image = in.Image + out.Ipc = in.Ipc + out.Labels = in.Labels + out.Pid = in.Pid + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]ServicePortConfig, len(*in)) + for i := range *in { + if err := Convert_v1beta2_ServicePortConfig_To_v1alpha3_ServicePortConfig(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Ports = nil + } + out.Privileged = in.Privileged + out.ReadOnly = in.ReadOnly + if in.Secrets != nil { + in, out := &in.Secrets, &out.Secrets + *out = make([]ServiceSecretConfig, len(*in)) + for i := range *in { + if err := Convert_v1beta2_ServiceSecretConfig_To_v1alpha3_ServiceSecretConfig(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Secrets = nil + } + out.StdinOpen = in.StdinOpen + out.StopGracePeriod = in.StopGracePeriod + out.Tmpfs = in.Tmpfs + out.Tty = in.Tty + out.User = in.User + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]ServiceVolumeConfig, len(*in)) + for i := range *in { + if err := Convert_v1beta2_ServiceVolumeConfig_To_v1alpha3_ServiceVolumeConfig(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Volumes = nil + } + out.WorkingDir = in.WorkingDir + return nil +} + +// Convert_v1beta2_ServiceConfig_To_v1alpha3_ServiceConfig is an autogenerated conversion function. +func Convert_v1beta2_ServiceConfig_To_v1alpha3_ServiceConfig(in *v1beta2.ServiceConfig, out *ServiceConfig, s conversion.Scope) error { + return autoConvert_v1beta2_ServiceConfig_To_v1alpha3_ServiceConfig(in, out, s) +} + +func autoConvert_v1alpha3_ServiceConfigObjConfig_To_v1beta2_ServiceConfigObjConfig(in *ServiceConfigObjConfig, out *v1beta2.ServiceConfigObjConfig, s conversion.Scope) error { + out.Source = in.Source + out.Target = in.Target + out.UID = in.UID + out.GID = in.GID + out.Mode = in.Mode + return nil +} + +// Convert_v1alpha3_ServiceConfigObjConfig_To_v1beta2_ServiceConfigObjConfig is an autogenerated conversion function. +func Convert_v1alpha3_ServiceConfigObjConfig_To_v1beta2_ServiceConfigObjConfig(in *ServiceConfigObjConfig, out *v1beta2.ServiceConfigObjConfig, s conversion.Scope) error { + return autoConvert_v1alpha3_ServiceConfigObjConfig_To_v1beta2_ServiceConfigObjConfig(in, out, s) +} + +func autoConvert_v1beta2_ServiceConfigObjConfig_To_v1alpha3_ServiceConfigObjConfig(in *v1beta2.ServiceConfigObjConfig, out *ServiceConfigObjConfig, s conversion.Scope) error { + out.Source = in.Source + out.Target = in.Target + out.UID = in.UID + out.GID = in.GID + out.Mode = in.Mode + return nil +} + +// Convert_v1beta2_ServiceConfigObjConfig_To_v1alpha3_ServiceConfigObjConfig is an autogenerated conversion function. +func Convert_v1beta2_ServiceConfigObjConfig_To_v1alpha3_ServiceConfigObjConfig(in *v1beta2.ServiceConfigObjConfig, out *ServiceConfigObjConfig, s conversion.Scope) error { + return autoConvert_v1beta2_ServiceConfigObjConfig_To_v1alpha3_ServiceConfigObjConfig(in, out, s) +} + +func autoConvert_v1alpha3_ServicePortConfig_To_v1beta2_ServicePortConfig(in *ServicePortConfig, out *v1beta2.ServicePortConfig, s conversion.Scope) error { + out.Mode = in.Mode + out.Target = in.Target + out.Published = in.Published + out.Protocol = in.Protocol + return nil +} + +// Convert_v1alpha3_ServicePortConfig_To_v1beta2_ServicePortConfig is an autogenerated conversion function. +func Convert_v1alpha3_ServicePortConfig_To_v1beta2_ServicePortConfig(in *ServicePortConfig, out *v1beta2.ServicePortConfig, s conversion.Scope) error { + return autoConvert_v1alpha3_ServicePortConfig_To_v1beta2_ServicePortConfig(in, out, s) +} + +func autoConvert_v1beta2_ServicePortConfig_To_v1alpha3_ServicePortConfig(in *v1beta2.ServicePortConfig, out *ServicePortConfig, s conversion.Scope) error { + out.Mode = in.Mode + out.Target = in.Target + out.Published = in.Published + out.Protocol = in.Protocol + return nil +} + +// Convert_v1beta2_ServicePortConfig_To_v1alpha3_ServicePortConfig is an autogenerated conversion function. +func Convert_v1beta2_ServicePortConfig_To_v1alpha3_ServicePortConfig(in *v1beta2.ServicePortConfig, out *ServicePortConfig, s conversion.Scope) error { + return autoConvert_v1beta2_ServicePortConfig_To_v1alpha3_ServicePortConfig(in, out, s) +} + +func autoConvert_v1alpha3_ServiceSecretConfig_To_v1beta2_ServiceSecretConfig(in *ServiceSecretConfig, out *v1beta2.ServiceSecretConfig, s conversion.Scope) error { + out.Source = in.Source + out.Target = in.Target + out.UID = in.UID + out.GID = in.GID + out.Mode = in.Mode + return nil +} + +// Convert_v1alpha3_ServiceSecretConfig_To_v1beta2_ServiceSecretConfig is an autogenerated conversion function. +func Convert_v1alpha3_ServiceSecretConfig_To_v1beta2_ServiceSecretConfig(in *ServiceSecretConfig, out *v1beta2.ServiceSecretConfig, s conversion.Scope) error { + return autoConvert_v1alpha3_ServiceSecretConfig_To_v1beta2_ServiceSecretConfig(in, out, s) +} + +func autoConvert_v1beta2_ServiceSecretConfig_To_v1alpha3_ServiceSecretConfig(in *v1beta2.ServiceSecretConfig, out *ServiceSecretConfig, s conversion.Scope) error { + out.Source = in.Source + out.Target = in.Target + out.UID = in.UID + out.GID = in.GID + out.Mode = in.Mode + return nil +} + +// Convert_v1beta2_ServiceSecretConfig_To_v1alpha3_ServiceSecretConfig is an autogenerated conversion function. +func Convert_v1beta2_ServiceSecretConfig_To_v1alpha3_ServiceSecretConfig(in *v1beta2.ServiceSecretConfig, out *ServiceSecretConfig, s conversion.Scope) error { + return autoConvert_v1beta2_ServiceSecretConfig_To_v1alpha3_ServiceSecretConfig(in, out, s) +} + +func autoConvert_v1alpha3_ServiceVolumeConfig_To_v1beta2_ServiceVolumeConfig(in *ServiceVolumeConfig, out *v1beta2.ServiceVolumeConfig, s conversion.Scope) error { + out.Type = in.Type + out.Source = in.Source + out.Target = in.Target + out.ReadOnly = in.ReadOnly + return nil +} + +// Convert_v1alpha3_ServiceVolumeConfig_To_v1beta2_ServiceVolumeConfig is an autogenerated conversion function. +func Convert_v1alpha3_ServiceVolumeConfig_To_v1beta2_ServiceVolumeConfig(in *ServiceVolumeConfig, out *v1beta2.ServiceVolumeConfig, s conversion.Scope) error { + return autoConvert_v1alpha3_ServiceVolumeConfig_To_v1beta2_ServiceVolumeConfig(in, out, s) +} + +func autoConvert_v1beta2_ServiceVolumeConfig_To_v1alpha3_ServiceVolumeConfig(in *v1beta2.ServiceVolumeConfig, out *ServiceVolumeConfig, s conversion.Scope) error { + out.Type = in.Type + out.Source = in.Source + out.Target = in.Target + out.ReadOnly = in.ReadOnly + return nil +} + +// Convert_v1beta2_ServiceVolumeConfig_To_v1alpha3_ServiceVolumeConfig is an autogenerated conversion function. +func Convert_v1beta2_ServiceVolumeConfig_To_v1alpha3_ServiceVolumeConfig(in *v1beta2.ServiceVolumeConfig, out *ServiceVolumeConfig, s conversion.Scope) error { + return autoConvert_v1beta2_ServiceVolumeConfig_To_v1alpha3_ServiceVolumeConfig(in, out, s) +} + +func autoConvert_v1alpha3_Stack_To_v1beta2_Stack(in *Stack, out *v1beta2.Stack, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if in.Spec != nil { + in, out := &in.Spec, &out.Spec + *out = new(v1beta2.StackSpec) + if err := Convert_v1alpha3_StackSpec_To_v1beta2_StackSpec(*in, *out, s); err != nil { + return err + } + } else { + out.Spec = nil + } + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(v1beta2.StackStatus) + if err := Convert_v1alpha3_StackStatus_To_v1beta2_StackStatus(*in, *out, s); err != nil { + return err + } + } else { + out.Status = nil + } + return nil +} + +// Convert_v1alpha3_Stack_To_v1beta2_Stack is an autogenerated conversion function. +func Convert_v1alpha3_Stack_To_v1beta2_Stack(in *Stack, out *v1beta2.Stack, s conversion.Scope) error { + return autoConvert_v1alpha3_Stack_To_v1beta2_Stack(in, out, s) +} + +func autoConvert_v1beta2_Stack_To_v1alpha3_Stack(in *v1beta2.Stack, out *Stack, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + if in.Spec != nil { + in, out := &in.Spec, &out.Spec + *out = new(StackSpec) + if err := Convert_v1beta2_StackSpec_To_v1alpha3_StackSpec(*in, *out, s); err != nil { + return err + } + } else { + out.Spec = nil + } + if in.Status != nil { + in, out := &in.Status, &out.Status + *out = new(StackStatus) + if err := Convert_v1beta2_StackStatus_To_v1alpha3_StackStatus(*in, *out, s); err != nil { + return err + } + } else { + out.Status = nil + } + return nil +} + +// Convert_v1beta2_Stack_To_v1alpha3_Stack is an autogenerated conversion function. +func Convert_v1beta2_Stack_To_v1alpha3_Stack(in *v1beta2.Stack, out *Stack, s conversion.Scope) error { + return autoConvert_v1beta2_Stack_To_v1alpha3_Stack(in, out, s) +} + +func autoConvert_v1alpha3_StackList_To_v1beta2_StackList(in *StackList, out *v1beta2.StackList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]v1beta2.Stack, len(*in)) + for i := range *in { + if err := Convert_v1alpha3_Stack_To_v1beta2_Stack(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1alpha3_StackList_To_v1beta2_StackList is an autogenerated conversion function. +func Convert_v1alpha3_StackList_To_v1beta2_StackList(in *StackList, out *v1beta2.StackList, s conversion.Scope) error { + return autoConvert_v1alpha3_StackList_To_v1beta2_StackList(in, out, s) +} + +func autoConvert_v1beta2_StackList_To_v1alpha3_StackList(in *v1beta2.StackList, out *StackList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Stack, len(*in)) + for i := range *in { + if err := Convert_v1beta2_Stack_To_v1alpha3_Stack(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +// Convert_v1beta2_StackList_To_v1alpha3_StackList is an autogenerated conversion function. +func Convert_v1beta2_StackList_To_v1alpha3_StackList(in *v1beta2.StackList, out *StackList, s conversion.Scope) error { + return autoConvert_v1beta2_StackList_To_v1alpha3_StackList(in, out, s) +} + +func autoConvert_v1alpha3_StackSpec_To_v1beta2_StackSpec(in *StackSpec, out *v1beta2.StackSpec, s conversion.Scope) error { + if in.Services != nil { + in, out := &in.Services, &out.Services + *out = make([]v1beta2.ServiceConfig, len(*in)) + for i := range *in { + if err := Convert_v1alpha3_ServiceConfig_To_v1beta2_ServiceConfig(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Services = nil + } + if in.Secrets != nil { + in, out := &in.Secrets, &out.Secrets + *out = make(map[string]v1beta2.SecretConfig, len(*in)) + for key, val := range *in { + newVal := new(v1beta2.SecretConfig) + if err := Convert_v1alpha3_SecretConfig_To_v1beta2_SecretConfig(&val, newVal, s); err != nil { + return err + } + (*out)[key] = *newVal + } + } else { + out.Secrets = nil + } + if in.Configs != nil { + in, out := &in.Configs, &out.Configs + *out = make(map[string]v1beta2.ConfigObjConfig, len(*in)) + for key, val := range *in { + newVal := new(v1beta2.ConfigObjConfig) + if err := Convert_v1alpha3_ConfigObjConfig_To_v1beta2_ConfigObjConfig(&val, newVal, s); err != nil { + return err + } + (*out)[key] = *newVal + } + } else { + out.Configs = nil + } + return nil +} + +// Convert_v1alpha3_StackSpec_To_v1beta2_StackSpec is an autogenerated conversion function. +func Convert_v1alpha3_StackSpec_To_v1beta2_StackSpec(in *StackSpec, out *v1beta2.StackSpec, s conversion.Scope) error { + return autoConvert_v1alpha3_StackSpec_To_v1beta2_StackSpec(in, out, s) +} + +func autoConvert_v1beta2_StackSpec_To_v1alpha3_StackSpec(in *v1beta2.StackSpec, out *StackSpec, s conversion.Scope) error { + if in.Services != nil { + in, out := &in.Services, &out.Services + *out = make([]ServiceConfig, len(*in)) + for i := range *in { + if err := Convert_v1beta2_ServiceConfig_To_v1alpha3_ServiceConfig(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Services = nil + } + if in.Secrets != nil { + in, out := &in.Secrets, &out.Secrets + *out = make(map[string]SecretConfig, len(*in)) + for key, val := range *in { + newVal := new(SecretConfig) + if err := Convert_v1beta2_SecretConfig_To_v1alpha3_SecretConfig(&val, newVal, s); err != nil { + return err + } + (*out)[key] = *newVal + } + } else { + out.Secrets = nil + } + if in.Configs != nil { + in, out := &in.Configs, &out.Configs + *out = make(map[string]ConfigObjConfig, len(*in)) + for key, val := range *in { + newVal := new(ConfigObjConfig) + if err := Convert_v1beta2_ConfigObjConfig_To_v1alpha3_ConfigObjConfig(&val, newVal, s); err != nil { + return err + } + (*out)[key] = *newVal + } + } else { + out.Configs = nil + } + return nil +} + +// Convert_v1beta2_StackSpec_To_v1alpha3_StackSpec is an autogenerated conversion function. +func Convert_v1beta2_StackSpec_To_v1alpha3_StackSpec(in *v1beta2.StackSpec, out *StackSpec, s conversion.Scope) error { + return autoConvert_v1beta2_StackSpec_To_v1alpha3_StackSpec(in, out, s) +} + +func autoConvert_v1alpha3_StackStatus_To_v1beta2_StackStatus(in *StackStatus, out *v1beta2.StackStatus, s conversion.Scope) error { + out.Phase = v1beta2.StackPhase(in.Phase) + out.Message = in.Message + return nil +} + +// Convert_v1alpha3_StackStatus_To_v1beta2_StackStatus is an autogenerated conversion function. +func Convert_v1alpha3_StackStatus_To_v1beta2_StackStatus(in *StackStatus, out *v1beta2.StackStatus, s conversion.Scope) error { + return autoConvert_v1alpha3_StackStatus_To_v1beta2_StackStatus(in, out, s) +} + +func autoConvert_v1beta2_StackStatus_To_v1alpha3_StackStatus(in *v1beta2.StackStatus, out *StackStatus, s conversion.Scope) error { + out.Phase = StackPhase(in.Phase) + out.Message = in.Message + return nil +} + +// Convert_v1beta2_StackStatus_To_v1alpha3_StackStatus is an autogenerated conversion function. +func Convert_v1beta2_StackStatus_To_v1alpha3_StackStatus(in *v1beta2.StackStatus, out *StackStatus, s conversion.Scope) error { + return autoConvert_v1beta2_StackStatus_To_v1alpha3_StackStatus(in, out, s) +} + +func autoConvert_v1alpha3_UpdateConfig_To_v1beta2_UpdateConfig(in *UpdateConfig, out *v1beta2.UpdateConfig, s conversion.Scope) error { + out.Parallelism = in.Parallelism + return nil +} + +// Convert_v1alpha3_UpdateConfig_To_v1beta2_UpdateConfig is an autogenerated conversion function. +func Convert_v1alpha3_UpdateConfig_To_v1beta2_UpdateConfig(in *UpdateConfig, out *v1beta2.UpdateConfig, s conversion.Scope) error { + return autoConvert_v1alpha3_UpdateConfig_To_v1beta2_UpdateConfig(in, out, s) +} + +func autoConvert_v1beta2_UpdateConfig_To_v1alpha3_UpdateConfig(in *v1beta2.UpdateConfig, out *UpdateConfig, s conversion.Scope) error { + out.Parallelism = in.Parallelism + return nil +} + +// Convert_v1beta2_UpdateConfig_To_v1alpha3_UpdateConfig is an autogenerated conversion function. +func Convert_v1beta2_UpdateConfig_To_v1alpha3_UpdateConfig(in *v1beta2.UpdateConfig, out *UpdateConfig, s conversion.Scope) error { + return autoConvert_v1beta2_UpdateConfig_To_v1alpha3_UpdateConfig(in, out, s) +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/deepcopy_generated.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/deepcopy_generated.go new file mode 100644 index 000000000000..3cfe5c471b12 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/deepcopy_generated.go @@ -0,0 +1,660 @@ +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +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. +*/ + +// Code generated by C:\gohome\bin\deepcopy-gen.exe. DO NOT EDIT. + +package v1alpha3 + +import ( + time "time" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigObjConfig) DeepCopyInto(out *ConfigObjConfig) { + *out = *in + out.External = in.External + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigObjConfig. +func (in *ConfigObjConfig) DeepCopy() *ConfigObjConfig { + if in == nil { + return nil + } + out := new(ConfigObjConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Constraint) DeepCopyInto(out *Constraint) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Constraint. +func (in *Constraint) DeepCopy() *Constraint { + if in == nil { + return nil + } + out := new(Constraint) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Constraints) DeepCopyInto(out *Constraints) { + *out = *in + if in.OperatingSystem != nil { + in, out := &in.OperatingSystem, &out.OperatingSystem + if *in == nil { + *out = nil + } else { + *out = new(Constraint) + **out = **in + } + } + if in.Architecture != nil { + in, out := &in.Architecture, &out.Architecture + if *in == nil { + *out = nil + } else { + *out = new(Constraint) + **out = **in + } + } + if in.Hostname != nil { + in, out := &in.Hostname, &out.Hostname + if *in == nil { + *out = nil + } else { + *out = new(Constraint) + **out = **in + } + } + if in.MatchLabels != nil { + in, out := &in.MatchLabels, &out.MatchLabels + *out = make(map[string]Constraint, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Constraints. +func (in *Constraints) DeepCopy() *Constraints { + if in == nil { + return nil + } + out := new(Constraints) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeployConfig) DeepCopyInto(out *DeployConfig) { + *out = *in + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + if *in == nil { + *out = nil + } else { + *out = new(uint64) + **out = **in + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.UpdateConfig != nil { + in, out := &in.UpdateConfig, &out.UpdateConfig + if *in == nil { + *out = nil + } else { + *out = new(UpdateConfig) + (*in).DeepCopyInto(*out) + } + } + in.Resources.DeepCopyInto(&out.Resources) + if in.RestartPolicy != nil { + in, out := &in.RestartPolicy, &out.RestartPolicy + if *in == nil { + *out = nil + } else { + *out = new(RestartPolicy) + **out = **in + } + } + in.Placement.DeepCopyInto(&out.Placement) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeployConfig. +func (in *DeployConfig) DeepCopy() *DeployConfig { + if in == nil { + return nil + } + out := new(DeployConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *External) DeepCopyInto(out *External) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new External. +func (in *External) DeepCopy() *External { + if in == nil { + return nil + } + out := new(External) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FileObjectConfig) DeepCopyInto(out *FileObjectConfig) { + *out = *in + out.External = in.External + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileObjectConfig. +func (in *FileObjectConfig) DeepCopy() *FileObjectConfig { + if in == nil { + return nil + } + out := new(FileObjectConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FileReferenceConfig) DeepCopyInto(out *FileReferenceConfig) { + *out = *in + if in.Mode != nil { + in, out := &in.Mode, &out.Mode + if *in == nil { + *out = nil + } else { + *out = new(uint32) + **out = **in + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileReferenceConfig. +func (in *FileReferenceConfig) DeepCopy() *FileReferenceConfig { + if in == nil { + return nil + } + out := new(FileReferenceConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HealthCheckConfig) DeepCopyInto(out *HealthCheckConfig) { + *out = *in + if in.Test != nil { + in, out := &in.Test, &out.Test + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + if *in == nil { + *out = nil + } else { + *out = new(time.Duration) + **out = **in + } + } + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + if *in == nil { + *out = nil + } else { + *out = new(time.Duration) + **out = **in + } + } + if in.Retries != nil { + in, out := &in.Retries, &out.Retries + if *in == nil { + *out = nil + } else { + *out = new(uint64) + **out = **in + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HealthCheckConfig. +func (in *HealthCheckConfig) DeepCopy() *HealthCheckConfig { + if in == nil { + return nil + } + out := new(HealthCheckConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Placement) DeepCopyInto(out *Placement) { + *out = *in + if in.Constraints != nil { + in, out := &in.Constraints, &out.Constraints + if *in == nil { + *out = nil + } else { + *out = new(Constraints) + (*in).DeepCopyInto(*out) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Placement. +func (in *Placement) DeepCopy() *Placement { + if in == nil { + return nil + } + out := new(Placement) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resource) DeepCopyInto(out *Resource) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resource. +func (in *Resource) DeepCopy() *Resource { + if in == nil { + return nil + } + out := new(Resource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resources) DeepCopyInto(out *Resources) { + *out = *in + if in.Limits != nil { + in, out := &in.Limits, &out.Limits + if *in == nil { + *out = nil + } else { + *out = new(Resource) + **out = **in + } + } + if in.Reservations != nil { + in, out := &in.Reservations, &out.Reservations + if *in == nil { + *out = nil + } else { + *out = new(Resource) + **out = **in + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resources. +func (in *Resources) DeepCopy() *Resources { + if in == nil { + return nil + } + out := new(Resources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RestartPolicy) DeepCopyInto(out *RestartPolicy) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestartPolicy. +func (in *RestartPolicy) DeepCopy() *RestartPolicy { + if in == nil { + return nil + } + out := new(RestartPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretConfig) DeepCopyInto(out *SecretConfig) { + *out = *in + out.External = in.External + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretConfig. +func (in *SecretConfig) DeepCopy() *SecretConfig { + if in == nil { + return nil + } + out := new(SecretConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceConfig) DeepCopyInto(out *ServiceConfig) { + *out = *in + if in.CapAdd != nil { + in, out := &in.CapAdd, &out.CapAdd + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.CapDrop != nil { + in, out := &in.CapDrop, &out.CapDrop + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Command != nil { + in, out := &in.Command, &out.Command + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Configs != nil { + in, out := &in.Configs, &out.Configs + *out = make([]ServiceConfigObjConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Deploy.DeepCopyInto(&out.Deploy) + if in.Entrypoint != nil { + in, out := &in.Entrypoint, &out.Entrypoint + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Environment != nil { + in, out := &in.Environment, &out.Environment + *out = make(map[string]*string, len(*in)) + for key, val := range *in { + if val == nil { + (*out)[key] = nil + } else { + outVal := *val + (*out)[key] = &outVal + } + } + } + if in.ExtraHosts != nil { + in, out := &in.ExtraHosts, &out.ExtraHosts + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.HealthCheck != nil { + in, out := &in.HealthCheck, &out.HealthCheck + if *in == nil { + *out = nil + } else { + *out = new(HealthCheckConfig) + (*in).DeepCopyInto(*out) + } + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]ServicePortConfig, len(*in)) + copy(*out, *in) + } + if in.Secrets != nil { + in, out := &in.Secrets, &out.Secrets + *out = make([]ServiceSecretConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.StopGracePeriod != nil { + in, out := &in.StopGracePeriod, &out.StopGracePeriod + if *in == nil { + *out = nil + } else { + *out = new(time.Duration) + **out = **in + } + } + if in.Tmpfs != nil { + in, out := &in.Tmpfs, &out.Tmpfs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.User != nil { + in, out := &in.User, &out.User + if *in == nil { + *out = nil + } else { + *out = new(int64) + **out = **in + } + } + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]ServiceVolumeConfig, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceConfig. +func (in *ServiceConfig) DeepCopy() *ServiceConfig { + if in == nil { + return nil + } + out := new(ServiceConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceConfigObjConfig) DeepCopyInto(out *ServiceConfigObjConfig) { + *out = *in + if in.Mode != nil { + in, out := &in.Mode, &out.Mode + if *in == nil { + *out = nil + } else { + *out = new(uint32) + **out = **in + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceConfigObjConfig. +func (in *ServiceConfigObjConfig) DeepCopy() *ServiceConfigObjConfig { + if in == nil { + return nil + } + out := new(ServiceConfigObjConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServicePortConfig) DeepCopyInto(out *ServicePortConfig) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServicePortConfig. +func (in *ServicePortConfig) DeepCopy() *ServicePortConfig { + if in == nil { + return nil + } + out := new(ServicePortConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceSecretConfig) DeepCopyInto(out *ServiceSecretConfig) { + *out = *in + if in.Mode != nil { + in, out := &in.Mode, &out.Mode + if *in == nil { + *out = nil + } else { + *out = new(uint32) + **out = **in + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceSecretConfig. +func (in *ServiceSecretConfig) DeepCopy() *ServiceSecretConfig { + if in == nil { + return nil + } + out := new(ServiceSecretConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceVolumeConfig) DeepCopyInto(out *ServiceVolumeConfig) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceVolumeConfig. +func (in *ServiceVolumeConfig) DeepCopy() *ServiceVolumeConfig { + if in == nil { + return nil + } + out := new(ServiceVolumeConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StackSpec) DeepCopyInto(out *StackSpec) { + *out = *in + if in.Services != nil { + in, out := &in.Services, &out.Services + *out = make([]ServiceConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Secrets != nil { + in, out := &in.Secrets, &out.Secrets + *out = make(map[string]SecretConfig, len(*in)) + for key, val := range *in { + newVal := new(SecretConfig) + val.DeepCopyInto(newVal) + (*out)[key] = *newVal + } + } + if in.Configs != nil { + in, out := &in.Configs, &out.Configs + *out = make(map[string]ConfigObjConfig, len(*in)) + for key, val := range *in { + newVal := new(ConfigObjConfig) + val.DeepCopyInto(newVal) + (*out)[key] = *newVal + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StackSpec. +func (in *StackSpec) DeepCopy() *StackSpec { + if in == nil { + return nil + } + out := new(StackSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *UpdateConfig) DeepCopyInto(out *UpdateConfig) { + *out = *in + if in.Parallelism != nil { + in, out := &in.Parallelism, &out.Parallelism + if *in == nil { + *out = nil + } else { + *out = new(uint64) + **out = **in + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UpdateConfig. +func (in *UpdateConfig) DeepCopy() *UpdateConfig { + if in == nil { + return nil + } + out := new(UpdateConfig) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/doc.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/doc.go new file mode 100644 index 000000000000..2864b9b3f78b --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/doc.go @@ -0,0 +1,8 @@ +// Api versions allow the api contract for a resource to be changed while keeping +// backward compatibility by support multiple concurrent versions +// of the same resource + +// Package v1alpha3 is the current in dev version of the stack, containing evolution on top of v1beta2 structured spec +// +k8s:openapi-gen=true +// +k8s:conversion-gen=github.com/docker/compose-on-kubernetes/api/compose/v1beta2 +package v1alpha3 diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/owner.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/owner.go new file mode 100644 index 000000000000..05d4ba8affca --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/owner.go @@ -0,0 +1,30 @@ +package v1alpha3 + +import ( + "github.com/docker/compose-on-kubernetes/api/compose/impersonation" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// Owner describes the user who created the stack +type Owner struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Owner impersonation.Config `json:"owner,omitempty"` +} + +func (o *Owner) clone() *Owner { + if o == nil { + return nil + } + result := new(Owner) + result.TypeMeta = o.TypeMeta + result.ObjectMeta = o.ObjectMeta + result.Owner = *result.Owner.Clone() + return result +} + +// DeepCopyObject clones the owner +func (o *Owner) DeepCopyObject() runtime.Object { + return o.clone() +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/register.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/register.go new file mode 100644 index 000000000000..b8c4d28b9ed2 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/register.go @@ -0,0 +1,42 @@ +package v1alpha3 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the name of the compose group +const GroupName = "compose.docker.com" + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha3"} + // SchemeBuilder is the scheme builder + SchemeBuilder runtime.SchemeBuilder + localSchemeBuilder = &SchemeBuilder + // AddToScheme adds to scheme + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + localSchemeBuilder.Register(addKnownTypes) +} + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &Stack{}, + &StackList{}, + &Owner{}, + &ComposeFile{}, + &Scale{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} + +// GroupResource takes an unqualified resource and returns a Group qualified GroupResource +func GroupResource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/scale.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/scale.go new file mode 100644 index 000000000000..b1f685bc7603 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/scale.go @@ -0,0 +1,29 @@ +package v1alpha3 + +import ( + "github.com/docker/compose-on-kubernetes/api/compose/clone" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// Scale contains the current/desired replica count for services in a stack. +type Scale struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec map[string]int `json:"spec,omitempty"` + Status map[string]int `json:"status,omitempty"` +} + +func (s *Scale) clone() *Scale { + return &Scale{ + TypeMeta: s.TypeMeta, + ObjectMeta: s.ObjectMeta, + Spec: clone.MapOfStringToInt(s.Spec), + Status: clone.MapOfStringToInt(s.Status), + } +} + +// DeepCopyObject clones the scale +func (s *Scale) DeepCopyObject() runtime.Object { + return s.clone() +} diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/stack.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/stack.go new file mode 100644 index 000000000000..67057ad6df45 --- /dev/null +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/stack.go @@ -0,0 +1,270 @@ +package v1alpha3 + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// StackList is a list of stacks +type StackList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + Items []Stack `json:"items" protobuf:"bytes,2,rep,name=items"` +} + +// Stack is v1alpha3's representation of a Stack +type Stack struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec *StackSpec `json:"spec,omitempty"` + Status *StackStatus `json:"status,omitempty"` +} + +// DeepCopyObject clones the stack +func (s *Stack) DeepCopyObject() runtime.Object { + return s.clone() +} + +// DeepCopyObject clones the stack list +func (s *StackList) DeepCopyObject() runtime.Object { + if s == nil { + return nil + } + result := new(StackList) + result.TypeMeta = s.TypeMeta + result.ListMeta = s.ListMeta + if s.Items == nil { + return result + } + result.Items = make([]Stack, len(s.Items)) + for ix, s := range s.Items { + result.Items[ix] = *s.clone() + } + return result +} + +func (s *Stack) clone() *Stack { + if s == nil { + return nil + } + result := new(Stack) + result.TypeMeta = s.TypeMeta + result.ObjectMeta = s.ObjectMeta + result.Spec = s.Spec.DeepCopy() + result.Status = s.Status.clone() + return result +} + +// StackSpec defines the desired state of Stack +// +k8s:deepcopy-gen=true +type StackSpec struct { + Services []ServiceConfig `json:"services,omitempty"` + Secrets map[string]SecretConfig `json:"secrets,omitempty"` + Configs map[string]ConfigObjConfig `json:"configs,omitempty"` +} + +// ServiceConfig is the configuration of one service +// +k8s:deepcopy-gen=true +type ServiceConfig struct { + Name string `json:"name,omitempty"` + + CapAdd []string `json:"cap_add,omitempty"` + CapDrop []string `json:"cap_drop,omitempty"` + Command []string `json:"command,omitempty"` + Configs []ServiceConfigObjConfig `json:"configs,omitempty"` + Deploy DeployConfig `json:"deploy,omitempty"` + Entrypoint []string `json:"entrypoint,omitempty"` + Environment map[string]*string `json:"environment,omitempty"` + ExtraHosts []string `json:"extra_hosts,omitempty"` + Hostname string `json:"hostname,omitempty"` + HealthCheck *HealthCheckConfig `json:"health_check,omitempty"` + Image string `json:"image,omitempty"` + Ipc string `json:"ipc,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + Pid string `json:"pid,omitempty"` + Ports []ServicePortConfig `json:"ports,omitempty"` + Privileged bool `json:"privileged,omitempty"` + ReadOnly bool `json:"read_only,omitempty"` + Secrets []ServiceSecretConfig `json:"secrets,omitempty"` + StdinOpen bool `json:"stdin_open,omitempty"` + StopGracePeriod *time.Duration `json:"stop_grace_period,omitempty"` + Tmpfs []string `json:"tmpfs,omitempty"` + Tty bool `json:"tty,omitempty"` + User *int64 `json:"user,omitempty"` + Volumes []ServiceVolumeConfig `json:"volumes,omitempty"` + WorkingDir string `json:"working_dir,omitempty"` +} + +// ServicePortConfig is the port configuration for a service +// +k8s:deepcopy-gen=true +type ServicePortConfig struct { + Mode string `json:"mode,omitempty"` + Target uint32 `json:"target,omitempty"` + Published uint32 `json:"published,omitempty"` + Protocol string `json:"protocol,omitempty"` +} + +// FileObjectConfig is a config type for a file used by a service +// +k8s:deepcopy-gen=true +type FileObjectConfig struct { + Name string `json:"name,omitempty"` + File string `json:"file,omitempty"` + External External `json:"external,omitempty"` + Labels map[string]string `json:"labels,omitempty"` +} + +// SecretConfig for a secret +// +k8s:deepcopy-gen=true +type SecretConfig FileObjectConfig + +// ConfigObjConfig is the config for the swarm "Config" object +// +k8s:deepcopy-gen=true +type ConfigObjConfig FileObjectConfig + +// External identifies a Volume or Network as a reference to a resource that is +// not managed, and should already exist. +// External.name is deprecated and replaced by Volume.name +// +k8s:deepcopy-gen=true +type External struct { + Name string `json:"name,omitempty"` + External bool `json:"external,omitempty"` +} + +// FileReferenceConfig for a reference to a swarm file object +// +k8s:deepcopy-gen=true +type FileReferenceConfig struct { + Source string `json:"source,omitempty"` + Target string `json:"target,omitempty"` + UID string `json:"uid,omitempty"` + GID string `json:"gid,omitempty"` + Mode *uint32 `json:"mode,omitempty"` +} + +// ServiceConfigObjConfig is the config obj configuration for a service +// +k8s:deepcopy-gen=true +type ServiceConfigObjConfig FileReferenceConfig + +// ServiceSecretConfig is the secret configuration for a service +// +k8s:deepcopy-gen=true +type ServiceSecretConfig FileReferenceConfig + +// DeployConfig is the deployment configuration for a service +// +k8s:deepcopy-gen=true +type DeployConfig struct { + Mode string `json:"mode,omitempty"` + Replicas *uint64 `json:"replicas,omitempty"` + Labels map[string]string `json:"labels,omitempty"` + UpdateConfig *UpdateConfig `json:"update_config,omitempty"` + Resources Resources `json:"resources,omitempty"` + RestartPolicy *RestartPolicy `json:"restart_policy,omitempty"` + Placement Placement `json:"placement,omitempty"` +} + +// UpdateConfig is the service update configuration +// +k8s:deepcopy-gen=true +type UpdateConfig struct { + Parallelism *uint64 `json:"paralellism,omitempty"` +} + +// Resources the resource limits and reservations +// +k8s:deepcopy-gen=true +type Resources struct { + Limits *Resource `json:"limits,omitempty"` + Reservations *Resource `json:"reservations,omitempty"` +} + +// Resource is a resource to be limited or reserved +// +k8s:deepcopy-gen=true +type Resource struct { + NanoCPUs string `json:"cpus,omitempty"` + MemoryBytes int64 `json:"memory,omitempty"` +} + +// RestartPolicy is the service restart policy +// +k8s:deepcopy-gen=true +type RestartPolicy struct { + Condition string `json:"condition,omitempty"` +} + +// Placement constraints for the service +// +k8s:deepcopy-gen=true +type Placement struct { + Constraints *Constraints `json:"constraints,omitempty"` +} + +// Constraints lists constraints that can be set on the service +// +k8s:deepcopy-gen=true +type Constraints struct { + OperatingSystem *Constraint + Architecture *Constraint + Hostname *Constraint + MatchLabels map[string]Constraint +} + +// Constraint defines a constraint and it's operator (== or !=) +// +k8s:deepcopy-gen=true +type Constraint struct { + Value string + Operator string +} + +// HealthCheckConfig the healthcheck configuration for a service +// +k8s:deepcopy-gen=true +type HealthCheckConfig struct { + Test []string `json:"test,omitempty"` + Timeout *time.Duration `json:"timeout,omitempty"` + Interval *time.Duration `json:"interval,omitempty"` + Retries *uint64 `json:"retries,omitempty"` +} + +// ServiceVolumeConfig are references to a volume used by a service +// +k8s:deepcopy-gen=true +type ServiceVolumeConfig struct { + Type string `json:"type,omitempty"` + Source string `json:"source,omitempty"` + Target string `json:"target,omitempty"` + ReadOnly bool `json:"read_only,omitempty"` +} + +// StackPhase is the deployment phase of a stack +type StackPhase string + +// These are valid conditions of a stack. +const ( + // StackAvailable means the stack is available. + StackAvailable StackPhase = "Available" + // StackProgressing means the deployment is progressing. + StackProgressing StackPhase = "Progressing" + // StackFailure is added in a stack when one of its members fails to be created + // or deleted. + StackFailure StackPhase = "Failure" + // StackReconciliationPending means the stack has not yet been reconciled + StackReconciliationPending StackPhase = "ReconciliationPending" +) + +// StackStatus defines the observed state of Stack +type StackStatus struct { + // Current condition of the stack. + // +optional + Phase StackPhase `json:"phase,omitempty" protobuf:"bytes,1,opt,name=phase,casttype=StackPhase"` + // A human readable message indicating details about the stack. + // +optional + Message string `json:"message,omitempty" protobuf:"bytes,5,opt,name=message"` +} + +func (s *StackStatus) clone() *StackStatus { + if s == nil { + return nil + } + result := *s + return &result +} + +// Clone clones a Stack +func (s *Stack) Clone() *Stack { + return s.clone() +} From 2e5981d613310dc2ff5b789ff13d198dd8a95169 Mon Sep 17 00:00:00 2001 From: Simon Ferquel Date: Wed, 16 Jan 2019 12:56:37 +0100 Subject: [PATCH 08/60] Handle version v1alpha3 Signed-off-by: Simon Ferquel --- cli/command/stack/kubernetes/cli.go | 2 +- cli/command/stack/kubernetes/client.go | 12 +- cli/command/stack/kubernetes/convert.go | 130 ++++++++++++------- cli/command/stack/kubernetes/convert_test.go | 2 + cli/command/stack/kubernetes/deploy_test.go | 90 ++++++++++++- cli/command/stack/kubernetes/stack.go | 4 +- cli/command/stack/kubernetes/stackclient.go | 92 ++++++++++++- cli/command/system/version.go | 8 +- kubernetes/check.go | 60 +++++++-- kubernetes/check_test.go | 54 ++++++++ 10 files changed, 375 insertions(+), 79 deletions(-) create mode 100644 kubernetes/check_test.go diff --git a/cli/command/stack/kubernetes/cli.go b/cli/command/stack/kubernetes/cli.go index a531846809cc..8f5baf657918 100644 --- a/cli/command/stack/kubernetes/cli.go +++ b/cli/command/stack/kubernetes/cli.go @@ -103,7 +103,7 @@ func WrapCli(dockerCli command.Cli, opts Options) (*KubeCli, error) { } func (c *KubeCli) composeClient() (*Factory, error) { - return NewFactory(c.kubeNamespace, c.kubeConfig, c.clientSet) + return NewFactory(c.kubeNamespace, c.kubeConfig, c.clientSet, c.ClientInfo().HasExperimental) } func (c *KubeCli) checkHostsMatch() error { diff --git a/cli/command/stack/kubernetes/client.go b/cli/command/stack/kubernetes/client.go index 28bb484d3ad3..fd516bb5d20b 100644 --- a/cli/command/stack/kubernetes/client.go +++ b/cli/command/stack/kubernetes/client.go @@ -1,7 +1,7 @@ package kubernetes import ( - kubernetes "github.com/docker/compose-on-kubernetes/api" + "github.com/docker/cli/kubernetes" "github.com/pkg/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kubeclient "k8s.io/client-go/kubernetes" @@ -18,10 +18,11 @@ type Factory struct { coreClientSet corev1.CoreV1Interface appsClientSet appsv1beta2.AppsV1beta2Interface clientSet *kubeclient.Clientset + experimental bool } // NewFactory creates a kubernetes client factory -func NewFactory(namespace string, config *restclient.Config, clientSet *kubeclient.Clientset) (*Factory, error) { +func NewFactory(namespace string, config *restclient.Config, clientSet *kubeclient.Clientset, experimental bool) (*Factory, error) { coreClientSet, err := corev1.NewForConfig(config) if err != nil { return nil, err @@ -38,6 +39,7 @@ func NewFactory(namespace string, config *restclient.Config, clientSet *kubeclie coreClientSet: coreClientSet, appsClientSet: appsClientSet, clientSet: clientSet, + experimental: experimental, }, nil } @@ -83,7 +85,7 @@ func (s *Factory) DaemonSets() typesappsv1beta2.DaemonSetInterface { // Stacks returns a client for Docker's Stack on Kubernetes func (s *Factory) Stacks(allNamespaces bool) (StackClient, error) { - version, err := kubernetes.GetStackAPIVersion(s.clientSet) + version, err := kubernetes.GetStackAPIVersion(s.clientSet.Discovery(), s.experimental) if err != nil { return nil, err } @@ -97,7 +99,9 @@ func (s *Factory) Stacks(allNamespaces bool) (StackClient, error) { return newStackV1Beta1(s.config, namespace) case kubernetes.StackAPIV1Beta2: return newStackV1Beta2(s.config, namespace) + case kubernetes.StackAPIV1Alpha3: + return newStackV1Alpha3(s.config, namespace) default: - return nil, errors.Errorf("no supported Stack API version") + return nil, errors.Errorf("unsupported stack API version: %q", version) } } diff --git a/cli/command/stack/kubernetes/convert.go b/cli/command/stack/kubernetes/convert.go index c76be93cad1f..21500d7c5838 100644 --- a/cli/command/stack/kubernetes/convert.go +++ b/cli/command/stack/kubernetes/convert.go @@ -11,6 +11,7 @@ import ( "github.com/docker/cli/cli/compose/schema" composeTypes "github.com/docker/cli/cli/compose/types" composetypes "github.com/docker/cli/cli/compose/types" + latest "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" "github.com/pkg/errors" @@ -24,8 +25,8 @@ func NewStackConverter(version string) (StackConverter, error) { switch version { case "v1beta1": return stackV1Beta1Converter{}, nil - case "v1beta2": - return stackV1Beta2Converter{}, nil + case "v1beta2", "v1alpha3": + return stackV1Beta2OrHigherConverter{}, nil default: return nil, errors.Errorf("stack version %s unsupported", version) } @@ -61,9 +62,9 @@ func (s stackV1Beta1Converter) FromCompose(stderr io.Writer, name string, cfg *c return st, nil } -type stackV1Beta2Converter struct{} +type stackV1Beta2OrHigherConverter struct{} -func (s stackV1Beta2Converter) FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) { +func (s stackV1Beta2OrHigherConverter) FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) { return fromCompose(stderr, name, cfg) } @@ -113,7 +114,38 @@ func stackToV1beta1(s Stack) *v1beta1.Stack { } } -func stackFromV1beta2(in *v1beta2.Stack) Stack { +func stackFromV1beta2(in *v1beta2.Stack) (Stack, error) { + var spec *latest.StackSpec + if in.Spec != nil { + spec = &latest.StackSpec{} + if err := latest.Convert_v1beta2_StackSpec_To_v1alpha3_StackSpec(in.Spec, spec, nil); err != nil { + return Stack{}, err + } + } + return Stack{ + Name: in.ObjectMeta.Name, + Namespace: in.ObjectMeta.Namespace, + Spec: spec, + }, nil +} + +func stackToV1beta2(s Stack) (*v1beta2.Stack, error) { + var spec *v1beta2.StackSpec + if s.Spec != nil { + spec = &v1beta2.StackSpec{} + if err := latest.Convert_v1alpha3_StackSpec_To_v1beta2_StackSpec(s.Spec, spec, nil); err != nil { + return nil, err + } + } + return &v1beta2.Stack{ + ObjectMeta: metav1.ObjectMeta{ + Name: s.Name, + }, + Spec: spec, + }, nil +} + +func stackFromV1alpha3(in *latest.Stack) Stack { return Stack{ Name: in.ObjectMeta.Name, Namespace: in.ObjectMeta.Namespace, @@ -121,8 +153,8 @@ func stackFromV1beta2(in *v1beta2.Stack) Stack { } } -func stackToV1beta2(s Stack) *v1beta2.Stack { - return &v1beta2.Stack{ +func stackToV1alpha3(s Stack) *latest.Stack { + return &latest.Stack{ ObjectMeta: metav1.ObjectMeta{ Name: s.Name, }, @@ -130,32 +162,32 @@ func stackToV1beta2(s Stack) *v1beta2.Stack { } } -func fromComposeConfig(stderr io.Writer, c *composeTypes.Config) *v1beta2.StackSpec { +func fromComposeConfig(stderr io.Writer, c *composeTypes.Config) *latest.StackSpec { if c == nil { return nil } warnUnsupportedFeatures(stderr, c) - serviceConfigs := make([]v1beta2.ServiceConfig, len(c.Services)) + serviceConfigs := make([]latest.ServiceConfig, len(c.Services)) for i, s := range c.Services { serviceConfigs[i] = fromComposeServiceConfig(s) } - return &v1beta2.StackSpec{ + return &latest.StackSpec{ Services: serviceConfigs, Secrets: fromComposeSecrets(c.Secrets), Configs: fromComposeConfigs(c.Configs), } } -func fromComposeSecrets(s map[string]composeTypes.SecretConfig) map[string]v1beta2.SecretConfig { +func fromComposeSecrets(s map[string]composeTypes.SecretConfig) map[string]latest.SecretConfig { if s == nil { return nil } - m := map[string]v1beta2.SecretConfig{} + m := map[string]latest.SecretConfig{} for key, value := range s { - m[key] = v1beta2.SecretConfig{ + m[key] = latest.SecretConfig{ Name: value.Name, File: value.File, - External: v1beta2.External{ + External: latest.External{ Name: value.External.Name, External: value.External.External, }, @@ -165,16 +197,16 @@ func fromComposeSecrets(s map[string]composeTypes.SecretConfig) map[string]v1bet return m } -func fromComposeConfigs(s map[string]composeTypes.ConfigObjConfig) map[string]v1beta2.ConfigObjConfig { +func fromComposeConfigs(s map[string]composeTypes.ConfigObjConfig) map[string]latest.ConfigObjConfig { if s == nil { return nil } - m := map[string]v1beta2.ConfigObjConfig{} + m := map[string]latest.ConfigObjConfig{} for key, value := range s { - m[key] = v1beta2.ConfigObjConfig{ + m[key] = latest.ConfigObjConfig{ Name: value.Name, File: value.File, - External: v1beta2.External{ + External: latest.External{ Name: value.External.Name, External: value.External.External, }, @@ -184,7 +216,7 @@ func fromComposeConfigs(s map[string]composeTypes.ConfigObjConfig) map[string]v1 return m } -func fromComposeServiceConfig(s composeTypes.ServiceConfig) v1beta2.ServiceConfig { +func fromComposeServiceConfig(s composeTypes.ServiceConfig) latest.ServiceConfig { var userID *int64 if s.User != "" { numerical, err := strconv.Atoi(s.User) @@ -193,13 +225,13 @@ func fromComposeServiceConfig(s composeTypes.ServiceConfig) v1beta2.ServiceConfi userID = &unixUserID } } - return v1beta2.ServiceConfig{ + return latest.ServiceConfig{ Name: s.Name, CapAdd: s.CapAdd, CapDrop: s.CapDrop, Command: s.Command, Configs: fromComposeServiceConfigs(s.Configs), - Deploy: v1beta2.DeployConfig{ + Deploy: latest.DeployConfig{ Mode: s.Deploy.Mode, Replicas: s.Deploy.Replicas, Labels: s.Deploy.Labels, @@ -231,13 +263,13 @@ func fromComposeServiceConfig(s composeTypes.ServiceConfig) v1beta2.ServiceConfi } } -func fromComposePorts(ports []composeTypes.ServicePortConfig) []v1beta2.ServicePortConfig { +func fromComposePorts(ports []composeTypes.ServicePortConfig) []latest.ServicePortConfig { if ports == nil { return nil } - p := make([]v1beta2.ServicePortConfig, len(ports)) + p := make([]latest.ServicePortConfig, len(ports)) for i, port := range ports { - p[i] = v1beta2.ServicePortConfig{ + p[i] = latest.ServicePortConfig{ Mode: port.Mode, Target: port.Target, Published: port.Published, @@ -247,13 +279,13 @@ func fromComposePorts(ports []composeTypes.ServicePortConfig) []v1beta2.ServiceP return p } -func fromComposeServiceSecrets(secrets []composeTypes.ServiceSecretConfig) []v1beta2.ServiceSecretConfig { +func fromComposeServiceSecrets(secrets []composeTypes.ServiceSecretConfig) []latest.ServiceSecretConfig { if secrets == nil { return nil } - c := make([]v1beta2.ServiceSecretConfig, len(secrets)) + c := make([]latest.ServiceSecretConfig, len(secrets)) for i, secret := range secrets { - c[i] = v1beta2.ServiceSecretConfig{ + c[i] = latest.ServiceSecretConfig{ Source: secret.Source, Target: secret.Target, UID: secret.UID, @@ -263,13 +295,13 @@ func fromComposeServiceSecrets(secrets []composeTypes.ServiceSecretConfig) []v1b return c } -func fromComposeServiceConfigs(configs []composeTypes.ServiceConfigObjConfig) []v1beta2.ServiceConfigObjConfig { +func fromComposeServiceConfigs(configs []composeTypes.ServiceConfigObjConfig) []latest.ServiceConfigObjConfig { if configs == nil { return nil } - c := make([]v1beta2.ServiceConfigObjConfig, len(configs)) + c := make([]latest.ServiceConfigObjConfig, len(configs)) for i, config := range configs { - c[i] = v1beta2.ServiceConfigObjConfig{ + c[i] = latest.ServiceConfigObjConfig{ Source: config.Source, Target: config.Target, UID: config.UID, @@ -279,11 +311,11 @@ func fromComposeServiceConfigs(configs []composeTypes.ServiceConfigObjConfig) [] return c } -func fromComposeHealthcheck(h *composeTypes.HealthCheckConfig) *v1beta2.HealthCheckConfig { +func fromComposeHealthcheck(h *composeTypes.HealthCheckConfig) *latest.HealthCheckConfig { if h == nil { return nil } - return &v1beta2.HealthCheckConfig{ + return &latest.HealthCheckConfig{ Test: h.Test, Timeout: composetypes.ConvertDurationPtr(h.Timeout), Interval: composetypes.ConvertDurationPtr(h.Interval), @@ -291,8 +323,8 @@ func fromComposeHealthcheck(h *composeTypes.HealthCheckConfig) *v1beta2.HealthCh } } -func fromComposePlacement(p composeTypes.Placement) v1beta2.Placement { - return v1beta2.Placement{ +func fromComposePlacement(p composeTypes.Placement) latest.Placement { + return latest.Placement{ Constraints: fromComposeConstraints(p.Constraints), } } @@ -306,18 +338,18 @@ const ( swarmLabelPrefix = "node.labels." ) -func fromComposeConstraints(s []string) *v1beta2.Constraints { +func fromComposeConstraints(s []string) *latest.Constraints { if len(s) == 0 { return nil } - constraints := &v1beta2.Constraints{} + constraints := &latest.Constraints{} for _, constraint := range s { matches := constraintEquals.FindStringSubmatch(constraint) if len(matches) == 4 { key := matches[1] operator := matches[2] value := matches[3] - constraint := &v1beta2.Constraint{ + constraint := &latest.Constraint{ Operator: operator, Value: value, } @@ -330,7 +362,7 @@ func fromComposeConstraints(s []string) *v1beta2.Constraints { constraints.Hostname = constraint case strings.HasPrefix(key, swarmLabelPrefix): if constraints.MatchLabels == nil { - constraints.MatchLabels = map[string]v1beta2.Constraint{} + constraints.MatchLabels = map[string]latest.Constraint{} } constraints.MatchLabels[strings.TrimPrefix(key, swarmLabelPrefix)] = *constraint } @@ -339,48 +371,48 @@ func fromComposeConstraints(s []string) *v1beta2.Constraints { return constraints } -func fromComposeResources(r composeTypes.Resources) v1beta2.Resources { - return v1beta2.Resources{ +func fromComposeResources(r composeTypes.Resources) latest.Resources { + return latest.Resources{ Limits: fromComposeResourcesResource(r.Limits), Reservations: fromComposeResourcesResource(r.Reservations), } } -func fromComposeResourcesResource(r *composeTypes.Resource) *v1beta2.Resource { +func fromComposeResourcesResource(r *composeTypes.Resource) *latest.Resource { if r == nil { return nil } - return &v1beta2.Resource{ + return &latest.Resource{ MemoryBytes: int64(r.MemoryBytes), NanoCPUs: r.NanoCPUs, } } -func fromComposeUpdateConfig(u *composeTypes.UpdateConfig) *v1beta2.UpdateConfig { +func fromComposeUpdateConfig(u *composeTypes.UpdateConfig) *latest.UpdateConfig { if u == nil { return nil } - return &v1beta2.UpdateConfig{ + return &latest.UpdateConfig{ Parallelism: u.Parallelism, } } -func fromComposeRestartPolicy(r *composeTypes.RestartPolicy) *v1beta2.RestartPolicy { +func fromComposeRestartPolicy(r *composeTypes.RestartPolicy) *latest.RestartPolicy { if r == nil { return nil } - return &v1beta2.RestartPolicy{ + return &latest.RestartPolicy{ Condition: r.Condition, } } -func fromComposeServiceVolumeConfig(vs []composeTypes.ServiceVolumeConfig) []v1beta2.ServiceVolumeConfig { +func fromComposeServiceVolumeConfig(vs []composeTypes.ServiceVolumeConfig) []latest.ServiceVolumeConfig { if vs == nil { return nil } - volumes := []v1beta2.ServiceVolumeConfig{} + volumes := []latest.ServiceVolumeConfig{} for _, v := range vs { - volumes = append(volumes, v1beta2.ServiceVolumeConfig{ + volumes = append(volumes, latest.ServiceVolumeConfig{ Type: v.Type, Source: v.Source, Target: v.Target, diff --git a/cli/command/stack/kubernetes/convert_test.go b/cli/command/stack/kubernetes/convert_test.go index 5e0f8dc54d6c..1c4b2d3e33eb 100644 --- a/cli/command/stack/kubernetes/convert_test.go +++ b/cli/command/stack/kubernetes/convert_test.go @@ -15,4 +15,6 @@ func TestNewStackConverter(t *testing.T) { assert.NilError(t, err) _, err = NewStackConverter("v1beta2") assert.NilError(t, err) + _, err = NewStackConverter("v1alpha3") + assert.NilError(t, err) } diff --git a/cli/command/stack/kubernetes/deploy_test.go b/cli/command/stack/kubernetes/deploy_test.go index 52e607c05059..85d1a5ff7d3a 100644 --- a/cli/command/stack/kubernetes/deploy_test.go +++ b/cli/command/stack/kubernetes/deploy_test.go @@ -4,8 +4,10 @@ import ( "errors" "testing" + composev1alpha3 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3" composev1beta1 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1" composev1beta2 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2" + "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" "gotest.tools/assert" @@ -31,11 +33,11 @@ configs: test: file: testdata/config `, - Spec: &v1beta2.StackSpec{ - Configs: map[string]v1beta2.ConfigObjConfig{ + Spec: &v1alpha3.StackSpec{ + Configs: map[string]v1alpha3.ConfigObjConfig{ "test": {Name: "test", File: "testdata/config"}, }, - Secrets: map[string]v1beta2.SecretConfig{ + Secrets: map[string]v1alpha3.SecretConfig{ "test": {Name: "test", File: "testdata/secret"}, }, }, @@ -86,6 +88,24 @@ func TestCreateChildResourcesV1Beta2(t *testing.T) { checkOwnerReferences(t, s.ObjectMeta, "test", v1beta2.SchemeGroupVersion.String()) } +func TestCreateChildResourcesV1Alpha3(t *testing.T) { + k8sclientSet := fake.NewSimpleClientset() + stack := testStack() + configs := k8sclientSet.CoreV1().ConfigMaps("test") + secrets := k8sclientSet.CoreV1().Secrets("test") + assert.NilError(t, createResources( + stack, + &stackV1Alpha3{stacks: &fakeV1alpha3Client{}}, + configs, + secrets)) + c, err := configs.Get("test", metav1.GetOptions{}) + assert.NilError(t, err) + checkOwnerReferences(t, c.ObjectMeta, "test", v1alpha3.SchemeGroupVersion.String()) + s, err := secrets.Get("test", metav1.GetOptions{}) + assert.NilError(t, err) + checkOwnerReferences(t, s.ObjectMeta, "test", v1alpha3.SchemeGroupVersion.String()) +} + func TestCreateChildResourcesWithStackCreationErrorV1Beta1(t *testing.T) { k8sclientSet := fake.NewSimpleClientset() stack := testStack() @@ -120,6 +140,23 @@ func TestCreateChildResourcesWithStackCreationErrorV1Beta2(t *testing.T) { assert.Check(t, kerrors.IsNotFound(err)) } +func TestCreateChildResourcesWithStackCreationErrorV1Alpha3(t *testing.T) { + k8sclientSet := fake.NewSimpleClientset() + stack := testStack() + configs := k8sclientSet.CoreV1().ConfigMaps("test") + secrets := k8sclientSet.CoreV1().Secrets("test") + err := createResources( + stack, + &stackV1Alpha3{stacks: &fakeV1alpha3Client{errorOnCreate: true}}, + configs, + secrets) + assert.Error(t, err, "some error") + _, err = configs.Get("test", metav1.GetOptions{}) + assert.Check(t, kerrors.IsNotFound(err)) + _, err = secrets.Get("test", metav1.GetOptions{}) + assert.Check(t, kerrors.IsNotFound(err)) +} + type fakeV1beta1Client struct { errorOnCreate bool } @@ -213,3 +250,50 @@ func (c *fakeV1beta2Client) Patch(name string, pt types.PatchType, data []byte, func (c *fakeV1beta2Client) WithSkipValidation() composev1beta2.StackInterface { return c } + +type fakeV1alpha3Client struct { + errorOnCreate bool +} + +func (c *fakeV1alpha3Client) Create(s *v1alpha3.Stack) (*v1alpha3.Stack, error) { + if c.errorOnCreate { + return nil, errors.New("some error") + } + return s, nil +} + +func (c *fakeV1alpha3Client) Update(*v1alpha3.Stack) (*v1alpha3.Stack, error) { + return nil, nil +} + +func (c *fakeV1alpha3Client) UpdateStatus(*v1alpha3.Stack) (*v1alpha3.Stack, error) { + return nil, nil +} + +func (c *fakeV1alpha3Client) Delete(name string, options *metav1.DeleteOptions) error { + return nil +} + +func (c *fakeV1alpha3Client) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error { + return nil +} + +func (c *fakeV1alpha3Client) Get(name string, options metav1.GetOptions) (*v1alpha3.Stack, error) { + return nil, kerrors.NewNotFound(v1beta1.SchemeGroupVersion.WithResource("stacks").GroupResource(), name) +} + +func (c *fakeV1alpha3Client) List(opts metav1.ListOptions) (*v1alpha3.StackList, error) { + return nil, nil +} + +func (c *fakeV1alpha3Client) Watch(opts metav1.ListOptions) (watch.Interface, error) { + return nil, nil +} + +func (c *fakeV1alpha3Client) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*v1alpha3.Stack, error) { + return nil, nil +} + +func (c *fakeV1alpha3Client) WithSkipValidation() composev1alpha3.StackInterface { + return c +} diff --git a/cli/command/stack/kubernetes/stack.go b/cli/command/stack/kubernetes/stack.go index 2a0ee16a5bf6..e368d718de9e 100644 --- a/cli/command/stack/kubernetes/stack.go +++ b/cli/command/stack/kubernetes/stack.go @@ -5,7 +5,7 @@ import ( "path/filepath" "sort" - "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" + latest "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" "github.com/docker/compose-on-kubernetes/api/labels" apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -17,7 +17,7 @@ type Stack struct { Name string Namespace string ComposeFile string - Spec *v1beta2.StackSpec + Spec *latest.StackSpec } type childResource interface { diff --git a/cli/command/stack/kubernetes/stackclient.go b/cli/command/stack/kubernetes/stackclient.go index 513b4389572a..53670e4d80c4 100644 --- a/cli/command/stack/kubernetes/stackclient.go +++ b/cli/command/stack/kubernetes/stackclient.go @@ -3,8 +3,10 @@ package kubernetes import ( "fmt" + composev1alpha3 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1alpha3" composev1beta1 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1" composev1beta2 "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta2" + "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" "github.com/docker/compose-on-kubernetes/api/labels" @@ -123,7 +125,7 @@ func verify(services corev1.ServiceInterface, stackName string, service string) // stackV1Beta2 implements stackClient interface and talks to compose component v1beta2. type stackV1Beta2 struct { - stackV1Beta2Converter + stackV1Beta2OrHigherConverter stacks composev1beta2.StackInterface } @@ -136,17 +138,21 @@ func newStackV1Beta2(config *rest.Config, namespace string) (*stackV1Beta2, erro } func (s *stackV1Beta2) CreateOrUpdate(internalStack Stack, childResources []childResource) error { - // If it already exists, update the stack var ( stack *v1beta2.Stack err error ) + resolved, err := stackToV1beta2(internalStack) + if err != nil { + deleteChildResources(childResources) + return err + } if stack, err = s.stacks.Get(internalStack.Name, metav1.GetOptions{}); err == nil { - stack.Spec = internalStack.Spec + stack.Spec = resolved.Spec stack, err = s.stacks.Update(stack) } else { // Or create it - stack, err = s.stacks.Create(stackToV1beta2(internalStack)) + stack, err = s.stacks.Create(resolved) } if err != nil { deleteChildResources(childResources) @@ -173,7 +179,7 @@ func (s *stackV1Beta2) Get(name string) (Stack, error) { if err != nil { return Stack{}, err } - return stackFromV1beta2(stackBeta2), nil + return stackFromV1beta2(stackBeta2) } func (s *stackV1Beta2) List(opts metav1.ListOptions) ([]Stack, error) { @@ -183,7 +189,9 @@ func (s *stackV1Beta2) List(opts metav1.ListOptions) ([]Stack, error) { } stacks := make([]Stack, len(list.Items)) for i := range list.Items { - stacks[i] = stackFromV1beta2(&list.Items[i]) + if stacks[i], err = stackFromV1beta2(&list.Items[i]); err != nil { + return nil, err + } } return stacks, nil } @@ -192,3 +200,75 @@ func (s *stackV1Beta2) List(opts metav1.ListOptions) ([]Stack, error) { func (s *stackV1Beta2) IsColliding(servicesClient corev1.ServiceInterface, st Stack) error { return nil } + +// stackV1Beta2 implements stackClient interface and talks to compose component v1beta2. +type stackV1Alpha3 struct { + stackV1Beta2OrHigherConverter + stacks composev1alpha3.StackInterface +} + +func newStackV1Alpha3(config *rest.Config, namespace string) (*stackV1Alpha3, error) { + client, err := composev1alpha3.NewForConfig(config) + if err != nil { + return nil, err + } + return &stackV1Alpha3{stacks: client.Stacks(namespace)}, nil +} + +func (s *stackV1Alpha3) CreateOrUpdate(internalStack Stack, childResources []childResource) error { + var ( + stack *v1alpha3.Stack + err error + ) + resolved := stackToV1alpha3(internalStack) + if stack, err = s.stacks.Get(internalStack.Name, metav1.GetOptions{}); err == nil { + stack.Spec = resolved.Spec + stack, err = s.stacks.Update(stack) + } else { + // Or create it + stack, err = s.stacks.Create(resolved) + } + if err != nil { + deleteChildResources(childResources) + return err + } + blockOwnerDeletion := true + isController := true + return setChildResourcesOwner(childResources, metav1.OwnerReference{ + APIVersion: v1alpha3.SchemeGroupVersion.String(), + Kind: "Stack", + Name: stack.Name, + UID: stack.UID, + BlockOwnerDeletion: &blockOwnerDeletion, + Controller: &isController, + }) +} + +func (s *stackV1Alpha3) Delete(name string) error { + return s.stacks.Delete(name, &metav1.DeleteOptions{}) +} + +func (s *stackV1Alpha3) Get(name string) (Stack, error) { + stackAlpha3, err := s.stacks.Get(name, metav1.GetOptions{}) + if err != nil { + return Stack{}, err + } + return stackFromV1alpha3(stackAlpha3), nil +} + +func (s *stackV1Alpha3) List(opts metav1.ListOptions) ([]Stack, error) { + list, err := s.stacks.List(opts) + if err != nil { + return nil, err + } + stacks := make([]Stack, len(list.Items)) + for i := range list.Items { + stacks[i] = stackFromV1alpha3(&list.Items[i]) + } + return stacks, nil +} + +// IsColliding is handle server side with the compose api v1beta2, so nothing to do here +func (s *stackV1Alpha3) IsColliding(servicesClient corev1.ServiceInterface, st Stack) error { + return nil +} diff --git a/cli/command/system/version.go b/cli/command/system/version.go index b6c7db30cf7b..a8f0beb1fdc0 100644 --- a/cli/command/system/version.go +++ b/cli/command/system/version.go @@ -12,8 +12,8 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" kubecontext "github.com/docker/cli/cli/context/kubernetes" + "github.com/docker/cli/kubernetes" "github.com/docker/cli/templates" - kubernetes "github.com/docker/compose-on-kubernetes/api" "github.com/docker/docker/api/types" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -260,13 +260,13 @@ func getKubernetesVersion(dockerCli command.Cli, kubeConfig string) *kubernetesV logrus.Debugf("failed to get Kubernetes client: %s", err) return &version } - version.StackAPI = getStackVersion(kubeClient) + version.StackAPI = getStackVersion(kubeClient, dockerCli.ClientInfo().HasExperimental) version.Kubernetes = getKubernetesServerVersion(kubeClient) return &version } -func getStackVersion(client *kubernetesClient.Clientset) string { - apiVersion, err := kubernetes.GetStackAPIVersion(client) +func getStackVersion(client *kubernetesClient.Clientset, experimental bool) string { + apiVersion, err := kubernetes.GetStackAPIVersion(client, experimental) if err != nil { logrus.Debugf("failed to get Stack API version: %s", err) return "Unknown" diff --git a/kubernetes/check.go b/kubernetes/check.go index 8d347280fdc2..6a676fa1dc21 100644 --- a/kubernetes/check.go +++ b/kubernetes/check.go @@ -1,20 +1,60 @@ package kubernetes -import api "github.com/docker/compose-on-kubernetes/api" +import ( + apiv1alpha3 "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" + apiv1beta1 "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" + apiv1beta2 "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" + "github.com/pkg/errors" + apimachinerymetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" +) // StackVersion represents the detected Compose Component on Kubernetes side. -// Deprecated: Use github.com/docker/compose-on-kubernetes/api.StackVersion instead -type StackVersion = api.StackVersion +type StackVersion string const ( // StackAPIV1Beta1 is returned if it's the most recent version available. - // Deprecated: Use github.com/docker/compose-on-kubernetes/api.StackAPIV1Beta1 instead - StackAPIV1Beta1 = api.StackAPIV1Beta1 + StackAPIV1Beta1 = StackVersion("v1beta1") // StackAPIV1Beta2 is returned if it's the most recent version available. - // Deprecated: Use github.com/docker/compose-on-kubernetes/api.StackAPIV1Beta2 instead - StackAPIV1Beta2 = api.StackAPIV1Beta2 + StackAPIV1Beta2 = StackVersion("v1beta2") + // StackAPIV1Alpha3 is returned if it's the most recent version available, and experimental flag is on. + StackAPIV1Alpha3 = StackVersion("v1alpha3") ) -// GetStackAPIVersion returns the most recent stack API installed. -// Deprecated: Use github.com/docker/compose-on-kubernetes/api.GetStackAPIVersion instead -var GetStackAPIVersion = api.GetStackAPIVersion +// GetStackAPIVersion returns the most appropriate stack API version installed. +func GetStackAPIVersion(serverGroups discovery.ServerGroupsInterface, experimental bool) (StackVersion, error) { + groups, err := serverGroups.ServerGroups() + if err != nil { + return "", err + } + + return getAPIVersion(groups, experimental) +} + +func getAPIVersion(groups *metav1.APIGroupList, experimental bool) (StackVersion, error) { + switch { + case experimental && findVersion(apiv1alpha3.SchemeGroupVersion, groups.Groups): + return StackAPIV1Alpha3, nil + case findVersion(apiv1beta2.SchemeGroupVersion, groups.Groups): + return StackAPIV1Beta2, nil + case findVersion(apiv1beta1.SchemeGroupVersion, groups.Groups): + return StackAPIV1Beta1, nil + default: + return "", errors.New("failed to find a Stack API version") + } +} + +func findVersion(stackAPI schema.GroupVersion, groups []apimachinerymetav1.APIGroup) bool { + for _, group := range groups { + if group.Name == stackAPI.Group { + for _, version := range group.Versions { + if version.Version == stackAPI.Version { + return true + } + } + } + } + return false +} diff --git a/kubernetes/check_test.go b/kubernetes/check_test.go new file mode 100644 index 000000000000..28a9029f06d4 --- /dev/null +++ b/kubernetes/check_test.go @@ -0,0 +1,54 @@ +package kubernetes + +import ( + "testing" + + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestGetStackAPIVersion(t *testing.T) { + var tests = []struct { + description string + groups *metav1.APIGroupList + experimental bool + err bool + expectedStack StackVersion + }{ + {"no stack api", makeGroups(), false, true, ""}, + {"v1beta1", makeGroups(groupVersion{"compose.docker.com", []string{"v1beta1"}}), false, false, StackAPIV1Beta1}, + {"v1beta2", makeGroups(groupVersion{"compose.docker.com", []string{"v1beta2"}}), false, false, StackAPIV1Beta2}, + {"most recent has precedence", makeGroups(groupVersion{"compose.docker.com", []string{"v1beta1", "v1beta2"}}), false, false, StackAPIV1Beta2}, + {"most recent has precedence", makeGroups(groupVersion{"compose.docker.com", []string{"v1beta1", "v1beta2", "v1alpha3"}}), false, false, StackAPIV1Beta2}, + {"most recent has precedence", makeGroups(groupVersion{"compose.docker.com", []string{"v1beta1", "v1beta2", "v1alpha3"}}), true, false, StackAPIV1Alpha3}, + } + + for _, test := range tests { + version, err := getAPIVersion(test.groups, test.experimental) + if test.err { + assert.ErrorContains(t, err, "") + } else { + assert.NilError(t, err) + } + assert.Check(t, is.Equal(test.expectedStack, version)) + } +} + +type groupVersion struct { + name string + versions []string +} + +func makeGroups(versions ...groupVersion) *metav1.APIGroupList { + groups := make([]metav1.APIGroup, len(versions)) + for i := range versions { + groups[i].Name = versions[i].name + for _, v := range versions[i].versions { + groups[i].Versions = append(groups[i].Versions, metav1.GroupVersionForDiscovery{Version: v}) + } + } + return &metav1.APIGroupList{ + Groups: groups, + } +} From 820b6f174206281c842c602174dd5b1f698356ee Mon Sep 17 00:00:00 2001 From: Simon Ferquel Date: Fri, 25 Jan 2019 09:58:31 +0100 Subject: [PATCH 09/60] Added stack conversion tests Signed-off-by: Simon Ferquel --- cli/command/stack/kubernetes/convert_test.go | 143 +++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/cli/command/stack/kubernetes/convert_test.go b/cli/command/stack/kubernetes/convert_test.go index 1c4b2d3e33eb..3f7f9e967c47 100644 --- a/cli/command/stack/kubernetes/convert_test.go +++ b/cli/command/stack/kubernetes/convert_test.go @@ -1,10 +1,15 @@ package kubernetes import ( + "path/filepath" "testing" + "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" + "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" + "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" "gotest.tools/assert" is "gotest.tools/assert/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestNewStackConverter(t *testing.T) { @@ -18,3 +23,141 @@ func TestNewStackConverter(t *testing.T) { _, err = NewStackConverter("v1alpha3") assert.NilError(t, err) } + +func TestConvertFromToV1beta1(t *testing.T) { + composefile := `version: "3.3" +services: + test: + image: nginx +secrets: + test: + file: testdata/secret +configs: + test: + file: testdata/config +` + stackv1beta1 := &v1beta1.Stack{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: v1beta1.StackSpec{ + ComposeFile: composefile, + }, + } + + result, err := stackFromV1beta1(stackv1beta1) + assert.NilError(t, err) + expected := Stack{ + Name: "test", + ComposeFile: composefile, + Spec: &v1alpha3.StackSpec{ + Services: []v1alpha3.ServiceConfig{ + { + Name: "test", + Image: "nginx", + Environment: make(map[string]*string), + }, + }, + Secrets: map[string]v1alpha3.SecretConfig{ + "test": {File: filepath.FromSlash("testdata/secret")}, + }, + Configs: map[string]v1alpha3.ConfigObjConfig{ + "test": {File: filepath.FromSlash("testdata/config")}, + }, + }, + } + assert.DeepEqual(t, expected, result) + assert.DeepEqual(t, stackv1beta1, stackToV1beta1(result)) +} + +func TestConvertFromToV1beta2(t *testing.T) { + stackv1beta2 := &v1beta2.Stack{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: &v1beta2.StackSpec{ + Services: []v1beta2.ServiceConfig{ + { + Name: "test", + Image: "nginx", + Environment: make(map[string]*string), + }, + }, + Secrets: map[string]v1beta2.SecretConfig{ + "test": {File: filepath.FromSlash("testdata/secret")}, + }, + Configs: map[string]v1beta2.ConfigObjConfig{ + "test": {File: filepath.FromSlash("testdata/config")}, + }, + }, + } + expected := Stack{ + Name: "test", + Spec: &v1alpha3.StackSpec{ + Services: []v1alpha3.ServiceConfig{ + { + Name: "test", + Image: "nginx", + Environment: make(map[string]*string), + }, + }, + Secrets: map[string]v1alpha3.SecretConfig{ + "test": {File: filepath.FromSlash("testdata/secret")}, + }, + Configs: map[string]v1alpha3.ConfigObjConfig{ + "test": {File: filepath.FromSlash("testdata/config")}, + }, + }, + } + result, err := stackFromV1beta2(stackv1beta2) + assert.NilError(t, err) + assert.DeepEqual(t, expected, result) + gotBack, err := stackToV1beta2(result) + assert.NilError(t, err) + assert.DeepEqual(t, stackv1beta2, gotBack) +} + +func TestConvertFromToV1alpha3(t *testing.T) { + stackv1alpha3 := &v1alpha3.Stack{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + }, + Spec: &v1alpha3.StackSpec{ + Services: []v1alpha3.ServiceConfig{ + { + Name: "test", + Image: "nginx", + Environment: make(map[string]*string), + }, + }, + Secrets: map[string]v1alpha3.SecretConfig{ + "test": {File: filepath.FromSlash("testdata/secret")}, + }, + Configs: map[string]v1alpha3.ConfigObjConfig{ + "test": {File: filepath.FromSlash("testdata/config")}, + }, + }, + } + expected := Stack{ + Name: "test", + Spec: &v1alpha3.StackSpec{ + Services: []v1alpha3.ServiceConfig{ + { + Name: "test", + Image: "nginx", + Environment: make(map[string]*string), + }, + }, + Secrets: map[string]v1alpha3.SecretConfig{ + "test": {File: filepath.FromSlash("testdata/secret")}, + }, + Configs: map[string]v1alpha3.ConfigObjConfig{ + "test": {File: filepath.FromSlash("testdata/config")}, + }, + }, + } + result := stackFromV1alpha3(stackv1alpha3) + assert.DeepEqual(t, expected, result) + gotBack := stackToV1alpha3(result) + assert.DeepEqual(t, stackv1alpha3, gotBack) +} From eb714f7c0e8375912e2ddc8df74b5c06d4a375d4 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Wed, 23 Jan 2019 14:26:12 +0000 Subject: [PATCH 10/60] Add unit test for `formatInfo`. Signed-off-by: Ian Campbell --- cli/command/system/info_test.go | 37 ++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/cli/command/system/info_test.go b/cli/command/system/info_test.go index 942a645b2e02..fa1a957f8b73 100644 --- a/cli/command/system/info_test.go +++ b/cli/command/system/info_test.go @@ -21,8 +21,10 @@ func base64Decode(val string) []byte { return decoded } +const sampleID = "EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX" + var sampleInfoNoSwarm = types.Info{ - ID: "EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX", + ID: sampleID, Containers: 0, ContainersRunning: 0, ContainersPaused: 0, @@ -263,3 +265,36 @@ func TestPrettyPrintInfo(t *testing.T) { }) } } + +func TestFormatInfo(t *testing.T) { + for _, tc := range []struct { + doc string + template string + expectedError string + expectedOut string + }{ + { + doc: "basic", + template: "{{.ID}}", + expectedOut: sampleID + "\n", + }, + { + doc: "syntax", + template: "{{}", + expectedError: `Status: Template parsing error: template: :1: unexpected "}" in command, Code: 64`, + }, + } { + t.Run(tc.doc, func(t *testing.T) { + cli := test.NewFakeCli(&fakeClient{}) + err := formatInfo(cli, sampleInfoNoSwarm, tc.template) + if tc.expectedOut != "" { + assert.NilError(t, err) + assert.Equal(t, cli.OutBuffer().String(), tc.expectedOut) + } else if tc.expectedError != "" { + assert.Error(t, err, tc.expectedError) + } else { + t.Fatal("test expected to neither pass nor fail") + } + }) + } +} From 7913fb6a5eedea1d0b5e04efc648e5a57a529fc1 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Thu, 24 Jan 2019 12:29:06 +0000 Subject: [PATCH 11/60] Check json output in existing `docker info` unit tests. This is in addition to the more specific `formatInfo` unit tests added previously. Signed-off-by: Ian Campbell --- cli/command/system/info_test.go | 30 ++++++++++++------- .../docker-info-daemon-warnings.json.golden | 1 + .../docker-info-legacy-warnings.json.golden | 1 + .../testdata/docker-info-no-swarm.json.golden | 1 + .../docker-info-with-swarm.json.golden | 1 + 5 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 cli/command/system/testdata/docker-info-daemon-warnings.json.golden create mode 100644 cli/command/system/testdata/docker-info-legacy-warnings.json.golden create mode 100644 cli/command/system/testdata/docker-info-no-swarm.json.golden create mode 100644 cli/command/system/testdata/docker-info-with-swarm.json.golden diff --git a/cli/command/system/info_test.go b/cli/command/system/info_test.go index fa1a957f8b73..5b7c74826e42 100644 --- a/cli/command/system/info_test.go +++ b/cli/command/system/info_test.go @@ -227,41 +227,51 @@ func TestPrettyPrintInfo(t *testing.T) { for _, tc := range []struct { doc string dockerInfo types.Info - expectedGolden string + prettyGolden string warningsGolden string + jsonGolden string }{ { - doc: "info without swarm", - dockerInfo: sampleInfoNoSwarm, - expectedGolden: "docker-info-no-swarm", + doc: "info without swarm", + dockerInfo: sampleInfoNoSwarm, + prettyGolden: "docker-info-no-swarm", + jsonGolden: "docker-info-no-swarm", }, { - doc: "info with swarm", - dockerInfo: infoWithSwarm, - expectedGolden: "docker-info-with-swarm", + doc: "info with swarm", + dockerInfo: infoWithSwarm, + prettyGolden: "docker-info-with-swarm", + jsonGolden: "docker-info-with-swarm", }, { doc: "info with legacy warnings", dockerInfo: infoWithWarningsLinux, - expectedGolden: "docker-info-no-swarm", + prettyGolden: "docker-info-no-swarm", warningsGolden: "docker-info-warnings", + jsonGolden: "docker-info-legacy-warnings", }, { doc: "info with daemon warnings", dockerInfo: sampleInfoDaemonWarnings, - expectedGolden: "docker-info-no-swarm", + prettyGolden: "docker-info-no-swarm", warningsGolden: "docker-info-warnings", + jsonGolden: "docker-info-daemon-warnings", }, } { t.Run(tc.doc, func(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) assert.NilError(t, prettyPrintInfo(cli, tc.dockerInfo)) - golden.Assert(t, cli.OutBuffer().String(), tc.expectedGolden+".golden") + golden.Assert(t, cli.OutBuffer().String(), tc.prettyGolden+".golden") if tc.warningsGolden != "" { golden.Assert(t, cli.ErrBuffer().String(), tc.warningsGolden+".golden") } else { assert.Check(t, is.Equal("", cli.ErrBuffer().String())) } + + cli = test.NewFakeCli(&fakeClient{}) + assert.NilError(t, formatInfo(cli, tc.dockerInfo, "{{json .}}")) + golden.Assert(t, cli.OutBuffer().String(), tc.jsonGolden+".json.golden") + assert.Check(t, is.Equal("", cli.ErrBuffer().String())) }) } } diff --git a/cli/command/system/testdata/docker-info-daemon-warnings.json.golden b/cli/command/system/testdata/docker-info-daemon-warnings.json.golden new file mode 100644 index 000000000000..909eba9641b7 --- /dev/null +++ b/cli/command/system/testdata/docker-info-daemon-warnings.json.golden @@ -0,0 +1 @@ +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":["WARNING: No memory limit support","WARNING: No swap limit support","WARNING: No kernel memory limit support","WARNING: No oom kill disable support","WARNING: No cpu cfs quota support","WARNING: No cpu cfs period support","WARNING: No cpu shares support","WARNING: No cpuset support","WARNING: IPv4 forwarding is disabled","WARNING: bridge-nf-call-iptables is disabled","WARNING: bridge-nf-call-ip6tables is disabled"]} diff --git a/cli/command/system/testdata/docker-info-legacy-warnings.json.golden b/cli/command/system/testdata/docker-info-legacy-warnings.json.golden new file mode 100644 index 000000000000..d71608c09d7b --- /dev/null +++ b/cli/command/system/testdata/docker-info-legacy-warnings.json.golden @@ -0,0 +1 @@ +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":false,"SwapLimit":false,"KernelMemory":false,"KernelMemoryTCP":false,"CpuCfsPeriod":false,"CpuCfsQuota":false,"CPUShares":false,"CPUSet":false,"IPv4Forwarding":false,"BridgeNfIptables":false,"BridgeNfIp6tables":false,"Debug":true,"NFd":33,"OomKillDisable":false,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null} diff --git a/cli/command/system/testdata/docker-info-no-swarm.json.golden b/cli/command/system/testdata/docker-info-no-swarm.json.golden new file mode 100644 index 000000000000..f4dea32f1cdb --- /dev/null +++ b/cli/command/system/testdata/docker-info-no-swarm.json.golden @@ -0,0 +1 @@ +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null} diff --git a/cli/command/system/testdata/docker-info-with-swarm.json.golden b/cli/command/system/testdata/docker-info-with-swarm.json.golden new file mode 100644 index 000000000000..65ced13906e8 --- /dev/null +++ b/cli/command/system/testdata/docker-info-with-swarm.json.golden @@ -0,0 +1 @@ +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"qo2dfdig9mmxqkawulggepdih","NodeAddr":"165.227.107.89","LocalNodeState":"active","ControlAvailable":true,"Error":"","RemoteManagers":[{"NodeID":"qo2dfdig9mmxqkawulggepdih","Addr":"165.227.107.89:2377"}],"Nodes":1,"Managers":1,"Cluster":{"ID":"9vs5ygs0gguyyec4iqf2314c0","Version":{"Index":11},"CreatedAt":"2017-08-24T17:34:19.278062352Z","UpdatedAt":"2017-08-24T17:34:42.398815481Z","Spec":{"Name":"default","Labels":null,"Orchestration":{"TaskHistoryRetentionLimit":5},"Raft":{"SnapshotInterval":10000,"KeepOldSnapshots":0,"LogEntriesForSlowFollowers":500,"ElectionTick":3,"HeartbeatTick":1},"Dispatcher":{"HeartbeatPeriod":5000000000},"CAConfig":{"NodeCertExpiry":7776000000000000},"TaskDefaults":{},"EncryptionConfig":{"AutoLockManagers":true}},"TLSInfo":{"TrustRoot":"\n-----BEGIN CERTIFICATE-----\nMIIBajCCARCgAwIBAgIUaFCW5xsq8eyiJ+Pmcv3MCflMLnMwCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwODI0MTcyOTAwWhcNMzcwODE5MTcy\nOTAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABDy7NebyUJyUjWJDBUdnZoV6GBxEGKO4TZPNDwnxDxJcUdLVaB7WGa4/DLrW\nUfsVgh1JGik2VTiLuTMA1tLlNPOjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBQl16XFtaaXiUAwEuJptJlDjfKskDAKBggqhkjO\nPQQDAgNIADBFAiEAo9fTQNM5DP9bHVcTJYfl2Cay1bFu1E+lnpmN+EYJfeACIGKH\n1pCUkZ+D0IB6CiEZGWSHyLuXPM1rlP+I5KuS7sB8\n-----END CERTIFICATE-----\n","CertIssuerSubject":"MBMxETAPBgNVBAMTCHN3YXJtLWNh","CertIssuerPublicKey":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPLs15vJQnJSNYkMFR2dmhXoYHEQYo7hNk80PCfEPElxR0tVoHtYZrj8MutZR+xWCHUkaKTZVOIu5MwDW0uU08w=="},"RootRotationInProgress":false,"DefaultAddrPool":null,"SubnetSize":0,"DataPathPort":0}},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null} From 62ed1c0c5bd90fb32f3e9d2185cc505c696c948a Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Wed, 23 Jan 2019 13:34:43 +0000 Subject: [PATCH 12/60] Separate client and daemon info in `docker system info` Right now the only client side info we have is whether debug is enabled, but we expect more in the future. We also preemptively prepare for the possibility of multiple errors when gathering both daemon and client info. Signed-off-by: Ian Campbell --- cli/command/system/info.go | 88 ++++++++++++++++--- cli/command/system/info_test.go | 54 +++++++++--- .../docker-info-daemon-warnings.json.golden | 2 +- .../system/testdata/docker-info-errors.golden | 7 ++ .../testdata/docker-info-errors.json.golden | 1 + .../docker-info-legacy-warnings.json.golden | 2 +- .../testdata/docker-info-no-swarm.golden | 9 +- .../testdata/docker-info-no-swarm.json.golden | 2 +- .../testdata/docker-info-with-swarm.golden | 9 +- .../docker-info-with-swarm.json.golden | 2 +- docs/reference/commandline/info.md | 24 +++-- man/src/system/info.md | 18 +++- 12 files changed, 177 insertions(+), 41 deletions(-) create mode 100644 cli/command/system/testdata/docker-info-errors.golden create mode 100644 cli/command/system/testdata/docker-info-errors.json.golden diff --git a/cli/command/system/info.go b/cli/command/system/info.go index 2d6ac31ccc0b..4c65cfeaefd5 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -21,6 +21,23 @@ type infoOptions struct { format string } +type clientInfo struct { + Debug bool + Warnings []string +} + +type info struct { + // This field should/could be ServerInfo but is anonymous to + // preserve backwards compatibility in the JSON rendering + // which has ServerInfo immediately within the top-level + // object. + *types.Info `json:",omitempty"` + ServerErrors []string `json:",omitempty"` + + ClientInfo *clientInfo `json:",omitempty"` + ClientErrors []string `json:",omitempty"` +} + // NewInfoCommand creates a new cobra.Command for `docker info` func NewInfoCommand(dockerCli command.Cli) *cobra.Command { var opts infoOptions @@ -42,19 +59,67 @@ func NewInfoCommand(dockerCli command.Cli) *cobra.Command { } func runInfo(dockerCli command.Cli, opts *infoOptions) error { + var info info + ctx := context.Background() - info, err := dockerCli.Client().Info(ctx) - if err != nil { - return err + if dinfo, err := dockerCli.Client().Info(ctx); err == nil { + info.Info = &dinfo + } else { + info.ServerErrors = append(info.ServerErrors, err.Error()) } + + info.ClientInfo = &clientInfo{ + Debug: debug.IsEnabled(), + } + if opts.format == "" { return prettyPrintInfo(dockerCli, info) } return formatInfo(dockerCli, info, opts.format) } +func prettyPrintInfo(dockerCli command.Cli, info info) error { + fmt.Fprintln(dockerCli.Out(), "Client") + fmt.Fprintln(dockerCli.Out(), "------") + if info.ClientInfo != nil { + if err := prettyPrintClientInfo(dockerCli, *info.ClientInfo); err != nil { + info.ClientErrors = append(info.ClientErrors, err.Error()) + } + } + for _, err := range info.ClientErrors { + fmt.Fprintln(dockerCli.Out(), "ERROR:", err) + } + + fmt.Fprintln(dockerCli.Out()) + fmt.Fprintln(dockerCli.Out(), "Server") + fmt.Fprintln(dockerCli.Out(), "------") + if info.Info != nil { + if err := prettyPrintServerInfo(dockerCli, *info.Info); err != nil { + info.ServerErrors = append(info.ServerErrors, err.Error()) + } + } + for _, err := range info.ServerErrors { + fmt.Fprintln(dockerCli.Out(), "ERROR:", err) + } + + if len(info.ServerErrors) > 0 || len(info.ClientErrors) > 0 { + return fmt.Errorf("errors pretty printing info") + } + return nil +} + +func prettyPrintClientInfo(dockerCli command.Cli, info clientInfo) error { + fmt.Fprintln(dockerCli.Out(), "Debug Mode:", info.Debug) + + if len(info.Warnings) > 0 { + fmt.Fprintln(dockerCli.Err(), strings.Join(info.Warnings, "\n")) + } + + return nil +} + // nolint: gocyclo -func prettyPrintInfo(dockerCli command.Cli, info types.Info) error { +func prettyPrintServerInfo(dockerCli command.Cli, info types.Info) error { fmt.Fprintln(dockerCli.Out(), "Containers:", info.Containers) fmt.Fprintln(dockerCli.Out(), " Running:", info.ContainersRunning) fmt.Fprintln(dockerCli.Out(), " Paused:", info.ContainersPaused) @@ -149,8 +214,7 @@ func prettyPrintInfo(dockerCli command.Cli, info types.Info) error { fprintlnNonEmpty(dockerCli.Out(), "Name:", info.Name) fprintlnNonEmpty(dockerCli.Out(), "ID:", info.ID) fmt.Fprintln(dockerCli.Out(), "Docker Root Dir:", info.DockerRootDir) - fmt.Fprintln(dockerCli.Out(), "Debug Mode (client):", debug.IsEnabled()) - fmt.Fprintln(dockerCli.Out(), "Debug Mode (server):", info.Debug) + fmt.Fprintln(dockerCli.Out(), "Debug Mode:", info.Debug) if info.Debug { fmt.Fprintln(dockerCli.Out(), " File Descriptors:", info.NFd) @@ -209,7 +273,7 @@ func prettyPrintInfo(dockerCli command.Cli, info types.Info) error { } fmt.Fprint(dockerCli.Out(), "\n") - printWarnings(dockerCli, info) + printServerWarnings(dockerCli, info) return nil } @@ -283,22 +347,22 @@ func printSwarmInfo(dockerCli command.Cli, info types.Info) { } } -func printWarnings(dockerCli command.Cli, info types.Info) { +func printServerWarnings(dockerCli command.Cli, info types.Info) { if len(info.Warnings) > 0 { fmt.Fprintln(dockerCli.Err(), strings.Join(info.Warnings, "\n")) return } // daemon didn't return warnings. Fallback to old behavior printStorageDriverWarnings(dockerCli, info) - printWarningsLegacy(dockerCli, info) + printServerWarningsLegacy(dockerCli, info) } -// printWarningsLegacy generates warnings based on information returned by the daemon. +// printServerWarningsLegacy generates warnings based on information returned by the daemon. // DEPRECATED: warnings are now generated by the daemon, and returned in // info.Warnings. This function is used to provide backward compatibility with // daemons that do not provide these warnings. No new warnings should be added // here. -func printWarningsLegacy(dockerCli command.Cli, info types.Info) { +func printServerWarningsLegacy(dockerCli command.Cli, info types.Info) { if info.OSType == "windows" { return } @@ -382,7 +446,7 @@ func getBackingFs(info types.Info) string { return "" } -func formatInfo(dockerCli command.Cli, info types.Info, format string) error { +func formatInfo(dockerCli command.Cli, info info, format string) error { tmpl, err := templates.Parse(format) if err != nil { return cli.StatusError{StatusCode: 64, diff --git a/cli/command/system/info_test.go b/cli/command/system/info_test.go index 5b7c74826e42..5b44935c2fec 100644 --- a/cli/command/system/info_test.go +++ b/cli/command/system/info_test.go @@ -226,41 +226,69 @@ func TestPrettyPrintInfo(t *testing.T) { for _, tc := range []struct { doc string - dockerInfo types.Info + dockerInfo info prettyGolden string warningsGolden string jsonGolden string + expectedError string }{ { - doc: "info without swarm", - dockerInfo: sampleInfoNoSwarm, + doc: "info without swarm", + dockerInfo: info{ + Info: &sampleInfoNoSwarm, + ClientInfo: &clientInfo{Debug: true}, + }, prettyGolden: "docker-info-no-swarm", jsonGolden: "docker-info-no-swarm", }, { - doc: "info with swarm", - dockerInfo: infoWithSwarm, + doc: "info with swarm", + dockerInfo: info{ + Info: &infoWithSwarm, + ClientInfo: &clientInfo{Debug: false}, + }, prettyGolden: "docker-info-with-swarm", jsonGolden: "docker-info-with-swarm", }, { - doc: "info with legacy warnings", - dockerInfo: infoWithWarningsLinux, + doc: "info with legacy warnings", + dockerInfo: info{ + Info: &infoWithWarningsLinux, + ClientInfo: &clientInfo{Debug: true}, + }, prettyGolden: "docker-info-no-swarm", warningsGolden: "docker-info-warnings", jsonGolden: "docker-info-legacy-warnings", }, { - doc: "info with daemon warnings", - dockerInfo: sampleInfoDaemonWarnings, + doc: "info with daemon warnings", + dockerInfo: info{ + Info: &sampleInfoDaemonWarnings, + ClientInfo: &clientInfo{Debug: true}, + }, prettyGolden: "docker-info-no-swarm", warningsGolden: "docker-info-warnings", jsonGolden: "docker-info-daemon-warnings", }, + { + doc: "errors for both", + dockerInfo: info{ + ServerErrors: []string{"a server error occurred"}, + ClientErrors: []string{"a client error occurred"}, + }, + prettyGolden: "docker-info-errors", + jsonGolden: "docker-info-errors", + expectedError: "errors pretty printing info", + }, } { t.Run(tc.doc, func(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - assert.NilError(t, prettyPrintInfo(cli, tc.dockerInfo)) + err := prettyPrintInfo(cli, tc.dockerInfo) + if tc.expectedError == "" { + assert.NilError(t, err) + } else { + assert.Error(t, err, tc.expectedError) + } golden.Assert(t, cli.OutBuffer().String(), tc.prettyGolden+".golden") if tc.warningsGolden != "" { golden.Assert(t, cli.ErrBuffer().String(), tc.warningsGolden+".golden") @@ -296,7 +324,11 @@ func TestFormatInfo(t *testing.T) { } { t.Run(tc.doc, func(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - err := formatInfo(cli, sampleInfoNoSwarm, tc.template) + info := info{ + Info: &sampleInfoNoSwarm, + ClientInfo: &clientInfo{Debug: true}, + } + err := formatInfo(cli, info, tc.template) if tc.expectedOut != "" { assert.NilError(t, err) assert.Equal(t, cli.OutBuffer().String(), tc.expectedOut) diff --git a/cli/command/system/testdata/docker-info-daemon-warnings.json.golden b/cli/command/system/testdata/docker-info-daemon-warnings.json.golden index 909eba9641b7..504939799d6e 100644 --- a/cli/command/system/testdata/docker-info-daemon-warnings.json.golden +++ b/cli/command/system/testdata/docker-info-daemon-warnings.json.golden @@ -1 +1 @@ -{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":["WARNING: No memory limit support","WARNING: No swap limit support","WARNING: No kernel memory limit support","WARNING: No oom kill disable support","WARNING: No cpu cfs quota support","WARNING: No cpu cfs period support","WARNING: No cpu shares support","WARNING: No cpuset support","WARNING: IPv4 forwarding is disabled","WARNING: bridge-nf-call-iptables is disabled","WARNING: bridge-nf-call-ip6tables is disabled"]} +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":["WARNING: No memory limit support","WARNING: No swap limit support","WARNING: No kernel memory limit support","WARNING: No oom kill disable support","WARNING: No cpu cfs quota support","WARNING: No cpu cfs period support","WARNING: No cpu shares support","WARNING: No cpuset support","WARNING: IPv4 forwarding is disabled","WARNING: bridge-nf-call-iptables is disabled","WARNING: bridge-nf-call-ip6tables is disabled"],"ClientInfo":{"Debug":true,"Warnings":null}} diff --git a/cli/command/system/testdata/docker-info-errors.golden b/cli/command/system/testdata/docker-info-errors.golden new file mode 100644 index 000000000000..cac76a12af81 --- /dev/null +++ b/cli/command/system/testdata/docker-info-errors.golden @@ -0,0 +1,7 @@ +Client +------ +ERROR: a client error occurred + +Server +------ +ERROR: a server error occurred diff --git a/cli/command/system/testdata/docker-info-errors.json.golden b/cli/command/system/testdata/docker-info-errors.json.golden new file mode 100644 index 000000000000..3dcddd789816 --- /dev/null +++ b/cli/command/system/testdata/docker-info-errors.json.golden @@ -0,0 +1 @@ +{"ServerErrors":["a server error occurred"],"ClientErrors":["a client error occurred"]} diff --git a/cli/command/system/testdata/docker-info-legacy-warnings.json.golden b/cli/command/system/testdata/docker-info-legacy-warnings.json.golden index d71608c09d7b..489f700eb3e6 100644 --- a/cli/command/system/testdata/docker-info-legacy-warnings.json.golden +++ b/cli/command/system/testdata/docker-info-legacy-warnings.json.golden @@ -1 +1 @@ -{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":false,"SwapLimit":false,"KernelMemory":false,"KernelMemoryTCP":false,"CpuCfsPeriod":false,"CpuCfsQuota":false,"CPUShares":false,"CPUSet":false,"IPv4Forwarding":false,"BridgeNfIptables":false,"BridgeNfIp6tables":false,"Debug":true,"NFd":33,"OomKillDisable":false,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null} +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":false,"SwapLimit":false,"KernelMemory":false,"KernelMemoryTCP":false,"CpuCfsPeriod":false,"CpuCfsQuota":false,"CPUShares":false,"CPUSet":false,"IPv4Forwarding":false,"BridgeNfIptables":false,"BridgeNfIp6tables":false,"Debug":true,"NFd":33,"OomKillDisable":false,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":true,"Warnings":null}} diff --git a/cli/command/system/testdata/docker-info-no-swarm.golden b/cli/command/system/testdata/docker-info-no-swarm.golden index 7a3e9667356f..6c71576c1b44 100644 --- a/cli/command/system/testdata/docker-info-no-swarm.golden +++ b/cli/command/system/testdata/docker-info-no-swarm.golden @@ -1,3 +1,9 @@ +Client +------ +Debug Mode: true + +Server +------ Containers: 0 Running: 0 Paused: 0 @@ -35,8 +41,7 @@ Total Memory: 1.953GiB Name: system-sample ID: EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX Docker Root Dir: /var/lib/docker -Debug Mode (client): false -Debug Mode (server): true +Debug Mode: true File Descriptors: 33 Goroutines: 135 System Time: 2017-08-24T17:44:34.077811894Z diff --git a/cli/command/system/testdata/docker-info-no-swarm.json.golden b/cli/command/system/testdata/docker-info-no-swarm.json.golden index f4dea32f1cdb..b99808753108 100644 --- a/cli/command/system/testdata/docker-info-no-swarm.json.golden +++ b/cli/command/system/testdata/docker-info-no-swarm.json.golden @@ -1 +1 @@ -{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null} +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":true,"Warnings":null}} diff --git a/cli/command/system/testdata/docker-info-with-swarm.golden b/cli/command/system/testdata/docker-info-with-swarm.golden index 17bb70fa7f58..cd9b08f38f66 100644 --- a/cli/command/system/testdata/docker-info-with-swarm.golden +++ b/cli/command/system/testdata/docker-info-with-swarm.golden @@ -1,3 +1,9 @@ +Client +------ +Debug Mode: false + +Server +------ Containers: 0 Running: 0 Paused: 0 @@ -57,8 +63,7 @@ Total Memory: 1.953GiB Name: system-sample ID: EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX Docker Root Dir: /var/lib/docker -Debug Mode (client): false -Debug Mode (server): true +Debug Mode: true File Descriptors: 33 Goroutines: 135 System Time: 2017-08-24T17:44:34.077811894Z diff --git a/cli/command/system/testdata/docker-info-with-swarm.json.golden b/cli/command/system/testdata/docker-info-with-swarm.json.golden index 65ced13906e8..58ff6ea2b2fc 100644 --- a/cli/command/system/testdata/docker-info-with-swarm.json.golden +++ b/cli/command/system/testdata/docker-info-with-swarm.json.golden @@ -1 +1 @@ -{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"qo2dfdig9mmxqkawulggepdih","NodeAddr":"165.227.107.89","LocalNodeState":"active","ControlAvailable":true,"Error":"","RemoteManagers":[{"NodeID":"qo2dfdig9mmxqkawulggepdih","Addr":"165.227.107.89:2377"}],"Nodes":1,"Managers":1,"Cluster":{"ID":"9vs5ygs0gguyyec4iqf2314c0","Version":{"Index":11},"CreatedAt":"2017-08-24T17:34:19.278062352Z","UpdatedAt":"2017-08-24T17:34:42.398815481Z","Spec":{"Name":"default","Labels":null,"Orchestration":{"TaskHistoryRetentionLimit":5},"Raft":{"SnapshotInterval":10000,"KeepOldSnapshots":0,"LogEntriesForSlowFollowers":500,"ElectionTick":3,"HeartbeatTick":1},"Dispatcher":{"HeartbeatPeriod":5000000000},"CAConfig":{"NodeCertExpiry":7776000000000000},"TaskDefaults":{},"EncryptionConfig":{"AutoLockManagers":true}},"TLSInfo":{"TrustRoot":"\n-----BEGIN CERTIFICATE-----\nMIIBajCCARCgAwIBAgIUaFCW5xsq8eyiJ+Pmcv3MCflMLnMwCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwODI0MTcyOTAwWhcNMzcwODE5MTcy\nOTAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABDy7NebyUJyUjWJDBUdnZoV6GBxEGKO4TZPNDwnxDxJcUdLVaB7WGa4/DLrW\nUfsVgh1JGik2VTiLuTMA1tLlNPOjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBQl16XFtaaXiUAwEuJptJlDjfKskDAKBggqhkjO\nPQQDAgNIADBFAiEAo9fTQNM5DP9bHVcTJYfl2Cay1bFu1E+lnpmN+EYJfeACIGKH\n1pCUkZ+D0IB6CiEZGWSHyLuXPM1rlP+I5KuS7sB8\n-----END CERTIFICATE-----\n","CertIssuerSubject":"MBMxETAPBgNVBAMTCHN3YXJtLWNh","CertIssuerPublicKey":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPLs15vJQnJSNYkMFR2dmhXoYHEQYo7hNk80PCfEPElxR0tVoHtYZrj8MutZR+xWCHUkaKTZVOIu5MwDW0uU08w=="},"RootRotationInProgress":false,"DefaultAddrPool":null,"SubnetSize":0,"DataPathPort":0}},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null} +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"qo2dfdig9mmxqkawulggepdih","NodeAddr":"165.227.107.89","LocalNodeState":"active","ControlAvailable":true,"Error":"","RemoteManagers":[{"NodeID":"qo2dfdig9mmxqkawulggepdih","Addr":"165.227.107.89:2377"}],"Nodes":1,"Managers":1,"Cluster":{"ID":"9vs5ygs0gguyyec4iqf2314c0","Version":{"Index":11},"CreatedAt":"2017-08-24T17:34:19.278062352Z","UpdatedAt":"2017-08-24T17:34:42.398815481Z","Spec":{"Name":"default","Labels":null,"Orchestration":{"TaskHistoryRetentionLimit":5},"Raft":{"SnapshotInterval":10000,"KeepOldSnapshots":0,"LogEntriesForSlowFollowers":500,"ElectionTick":3,"HeartbeatTick":1},"Dispatcher":{"HeartbeatPeriod":5000000000},"CAConfig":{"NodeCertExpiry":7776000000000000},"TaskDefaults":{},"EncryptionConfig":{"AutoLockManagers":true}},"TLSInfo":{"TrustRoot":"\n-----BEGIN CERTIFICATE-----\nMIIBajCCARCgAwIBAgIUaFCW5xsq8eyiJ+Pmcv3MCflMLnMwCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwODI0MTcyOTAwWhcNMzcwODE5MTcy\nOTAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABDy7NebyUJyUjWJDBUdnZoV6GBxEGKO4TZPNDwnxDxJcUdLVaB7WGa4/DLrW\nUfsVgh1JGik2VTiLuTMA1tLlNPOjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBQl16XFtaaXiUAwEuJptJlDjfKskDAKBggqhkjO\nPQQDAgNIADBFAiEAo9fTQNM5DP9bHVcTJYfl2Cay1bFu1E+lnpmN+EYJfeACIGKH\n1pCUkZ+D0IB6CiEZGWSHyLuXPM1rlP+I5KuS7sB8\n-----END CERTIFICATE-----\n","CertIssuerSubject":"MBMxETAPBgNVBAMTCHN3YXJtLWNh","CertIssuerPublicKey":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPLs15vJQnJSNYkMFR2dmhXoYHEQYo7hNk80PCfEPElxR0tVoHtYZrj8MutZR+xWCHUkaKTZVOIu5MwDW0uU08w=="},"RootRotationInProgress":false,"DefaultAddrPool":null,"SubnetSize":0,"DataPathPort":0}},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":false,"Warnings":null}} diff --git a/docs/reference/commandline/info.md b/docs/reference/commandline/info.md index 87f47076f144..fc64141ed35a 100644 --- a/docs/reference/commandline/info.md +++ b/docs/reference/commandline/info.md @@ -55,7 +55,12 @@ information about the `devicemapper` storage driver is shown: ```bash $ docker info +Client +------ +Debug Mode: false +Server +------ Containers: 14 Running: 3 Paused: 1 @@ -96,8 +101,7 @@ Total Memory: 991.7 MiB Name: ip-172-30-0-91.ec2.internal ID: I54V:OLXT:HVMM:TPKO:JPHQ:CQCD:JNLC:O3BZ:4ZVJ:43XJ:PFHZ:6N2S Docker Root Dir: /var/lib/docker -Debug mode (client): false -Debug mode (server): false +Debug Mode: false Username: gordontheturtle Registry: https://index.docker.io/v1/ Insecure registries: @@ -112,7 +116,12 @@ storage driver and a node that is part of a 2-node swarm: ```bash $ docker -D info +Client +------ +Debug Mode: true +Server +------ Containers: 14 Running: 3 Paused: 1 @@ -168,8 +177,7 @@ Total Memory: 1.937 GiB Name: ubuntu ID: H52R:7ZR6:EIIA:76JG:ORIY:BVKF:GSFU:HNPG:B5MK:APSC:SZ3Q:N326 Docker Root Dir: /var/lib/docker -Debug Mode (client): true -Debug Mode (server): true +Debug Mode: true File Descriptors: 30 Goroutines: 123 System Time: 2016-11-12T17:24:37.955404361-08:00 @@ -209,7 +217,12 @@ Here is a sample output for a daemon running on Windows Server 2016: ```none E:\docker>docker info +Client +------ +Debug Mode: false +Server +------ Containers: 1 Running: 0 Paused: 0 @@ -233,8 +246,7 @@ Total Memory: 3.999 GiB Name: WIN-V0V70C0LU5P ID: NYMS:B5VK:UMSL:FVDZ:EWB5:FKVK:LPFL:FJMQ:H6FT:BZJ6:L2TD:XH62 Docker Root Dir: C:\control -Debug Mode (client): false -Debug Mode (server): false +Debug Mode: false Registry: https://index.docker.io/v1/ Insecure Registries: 127.0.0.0/8 diff --git a/man/src/system/info.md b/man/src/system/info.md index 9a87e985edab..774f72f465a3 100644 --- a/man/src/system/info.md +++ b/man/src/system/info.md @@ -24,6 +24,12 @@ Here is a sample output for a daemon running on Ubuntu, using the overlay2 storage driver: $ docker -D info + Client + ------ + Debug Mode: true + + Server + ------ Containers: 14 Running: 3 Paused: 1 @@ -78,8 +84,7 @@ storage driver: Name: ubuntu ID: H52R:7ZR6:EIIA:76JG:ORIY:BVKF:GSFU:HNPG:B5MK:APSC:SZ3Q:N326 Docker Root Dir: /var/lib/docker - Debug Mode (client): true - Debug Mode (server): true + Debug Mode: true File Descriptors: 30 Goroutines: 123 System Time: 2016-11-12T17:24:37.955404361-08:00 @@ -109,6 +114,12 @@ using the devicemapper storage driver. As can be seen in the output, additional information about the devicemapper storage driver is shown: $ docker info + Client + ------ + Debug Mode: false + + Server + ------ Containers: 14 Running: 3 Paused: 1 @@ -149,8 +160,7 @@ information about the devicemapper storage driver is shown: Name: ip-172-30-0-91.ec2.internal ID: I54V:OLXT:HVMM:TPKO:JPHQ:CQCD:JNLC:O3BZ:4ZVJ:43XJ:PFHZ:6N2S Docker Root Dir: /var/lib/docker - Debug mode (client): false - Debug mode (server): false + Debug Mode: false Username: gordontheturtle Registry: https://index.docker.io/v1/ Insecure registries: From c9e60ae17a118555196ab827f366629c4884bbb6 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Thu, 24 Jan 2019 14:22:55 +0000 Subject: [PATCH 13/60] Allow `prettyPrintInfo` to return multiple errors This allows it to print what it can, rather than aborting half way when a bad security context is hit. Signed-off-by: Ian Campbell --- cli/command/system/info.go | 34 ++++++------ cli/command/system/info_test.go | 14 +++++ .../system/testdata/docker-info-badsec.golden | 54 +++++++++++++++++++ .../testdata/docker-info-badsec.json.golden | 1 + 4 files changed, 87 insertions(+), 16 deletions(-) create mode 100644 cli/command/system/testdata/docker-info-badsec.golden create mode 100644 cli/command/system/testdata/docker-info-badsec.json.golden diff --git a/cli/command/system/info.go b/cli/command/system/info.go index 4c65cfeaefd5..2ec94db0fa79 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -94,7 +94,7 @@ func prettyPrintInfo(dockerCli command.Cli, info info) error { fmt.Fprintln(dockerCli.Out(), "Server") fmt.Fprintln(dockerCli.Out(), "------") if info.Info != nil { - if err := prettyPrintServerInfo(dockerCli, *info.Info); err != nil { + for _, err := range prettyPrintServerInfo(dockerCli, *info.Info) { info.ServerErrors = append(info.ServerErrors, err.Error()) } } @@ -119,7 +119,9 @@ func prettyPrintClientInfo(dockerCli command.Cli, info clientInfo) error { } // nolint: gocyclo -func prettyPrintServerInfo(dockerCli command.Cli, info types.Info) error { +func prettyPrintServerInfo(dockerCli command.Cli, info types.Info) []error { + var errs []error + fmt.Fprintln(dockerCli.Out(), "Containers:", info.Containers) fmt.Fprintln(dockerCli.Out(), " Running:", info.ContainersRunning) fmt.Fprintln(dockerCli.Out(), " Paused:", info.ContainersPaused) @@ -180,20 +182,20 @@ func prettyPrintServerInfo(dockerCli command.Cli, info types.Info) error { fmt.Fprint(dockerCli.Out(), "\n") } if len(info.SecurityOptions) != 0 { - kvs, err := types.DecodeSecurityOptions(info.SecurityOptions) - if err != nil { - return err - } - fmt.Fprintln(dockerCli.Out(), "Security Options:") - for _, so := range kvs { - fmt.Fprintln(dockerCli.Out(), " "+so.Name) - for _, o := range so.Options { - switch o.Key { - case "profile": - if o.Value != "default" { - fmt.Fprintln(dockerCli.Err(), " WARNING: You're not using the default seccomp profile") + if kvs, err := types.DecodeSecurityOptions(info.SecurityOptions); err != nil { + errs = append(errs, err) + } else { + fmt.Fprintln(dockerCli.Out(), "Security Options:") + for _, so := range kvs { + fmt.Fprintln(dockerCli.Out(), " "+so.Name) + for _, o := range so.Options { + switch o.Key { + case "profile": + if o.Value != "default" { + fmt.Fprintln(dockerCli.Err(), " WARNING: You're not using the default seccomp profile") + } + fmt.Fprintln(dockerCli.Out(), " Profile:", o.Value) } - fmt.Fprintln(dockerCli.Out(), " Profile:", o.Value) } } } @@ -274,7 +276,7 @@ func prettyPrintServerInfo(dockerCli command.Cli, info types.Info) error { fmt.Fprint(dockerCli.Out(), "\n") printServerWarnings(dockerCli, info) - return nil + return errs } // nolint: gocyclo diff --git a/cli/command/system/info_test.go b/cli/command/system/info_test.go index 5b44935c2fec..d3bcd7e02af6 100644 --- a/cli/command/system/info_test.go +++ b/cli/command/system/info_test.go @@ -224,6 +224,9 @@ func TestPrettyPrintInfo(t *testing.T) { "WARNING: bridge-nf-call-ip6tables is disabled", } + sampleInfoBadSecurity := sampleInfoNoSwarm + sampleInfoBadSecurity.SecurityOptions = []string{"foo="} + for _, tc := range []struct { doc string dockerInfo info @@ -280,6 +283,17 @@ func TestPrettyPrintInfo(t *testing.T) { jsonGolden: "docker-info-errors", expectedError: "errors pretty printing info", }, + { + doc: "bad security info", + dockerInfo: info{ + Info: &sampleInfoBadSecurity, + ServerErrors: []string{"an error happened"}, + ClientInfo: &clientInfo{Debug: false}, + }, + prettyGolden: "docker-info-badsec", + jsonGolden: "docker-info-badsec", + expectedError: "errors pretty printing info", + }, } { t.Run(tc.doc, func(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) diff --git a/cli/command/system/testdata/docker-info-badsec.golden b/cli/command/system/testdata/docker-info-badsec.golden new file mode 100644 index 000000000000..337991ca7411 --- /dev/null +++ b/cli/command/system/testdata/docker-info-badsec.golden @@ -0,0 +1,54 @@ +Client +------ +Debug Mode: false + +Server +------ +Containers: 0 + Running: 0 + Paused: 0 + Stopped: 0 +Images: 0 +Server Version: 17.06.1-ce +Storage Driver: aufs + Root Dir: /var/lib/docker/aufs + Backing Filesystem: extfs + Dirs: 0 + Dirperm1 Supported: true +Logging Driver: json-file +Cgroup Driver: cgroupfs +Plugins: + Volume: local + Network: bridge host macvlan null overlay + Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog +Swarm: inactive +Runtimes: runc +Default Runtime: runc +Init Binary: docker-init +containerd version: 6e23458c129b551d5c9871e5174f6b1b7f6d1170 +runc version: 810190ceaa507aa2727d7ae6f4790c76ec150bd2 +init version: 949e6fa +Kernel Version: 4.4.0-87-generic +Operating System: Ubuntu 16.04.3 LTS +OSType: linux +Architecture: x86_64 +CPUs: 2 +Total Memory: 1.953GiB +Name: system-sample +ID: EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX +Docker Root Dir: /var/lib/docker +Debug Mode: true + File Descriptors: 33 + Goroutines: 135 + System Time: 2017-08-24T17:44:34.077811894Z + EventsListeners: 0 +Registry: https://index.docker.io/v1/ +Labels: + provider=digitalocean +Experimental: false +Insecure Registries: + 127.0.0.0/8 +Live Restore Enabled: false + +ERROR: an error happened +ERROR: invalid empty security option diff --git a/cli/command/system/testdata/docker-info-badsec.json.golden b/cli/command/system/testdata/docker-info-badsec.json.golden new file mode 100644 index 000000000000..4b15ac022239 --- /dev/null +++ b/cli/command/system/testdata/docker-info-badsec.json.golden @@ -0,0 +1 @@ +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["foo="],"Warnings":null,"ServerErrors":["an error happened"],"ClientInfo":{"Debug":false,"Warnings":null}} From bcb06b5f58aee62521737a60703c44d41405710c Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Fri, 25 Jan 2019 13:10:59 +0000 Subject: [PATCH 14/60] Rework `docker info` output to be more like `docker version` That is, reindent the two sections by one space. While the code was done by hand the `.golden` files had the extra space inserted with emacs' `string-insert-rectangle` macro to (try to) avoid possible manual errors. The docs were edited the same way. Signed-off-by: Ian Campbell --- cli/command/system/info.go | 178 +++++----- .../system/testdata/docker-info-badsec.golden | 98 +++-- .../system/testdata/docker-info-errors.golden | 6 +- .../testdata/docker-info-no-swarm.golden | 106 +++--- .../testdata/docker-info-with-swarm.golden | 150 ++++---- docs/reference/commandline/info.md | 336 +++++++++--------- man/src/system/info.md | 264 +++++++------- 7 files changed, 559 insertions(+), 579 deletions(-) diff --git a/cli/command/system/info.go b/cli/command/system/info.go index 2ec94db0fa79..29380ea1a3e5 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -79,8 +79,7 @@ func runInfo(dockerCli command.Cli, opts *infoOptions) error { } func prettyPrintInfo(dockerCli command.Cli, info info) error { - fmt.Fprintln(dockerCli.Out(), "Client") - fmt.Fprintln(dockerCli.Out(), "------") + fmt.Fprintln(dockerCli.Out(), "Client:") if info.ClientInfo != nil { if err := prettyPrintClientInfo(dockerCli, *info.ClientInfo); err != nil { info.ClientErrors = append(info.ClientErrors, err.Error()) @@ -91,8 +90,7 @@ func prettyPrintInfo(dockerCli command.Cli, info info) error { } fmt.Fprintln(dockerCli.Out()) - fmt.Fprintln(dockerCli.Out(), "Server") - fmt.Fprintln(dockerCli.Out(), "------") + fmt.Fprintln(dockerCli.Out(), "Server:") if info.Info != nil { for _, err := range prettyPrintServerInfo(dockerCli, *info.Info) { info.ServerErrors = append(info.ServerErrors, err.Error()) @@ -109,7 +107,7 @@ func prettyPrintInfo(dockerCli command.Cli, info info) error { } func prettyPrintClientInfo(dockerCli command.Cli, info clientInfo) error { - fmt.Fprintln(dockerCli.Out(), "Debug Mode:", info.Debug) + fmt.Fprintln(dockerCli.Out(), " Debug Mode:", info.Debug) if len(info.Warnings) > 0 { fmt.Fprintln(dockerCli.Err(), strings.Join(info.Warnings, "\n")) @@ -122,50 +120,50 @@ func prettyPrintClientInfo(dockerCli command.Cli, info clientInfo) error { func prettyPrintServerInfo(dockerCli command.Cli, info types.Info) []error { var errs []error - fmt.Fprintln(dockerCli.Out(), "Containers:", info.Containers) - fmt.Fprintln(dockerCli.Out(), " Running:", info.ContainersRunning) - fmt.Fprintln(dockerCli.Out(), " Paused:", info.ContainersPaused) - fmt.Fprintln(dockerCli.Out(), " Stopped:", info.ContainersStopped) - fmt.Fprintln(dockerCli.Out(), "Images:", info.Images) - fprintlnNonEmpty(dockerCli.Out(), "Server Version:", info.ServerVersion) - fprintlnNonEmpty(dockerCli.Out(), "Storage Driver:", info.Driver) + fmt.Fprintln(dockerCli.Out(), " Containers:", info.Containers) + fmt.Fprintln(dockerCli.Out(), " Running:", info.ContainersRunning) + fmt.Fprintln(dockerCli.Out(), " Paused:", info.ContainersPaused) + fmt.Fprintln(dockerCli.Out(), " Stopped:", info.ContainersStopped) + fmt.Fprintln(dockerCli.Out(), " Images:", info.Images) + fprintlnNonEmpty(dockerCli.Out(), " Server Version:", info.ServerVersion) + fprintlnNonEmpty(dockerCli.Out(), " Storage Driver:", info.Driver) if info.DriverStatus != nil { for _, pair := range info.DriverStatus { - fmt.Fprintf(dockerCli.Out(), " %s: %s\n", pair[0], pair[1]) + fmt.Fprintf(dockerCli.Out(), " %s: %s\n", pair[0], pair[1]) } } if info.SystemStatus != nil { for _, pair := range info.SystemStatus { - fmt.Fprintf(dockerCli.Out(), "%s: %s\n", pair[0], pair[1]) + fmt.Fprintf(dockerCli.Out(), " %s: %s\n", pair[0], pair[1]) } } - fprintlnNonEmpty(dockerCli.Out(), "Logging Driver:", info.LoggingDriver) - fprintlnNonEmpty(dockerCli.Out(), "Cgroup Driver:", info.CgroupDriver) + fprintlnNonEmpty(dockerCli.Out(), " Logging Driver:", info.LoggingDriver) + fprintlnNonEmpty(dockerCli.Out(), " Cgroup Driver:", info.CgroupDriver) - fmt.Fprintln(dockerCli.Out(), "Plugins:") - fmt.Fprintln(dockerCli.Out(), " Volume:", strings.Join(info.Plugins.Volume, " ")) - fmt.Fprintln(dockerCli.Out(), " Network:", strings.Join(info.Plugins.Network, " ")) + fmt.Fprintln(dockerCli.Out(), " Plugins:") + fmt.Fprintln(dockerCli.Out(), " Volume:", strings.Join(info.Plugins.Volume, " ")) + fmt.Fprintln(dockerCli.Out(), " Network:", strings.Join(info.Plugins.Network, " ")) if len(info.Plugins.Authorization) != 0 { - fmt.Fprintln(dockerCli.Out(), " Authorization:", strings.Join(info.Plugins.Authorization, " ")) + fmt.Fprintln(dockerCli.Out(), " Authorization:", strings.Join(info.Plugins.Authorization, " ")) } - fmt.Fprintln(dockerCli.Out(), " Log:", strings.Join(info.Plugins.Log, " ")) + fmt.Fprintln(dockerCli.Out(), " Log:", strings.Join(info.Plugins.Log, " ")) - fmt.Fprintln(dockerCli.Out(), "Swarm:", info.Swarm.LocalNodeState) + fmt.Fprintln(dockerCli.Out(), " Swarm:", info.Swarm.LocalNodeState) printSwarmInfo(dockerCli, info) if len(info.Runtimes) > 0 { - fmt.Fprint(dockerCli.Out(), "Runtimes:") + fmt.Fprint(dockerCli.Out(), " Runtimes:") for name := range info.Runtimes { fmt.Fprintf(dockerCli.Out(), " %s", name) } fmt.Fprint(dockerCli.Out(), "\n") - fmt.Fprintln(dockerCli.Out(), "Default Runtime:", info.DefaultRuntime) + fmt.Fprintln(dockerCli.Out(), " Default Runtime:", info.DefaultRuntime) } if info.OSType == "linux" { - fmt.Fprintln(dockerCli.Out(), "Init Binary:", info.InitBinary) + fmt.Fprintln(dockerCli.Out(), " Init Binary:", info.InitBinary) for _, ci := range []struct { Name string @@ -175,7 +173,7 @@ func prettyPrintServerInfo(dockerCli command.Cli, info types.Info) []error { {"runc", info.RuncCommit}, {"init", info.InitCommit}, } { - fmt.Fprintf(dockerCli.Out(), "%s version: %s", ci.Name, ci.Commit.ID) + fmt.Fprintf(dockerCli.Out(), " %s version: %s", ci.Name, ci.Commit.ID) if ci.Commit.ID != ci.Commit.Expected { fmt.Fprintf(dockerCli.Out(), " (expected: %s)", ci.Commit.Expected) } @@ -185,16 +183,16 @@ func prettyPrintServerInfo(dockerCli command.Cli, info types.Info) []error { if kvs, err := types.DecodeSecurityOptions(info.SecurityOptions); err != nil { errs = append(errs, err) } else { - fmt.Fprintln(dockerCli.Out(), "Security Options:") + fmt.Fprintln(dockerCli.Out(), " Security Options:") for _, so := range kvs { - fmt.Fprintln(dockerCli.Out(), " "+so.Name) + fmt.Fprintln(dockerCli.Out(), " "+so.Name) for _, o := range so.Options { switch o.Key { case "profile": if o.Value != "default" { fmt.Fprintln(dockerCli.Err(), " WARNING: You're not using the default seccomp profile") } - fmt.Fprintln(dockerCli.Out(), " Profile:", o.Value) + fmt.Fprintln(dockerCli.Out(), " Profile:", o.Value) } } } @@ -204,74 +202,74 @@ func prettyPrintServerInfo(dockerCli command.Cli, info types.Info) []error { // Isolation only has meaning on a Windows daemon. if info.OSType == "windows" { - fmt.Fprintln(dockerCli.Out(), "Default Isolation:", info.Isolation) + fmt.Fprintln(dockerCli.Out(), " Default Isolation:", info.Isolation) } - fprintlnNonEmpty(dockerCli.Out(), "Kernel Version:", info.KernelVersion) - fprintlnNonEmpty(dockerCli.Out(), "Operating System:", info.OperatingSystem) - fprintlnNonEmpty(dockerCli.Out(), "OSType:", info.OSType) - fprintlnNonEmpty(dockerCli.Out(), "Architecture:", info.Architecture) - fmt.Fprintln(dockerCli.Out(), "CPUs:", info.NCPU) - fmt.Fprintln(dockerCli.Out(), "Total Memory:", units.BytesSize(float64(info.MemTotal))) - fprintlnNonEmpty(dockerCli.Out(), "Name:", info.Name) - fprintlnNonEmpty(dockerCli.Out(), "ID:", info.ID) - fmt.Fprintln(dockerCli.Out(), "Docker Root Dir:", info.DockerRootDir) - fmt.Fprintln(dockerCli.Out(), "Debug Mode:", info.Debug) + fprintlnNonEmpty(dockerCli.Out(), " Kernel Version:", info.KernelVersion) + fprintlnNonEmpty(dockerCli.Out(), " Operating System:", info.OperatingSystem) + fprintlnNonEmpty(dockerCli.Out(), " OSType:", info.OSType) + fprintlnNonEmpty(dockerCli.Out(), " Architecture:", info.Architecture) + fmt.Fprintln(dockerCli.Out(), " CPUs:", info.NCPU) + fmt.Fprintln(dockerCli.Out(), " Total Memory:", units.BytesSize(float64(info.MemTotal))) + fprintlnNonEmpty(dockerCli.Out(), " Name:", info.Name) + fprintlnNonEmpty(dockerCli.Out(), " ID:", info.ID) + fmt.Fprintln(dockerCli.Out(), " Docker Root Dir:", info.DockerRootDir) + fmt.Fprintln(dockerCli.Out(), " Debug Mode:", info.Debug) if info.Debug { - fmt.Fprintln(dockerCli.Out(), " File Descriptors:", info.NFd) - fmt.Fprintln(dockerCli.Out(), " Goroutines:", info.NGoroutines) - fmt.Fprintln(dockerCli.Out(), " System Time:", info.SystemTime) - fmt.Fprintln(dockerCli.Out(), " EventsListeners:", info.NEventsListener) + fmt.Fprintln(dockerCli.Out(), " File Descriptors:", info.NFd) + fmt.Fprintln(dockerCli.Out(), " Goroutines:", info.NGoroutines) + fmt.Fprintln(dockerCli.Out(), " System Time:", info.SystemTime) + fmt.Fprintln(dockerCli.Out(), " EventsListeners:", info.NEventsListener) } - fprintlnNonEmpty(dockerCli.Out(), "HTTP Proxy:", info.HTTPProxy) - fprintlnNonEmpty(dockerCli.Out(), "HTTPS Proxy:", info.HTTPSProxy) - fprintlnNonEmpty(dockerCli.Out(), "No Proxy:", info.NoProxy) + fprintlnNonEmpty(dockerCli.Out(), " HTTP Proxy:", info.HTTPProxy) + fprintlnNonEmpty(dockerCli.Out(), " HTTPS Proxy:", info.HTTPSProxy) + fprintlnNonEmpty(dockerCli.Out(), " No Proxy:", info.NoProxy) if info.IndexServerAddress != "" { u := dockerCli.ConfigFile().AuthConfigs[info.IndexServerAddress].Username if len(u) > 0 { - fmt.Fprintln(dockerCli.Out(), "Username:", u) + fmt.Fprintln(dockerCli.Out(), " Username:", u) } - fmt.Fprintln(dockerCli.Out(), "Registry:", info.IndexServerAddress) + fmt.Fprintln(dockerCli.Out(), " Registry:", info.IndexServerAddress) } if info.Labels != nil { - fmt.Fprintln(dockerCli.Out(), "Labels:") + fmt.Fprintln(dockerCli.Out(), " Labels:") for _, lbl := range info.Labels { - fmt.Fprintln(dockerCli.Out(), " "+lbl) + fmt.Fprintln(dockerCli.Out(), " "+lbl) } } - fmt.Fprintln(dockerCli.Out(), "Experimental:", info.ExperimentalBuild) - fprintlnNonEmpty(dockerCli.Out(), "Cluster Store:", info.ClusterStore) - fprintlnNonEmpty(dockerCli.Out(), "Cluster Advertise:", info.ClusterAdvertise) + fmt.Fprintln(dockerCli.Out(), " Experimental:", info.ExperimentalBuild) + fprintlnNonEmpty(dockerCli.Out(), " Cluster Store:", info.ClusterStore) + fprintlnNonEmpty(dockerCli.Out(), " Cluster Advertise:", info.ClusterAdvertise) if info.RegistryConfig != nil && (len(info.RegistryConfig.InsecureRegistryCIDRs) > 0 || len(info.RegistryConfig.IndexConfigs) > 0) { - fmt.Fprintln(dockerCli.Out(), "Insecure Registries:") + fmt.Fprintln(dockerCli.Out(), " Insecure Registries:") for _, registry := range info.RegistryConfig.IndexConfigs { if !registry.Secure { - fmt.Fprintln(dockerCli.Out(), " "+registry.Name) + fmt.Fprintln(dockerCli.Out(), " "+registry.Name) } } for _, registry := range info.RegistryConfig.InsecureRegistryCIDRs { mask, _ := registry.Mask.Size() - fmt.Fprintf(dockerCli.Out(), " %s/%d\n", registry.IP.String(), mask) + fmt.Fprintf(dockerCli.Out(), " %s/%d\n", registry.IP.String(), mask) } } if info.RegistryConfig != nil && len(info.RegistryConfig.Mirrors) > 0 { - fmt.Fprintln(dockerCli.Out(), "Registry Mirrors:") + fmt.Fprintln(dockerCli.Out(), " Registry Mirrors:") for _, mirror := range info.RegistryConfig.Mirrors { - fmt.Fprintln(dockerCli.Out(), " "+mirror) + fmt.Fprintln(dockerCli.Out(), " "+mirror) } } - fmt.Fprintln(dockerCli.Out(), "Live Restore Enabled:", info.LiveRestoreEnabled) + fmt.Fprintln(dockerCli.Out(), " Live Restore Enabled:", info.LiveRestoreEnabled) if info.ProductLicense != "" { - fmt.Fprintln(dockerCli.Out(), "Product License:", info.ProductLicense) + fmt.Fprintln(dockerCli.Out(), " Product License:", info.ProductLicense) } fmt.Fprint(dockerCli.Out(), "\n") @@ -284,67 +282,67 @@ func printSwarmInfo(dockerCli command.Cli, info types.Info) { if info.Swarm.LocalNodeState == swarm.LocalNodeStateInactive || info.Swarm.LocalNodeState == swarm.LocalNodeStateLocked { return } - fmt.Fprintln(dockerCli.Out(), " NodeID:", info.Swarm.NodeID) + fmt.Fprintln(dockerCli.Out(), " NodeID:", info.Swarm.NodeID) if info.Swarm.Error != "" { - fmt.Fprintln(dockerCli.Out(), " Error:", info.Swarm.Error) + fmt.Fprintln(dockerCli.Out(), " Error:", info.Swarm.Error) } - fmt.Fprintln(dockerCli.Out(), " Is Manager:", info.Swarm.ControlAvailable) + fmt.Fprintln(dockerCli.Out(), " Is Manager:", info.Swarm.ControlAvailable) if info.Swarm.Cluster != nil && info.Swarm.ControlAvailable && info.Swarm.Error == "" && info.Swarm.LocalNodeState != swarm.LocalNodeStateError { - fmt.Fprintln(dockerCli.Out(), " ClusterID:", info.Swarm.Cluster.ID) - fmt.Fprintln(dockerCli.Out(), " Managers:", info.Swarm.Managers) - fmt.Fprintln(dockerCli.Out(), " Nodes:", info.Swarm.Nodes) + fmt.Fprintln(dockerCli.Out(), " ClusterID:", info.Swarm.Cluster.ID) + fmt.Fprintln(dockerCli.Out(), " Managers:", info.Swarm.Managers) + fmt.Fprintln(dockerCli.Out(), " Nodes:", info.Swarm.Nodes) var strAddrPool strings.Builder if info.Swarm.Cluster.DefaultAddrPool != nil { for _, p := range info.Swarm.Cluster.DefaultAddrPool { strAddrPool.WriteString(p + " ") } - fmt.Fprintln(dockerCli.Out(), " Default Address Pool:", strAddrPool.String()) - fmt.Fprintln(dockerCli.Out(), " SubnetSize:", info.Swarm.Cluster.SubnetSize) + fmt.Fprintln(dockerCli.Out(), " Default Address Pool:", strAddrPool.String()) + fmt.Fprintln(dockerCli.Out(), " SubnetSize:", info.Swarm.Cluster.SubnetSize) } if info.Swarm.Cluster.DataPathPort > 0 { - fmt.Fprintln(dockerCli.Out(), " Data Path Port:", info.Swarm.Cluster.DataPathPort) + fmt.Fprintln(dockerCli.Out(), " Data Path Port:", info.Swarm.Cluster.DataPathPort) } - fmt.Fprintln(dockerCli.Out(), " Orchestration:") + fmt.Fprintln(dockerCli.Out(), " Orchestration:") taskHistoryRetentionLimit := int64(0) if info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit != nil { taskHistoryRetentionLimit = *info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit } - fmt.Fprintln(dockerCli.Out(), " Task History Retention Limit:", taskHistoryRetentionLimit) - fmt.Fprintln(dockerCli.Out(), " Raft:") - fmt.Fprintln(dockerCli.Out(), " Snapshot Interval:", info.Swarm.Cluster.Spec.Raft.SnapshotInterval) + fmt.Fprintln(dockerCli.Out(), " Task History Retention Limit:", taskHistoryRetentionLimit) + fmt.Fprintln(dockerCli.Out(), " Raft:") + fmt.Fprintln(dockerCli.Out(), " Snapshot Interval:", info.Swarm.Cluster.Spec.Raft.SnapshotInterval) if info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots != nil { - fmt.Fprintf(dockerCli.Out(), " Number of Old Snapshots to Retain: %d\n", *info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots) + fmt.Fprintf(dockerCli.Out(), " Number of Old Snapshots to Retain: %d\n", *info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots) } - fmt.Fprintln(dockerCli.Out(), " Heartbeat Tick:", info.Swarm.Cluster.Spec.Raft.HeartbeatTick) - fmt.Fprintln(dockerCli.Out(), " Election Tick:", info.Swarm.Cluster.Spec.Raft.ElectionTick) - fmt.Fprintln(dockerCli.Out(), " Dispatcher:") - fmt.Fprintln(dockerCli.Out(), " Heartbeat Period:", units.HumanDuration(info.Swarm.Cluster.Spec.Dispatcher.HeartbeatPeriod)) - fmt.Fprintln(dockerCli.Out(), " CA Configuration:") - fmt.Fprintln(dockerCli.Out(), " Expiry Duration:", units.HumanDuration(info.Swarm.Cluster.Spec.CAConfig.NodeCertExpiry)) - fmt.Fprintln(dockerCli.Out(), " Force Rotate:", info.Swarm.Cluster.Spec.CAConfig.ForceRotate) + fmt.Fprintln(dockerCli.Out(), " Heartbeat Tick:", info.Swarm.Cluster.Spec.Raft.HeartbeatTick) + fmt.Fprintln(dockerCli.Out(), " Election Tick:", info.Swarm.Cluster.Spec.Raft.ElectionTick) + fmt.Fprintln(dockerCli.Out(), " Dispatcher:") + fmt.Fprintln(dockerCli.Out(), " Heartbeat Period:", units.HumanDuration(info.Swarm.Cluster.Spec.Dispatcher.HeartbeatPeriod)) + fmt.Fprintln(dockerCli.Out(), " CA Configuration:") + fmt.Fprintln(dockerCli.Out(), " Expiry Duration:", units.HumanDuration(info.Swarm.Cluster.Spec.CAConfig.NodeCertExpiry)) + fmt.Fprintln(dockerCli.Out(), " Force Rotate:", info.Swarm.Cluster.Spec.CAConfig.ForceRotate) if caCert := strings.TrimSpace(info.Swarm.Cluster.Spec.CAConfig.SigningCACert); caCert != "" { - fmt.Fprintf(dockerCli.Out(), " Signing CA Certificate: \n%s\n\n", caCert) + fmt.Fprintf(dockerCli.Out(), " Signing CA Certificate: \n%s\n\n", caCert) } if len(info.Swarm.Cluster.Spec.CAConfig.ExternalCAs) > 0 { - fmt.Fprintln(dockerCli.Out(), " External CAs:") + fmt.Fprintln(dockerCli.Out(), " External CAs:") for _, entry := range info.Swarm.Cluster.Spec.CAConfig.ExternalCAs { - fmt.Fprintf(dockerCli.Out(), " %s: %s\n", entry.Protocol, entry.URL) + fmt.Fprintf(dockerCli.Out(), " %s: %s\n", entry.Protocol, entry.URL) } } - fmt.Fprintln(dockerCli.Out(), " Autolock Managers:", info.Swarm.Cluster.Spec.EncryptionConfig.AutoLockManagers) - fmt.Fprintln(dockerCli.Out(), " Root Rotation In Progress:", info.Swarm.Cluster.RootRotationInProgress) + fmt.Fprintln(dockerCli.Out(), " Autolock Managers:", info.Swarm.Cluster.Spec.EncryptionConfig.AutoLockManagers) + fmt.Fprintln(dockerCli.Out(), " Root Rotation In Progress:", info.Swarm.Cluster.RootRotationInProgress) } - fmt.Fprintln(dockerCli.Out(), " Node Address:", info.Swarm.NodeAddr) + fmt.Fprintln(dockerCli.Out(), " Node Address:", info.Swarm.NodeAddr) if len(info.Swarm.RemoteManagers) > 0 { managers := []string{} for _, entry := range info.Swarm.RemoteManagers { managers = append(managers, entry.Addr) } sort.Strings(managers) - fmt.Fprintln(dockerCli.Out(), " Manager Addresses:") + fmt.Fprintln(dockerCli.Out(), " Manager Addresses:") for _, entry := range managers { - fmt.Fprintf(dockerCli.Out(), " %s\n", entry) + fmt.Fprintf(dockerCli.Out(), " %s\n", entry) } } } diff --git a/cli/command/system/testdata/docker-info-badsec.golden b/cli/command/system/testdata/docker-info-badsec.golden index 337991ca7411..7c074e8d55f7 100644 --- a/cli/command/system/testdata/docker-info-badsec.golden +++ b/cli/command/system/testdata/docker-info-badsec.golden @@ -1,54 +1,52 @@ -Client ------- -Debug Mode: false +Client: + Debug Mode: false -Server ------- -Containers: 0 - Running: 0 - Paused: 0 - Stopped: 0 -Images: 0 -Server Version: 17.06.1-ce -Storage Driver: aufs - Root Dir: /var/lib/docker/aufs - Backing Filesystem: extfs - Dirs: 0 - Dirperm1 Supported: true -Logging Driver: json-file -Cgroup Driver: cgroupfs -Plugins: - Volume: local - Network: bridge host macvlan null overlay - Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog -Swarm: inactive -Runtimes: runc -Default Runtime: runc -Init Binary: docker-init -containerd version: 6e23458c129b551d5c9871e5174f6b1b7f6d1170 -runc version: 810190ceaa507aa2727d7ae6f4790c76ec150bd2 -init version: 949e6fa -Kernel Version: 4.4.0-87-generic -Operating System: Ubuntu 16.04.3 LTS -OSType: linux -Architecture: x86_64 -CPUs: 2 -Total Memory: 1.953GiB -Name: system-sample -ID: EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX -Docker Root Dir: /var/lib/docker -Debug Mode: true - File Descriptors: 33 - Goroutines: 135 - System Time: 2017-08-24T17:44:34.077811894Z - EventsListeners: 0 -Registry: https://index.docker.io/v1/ -Labels: - provider=digitalocean -Experimental: false -Insecure Registries: - 127.0.0.0/8 -Live Restore Enabled: false +Server: + Containers: 0 + Running: 0 + Paused: 0 + Stopped: 0 + Images: 0 + Server Version: 17.06.1-ce + Storage Driver: aufs + Root Dir: /var/lib/docker/aufs + Backing Filesystem: extfs + Dirs: 0 + Dirperm1 Supported: true + Logging Driver: json-file + Cgroup Driver: cgroupfs + Plugins: + Volume: local + Network: bridge host macvlan null overlay + Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog + Swarm: inactive + Runtimes: runc + Default Runtime: runc + Init Binary: docker-init + containerd version: 6e23458c129b551d5c9871e5174f6b1b7f6d1170 + runc version: 810190ceaa507aa2727d7ae6f4790c76ec150bd2 + init version: 949e6fa + Kernel Version: 4.4.0-87-generic + Operating System: Ubuntu 16.04.3 LTS + OSType: linux + Architecture: x86_64 + CPUs: 2 + Total Memory: 1.953GiB + Name: system-sample + ID: EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX + Docker Root Dir: /var/lib/docker + Debug Mode: true + File Descriptors: 33 + Goroutines: 135 + System Time: 2017-08-24T17:44:34.077811894Z + EventsListeners: 0 + Registry: https://index.docker.io/v1/ + Labels: + provider=digitalocean + Experimental: false + Insecure Registries: + 127.0.0.0/8 + Live Restore Enabled: false ERROR: an error happened ERROR: invalid empty security option diff --git a/cli/command/system/testdata/docker-info-errors.golden b/cli/command/system/testdata/docker-info-errors.golden index cac76a12af81..549611ee1342 100644 --- a/cli/command/system/testdata/docker-info-errors.golden +++ b/cli/command/system/testdata/docker-info-errors.golden @@ -1,7 +1,5 @@ -Client ------- +Client: ERROR: a client error occurred -Server ------- +Server: ERROR: a server error occurred diff --git a/cli/command/system/testdata/docker-info-no-swarm.golden b/cli/command/system/testdata/docker-info-no-swarm.golden index 6c71576c1b44..b1539de0db59 100644 --- a/cli/command/system/testdata/docker-info-no-swarm.golden +++ b/cli/command/system/testdata/docker-info-no-swarm.golden @@ -1,56 +1,54 @@ -Client ------- -Debug Mode: true +Client: + Debug Mode: true -Server ------- -Containers: 0 - Running: 0 - Paused: 0 - Stopped: 0 -Images: 0 -Server Version: 17.06.1-ce -Storage Driver: aufs - Root Dir: /var/lib/docker/aufs - Backing Filesystem: extfs - Dirs: 0 - Dirperm1 Supported: true -Logging Driver: json-file -Cgroup Driver: cgroupfs -Plugins: - Volume: local - Network: bridge host macvlan null overlay - Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog -Swarm: inactive -Runtimes: runc -Default Runtime: runc -Init Binary: docker-init -containerd version: 6e23458c129b551d5c9871e5174f6b1b7f6d1170 -runc version: 810190ceaa507aa2727d7ae6f4790c76ec150bd2 -init version: 949e6fa -Security Options: - apparmor - seccomp - Profile: default -Kernel Version: 4.4.0-87-generic -Operating System: Ubuntu 16.04.3 LTS -OSType: linux -Architecture: x86_64 -CPUs: 2 -Total Memory: 1.953GiB -Name: system-sample -ID: EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX -Docker Root Dir: /var/lib/docker -Debug Mode: true - File Descriptors: 33 - Goroutines: 135 - System Time: 2017-08-24T17:44:34.077811894Z - EventsListeners: 0 -Registry: https://index.docker.io/v1/ -Labels: - provider=digitalocean -Experimental: false -Insecure Registries: - 127.0.0.0/8 -Live Restore Enabled: false +Server: + Containers: 0 + Running: 0 + Paused: 0 + Stopped: 0 + Images: 0 + Server Version: 17.06.1-ce + Storage Driver: aufs + Root Dir: /var/lib/docker/aufs + Backing Filesystem: extfs + Dirs: 0 + Dirperm1 Supported: true + Logging Driver: json-file + Cgroup Driver: cgroupfs + Plugins: + Volume: local + Network: bridge host macvlan null overlay + Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog + Swarm: inactive + Runtimes: runc + Default Runtime: runc + Init Binary: docker-init + containerd version: 6e23458c129b551d5c9871e5174f6b1b7f6d1170 + runc version: 810190ceaa507aa2727d7ae6f4790c76ec150bd2 + init version: 949e6fa + Security Options: + apparmor + seccomp + Profile: default + Kernel Version: 4.4.0-87-generic + Operating System: Ubuntu 16.04.3 LTS + OSType: linux + Architecture: x86_64 + CPUs: 2 + Total Memory: 1.953GiB + Name: system-sample + ID: EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX + Docker Root Dir: /var/lib/docker + Debug Mode: true + File Descriptors: 33 + Goroutines: 135 + System Time: 2017-08-24T17:44:34.077811894Z + EventsListeners: 0 + Registry: https://index.docker.io/v1/ + Labels: + provider=digitalocean + Experimental: false + Insecure Registries: + 127.0.0.0/8 + Live Restore Enabled: false diff --git a/cli/command/system/testdata/docker-info-with-swarm.golden b/cli/command/system/testdata/docker-info-with-swarm.golden index cd9b08f38f66..e6c04f695c08 100644 --- a/cli/command/system/testdata/docker-info-with-swarm.golden +++ b/cli/command/system/testdata/docker-info-with-swarm.golden @@ -1,78 +1,76 @@ -Client ------- -Debug Mode: false +Client: + Debug Mode: false -Server ------- -Containers: 0 - Running: 0 - Paused: 0 - Stopped: 0 -Images: 0 -Server Version: 17.06.1-ce -Storage Driver: aufs - Root Dir: /var/lib/docker/aufs - Backing Filesystem: extfs - Dirs: 0 - Dirperm1 Supported: true -Logging Driver: json-file -Cgroup Driver: cgroupfs -Plugins: - Volume: local - Network: bridge host macvlan null overlay - Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog -Swarm: active - NodeID: qo2dfdig9mmxqkawulggepdih - Is Manager: true - ClusterID: 9vs5ygs0gguyyec4iqf2314c0 - Managers: 1 - Nodes: 1 - Orchestration: - Task History Retention Limit: 5 - Raft: - Snapshot Interval: 10000 - Number of Old Snapshots to Retain: 0 - Heartbeat Tick: 1 - Election Tick: 3 - Dispatcher: - Heartbeat Period: 5 seconds - CA Configuration: - Expiry Duration: 3 months - Force Rotate: 0 - Autolock Managers: true - Root Rotation In Progress: false - Node Address: 165.227.107.89 - Manager Addresses: - 165.227.107.89:2377 -Runtimes: runc -Default Runtime: runc -Init Binary: docker-init -containerd version: 6e23458c129b551d5c9871e5174f6b1b7f6d1170 -runc version: 810190ceaa507aa2727d7ae6f4790c76ec150bd2 -init version: 949e6fa -Security Options: - apparmor - seccomp - Profile: default -Kernel Version: 4.4.0-87-generic -Operating System: Ubuntu 16.04.3 LTS -OSType: linux -Architecture: x86_64 -CPUs: 2 -Total Memory: 1.953GiB -Name: system-sample -ID: EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX -Docker Root Dir: /var/lib/docker -Debug Mode: true - File Descriptors: 33 - Goroutines: 135 - System Time: 2017-08-24T17:44:34.077811894Z - EventsListeners: 0 -Registry: https://index.docker.io/v1/ -Labels: - provider=digitalocean -Experimental: false -Insecure Registries: - 127.0.0.0/8 -Live Restore Enabled: false +Server: + Containers: 0 + Running: 0 + Paused: 0 + Stopped: 0 + Images: 0 + Server Version: 17.06.1-ce + Storage Driver: aufs + Root Dir: /var/lib/docker/aufs + Backing Filesystem: extfs + Dirs: 0 + Dirperm1 Supported: true + Logging Driver: json-file + Cgroup Driver: cgroupfs + Plugins: + Volume: local + Network: bridge host macvlan null overlay + Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog + Swarm: active + NodeID: qo2dfdig9mmxqkawulggepdih + Is Manager: true + ClusterID: 9vs5ygs0gguyyec4iqf2314c0 + Managers: 1 + Nodes: 1 + Orchestration: + Task History Retention Limit: 5 + Raft: + Snapshot Interval: 10000 + Number of Old Snapshots to Retain: 0 + Heartbeat Tick: 1 + Election Tick: 3 + Dispatcher: + Heartbeat Period: 5 seconds + CA Configuration: + Expiry Duration: 3 months + Force Rotate: 0 + Autolock Managers: true + Root Rotation In Progress: false + Node Address: 165.227.107.89 + Manager Addresses: + 165.227.107.89:2377 + Runtimes: runc + Default Runtime: runc + Init Binary: docker-init + containerd version: 6e23458c129b551d5c9871e5174f6b1b7f6d1170 + runc version: 810190ceaa507aa2727d7ae6f4790c76ec150bd2 + init version: 949e6fa + Security Options: + apparmor + seccomp + Profile: default + Kernel Version: 4.4.0-87-generic + Operating System: Ubuntu 16.04.3 LTS + OSType: linux + Architecture: x86_64 + CPUs: 2 + Total Memory: 1.953GiB + Name: system-sample + ID: EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX + Docker Root Dir: /var/lib/docker + Debug Mode: true + File Descriptors: 33 + Goroutines: 135 + System Time: 2017-08-24T17:44:34.077811894Z + EventsListeners: 0 + Registry: https://index.docker.io/v1/ + Labels: + provider=digitalocean + Experimental: false + Insecure Registries: + 127.0.0.0/8 + Live Restore Enabled: false diff --git a/docs/reference/commandline/info.md b/docs/reference/commandline/info.md index fc64141ed35a..aee122008da9 100644 --- a/docs/reference/commandline/info.md +++ b/docs/reference/commandline/info.md @@ -55,60 +55,58 @@ information about the `devicemapper` storage driver is shown: ```bash $ docker info -Client ------- -Debug Mode: false - -Server ------- -Containers: 14 - Running: 3 - Paused: 1 - Stopped: 10 -Images: 52 -Server Version: 1.10.3 -Storage Driver: devicemapper - Pool Name: docker-202:2-25583803-pool - Pool Blocksize: 65.54 kB - Base Device Size: 10.74 GB - Backing Filesystem: xfs - Data file: /dev/loop0 - Metadata file: /dev/loop1 - Data Space Used: 1.68 GB - Data Space Total: 107.4 GB - Data Space Available: 7.548 GB - Metadata Space Used: 2.322 MB - Metadata Space Total: 2.147 GB - Metadata Space Available: 2.145 GB - Udev Sync Supported: true - Deferred Removal Enabled: false - Deferred Deletion Enabled: false - Deferred Deleted Device Count: 0 - Data loop file: /var/lib/docker/devicemapper/devicemapper/data - Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata - Library Version: 1.02.107-RHEL7 (2015-12-01) -Execution Driver: native-0.2 -Logging Driver: json-file -Plugins: - Volume: local - Network: null host bridge -Kernel Version: 3.10.0-327.el7.x86_64 -Operating System: Red Hat Enterprise Linux Server 7.2 (Maipo) -OSType: linux -Architecture: x86_64 -CPUs: 1 -Total Memory: 991.7 MiB -Name: ip-172-30-0-91.ec2.internal -ID: I54V:OLXT:HVMM:TPKO:JPHQ:CQCD:JNLC:O3BZ:4ZVJ:43XJ:PFHZ:6N2S -Docker Root Dir: /var/lib/docker -Debug Mode: false -Username: gordontheturtle -Registry: https://index.docker.io/v1/ -Insecure registries: - myinsecurehost:5000 - 127.0.0.0/8 +Client: + Debug Mode: false + +Server: + Containers: 14 + Running: 3 + Paused: 1 + Stopped: 10 + Images: 52 + Server Version: 1.10.3 + Storage Driver: devicemapper + Pool Name: docker-202:2-25583803-pool + Pool Blocksize: 65.54 kB + Base Device Size: 10.74 GB + Backing Filesystem: xfs + Data file: /dev/loop0 + Metadata file: /dev/loop1 + Data Space Used: 1.68 GB + Data Space Total: 107.4 GB + Data Space Available: 7.548 GB + Metadata Space Used: 2.322 MB + Metadata Space Total: 2.147 GB + Metadata Space Available: 2.145 GB + Udev Sync Supported: true + Deferred Removal Enabled: false + Deferred Deletion Enabled: false + Deferred Deleted Device Count: 0 + Data loop file: /var/lib/docker/devicemapper/devicemapper/data + Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata + Library Version: 1.02.107-RHEL7 (2015-12-01) + Execution Driver: native-0.2 + Logging Driver: json-file + Plugins: + Volume: local + Network: null host bridge + Kernel Version: 3.10.0-327.el7.x86_64 + Operating System: Red Hat Enterprise Linux Server 7.2 (Maipo) + OSType: linux + Architecture: x86_64 + CPUs: 1 + Total Memory: 991.7 MiB + Name: ip-172-30-0-91.ec2.internal + ID: I54V:OLXT:HVMM:TPKO:JPHQ:CQCD:JNLC:O3BZ:4ZVJ:43XJ:PFHZ:6N2S + Docker Root Dir: /var/lib/docker + Debug Mode: false + Username: gordontheturtle + Registry: https://index.docker.io/v1/ + Insecure registries: + myinsecurehost:5000 + 127.0.0.0/8 ``` - + ### Show debugging output Here is a sample output for a daemon running on Ubuntu, using the overlay2 @@ -116,87 +114,85 @@ storage driver and a node that is part of a 2-node swarm: ```bash $ docker -D info -Client ------- -Debug Mode: true - -Server ------- -Containers: 14 - Running: 3 - Paused: 1 - Stopped: 10 -Images: 52 -Server Version: 1.13.0 -Storage Driver: overlay2 - Backing Filesystem: extfs - Supports d_type: true - Native Overlay Diff: false -Logging Driver: json-file -Cgroup Driver: cgroupfs -Plugins: - Volume: local - Network: bridge host macvlan null overlay -Swarm: active - NodeID: rdjq45w1op418waxlairloqbm - Is Manager: true - ClusterID: te8kdyw33n36fqiz74bfjeixd - Managers: 1 - Nodes: 2 - Orchestration: - Task History Retention Limit: 5 - Raft: - Snapshot Interval: 10000 - Number of Old Snapshots to Retain: 0 - Heartbeat Tick: 1 - Election Tick: 3 - Dispatcher: - Heartbeat Period: 5 seconds - CA Configuration: - Expiry Duration: 3 months - Root Rotation In Progress: false - Node Address: 172.16.66.128 172.16.66.129 - Manager Addresses: - 172.16.66.128:2477 -Runtimes: runc -Default Runtime: runc -Init Binary: docker-init -containerd version: 8517738ba4b82aff5662c97ca4627e7e4d03b531 -runc version: ac031b5bf1cc92239461125f4c1ffb760522bbf2 -init version: N/A (expected: v0.13.0) -Security Options: - apparmor - seccomp - Profile: default -Kernel Version: 4.4.0-31-generic -Operating System: Ubuntu 16.04.1 LTS -OSType: linux -Architecture: x86_64 -CPUs: 2 -Total Memory: 1.937 GiB -Name: ubuntu -ID: H52R:7ZR6:EIIA:76JG:ORIY:BVKF:GSFU:HNPG:B5MK:APSC:SZ3Q:N326 -Docker Root Dir: /var/lib/docker -Debug Mode: true - File Descriptors: 30 - Goroutines: 123 - System Time: 2016-11-12T17:24:37.955404361-08:00 - EventsListeners: 0 -Http Proxy: http://test:test@proxy.example.com:8080 -Https Proxy: https://test:test@proxy.example.com:8080 -No Proxy: localhost,127.0.0.1,docker-registry.somecorporation.com -Registry: https://index.docker.io/v1/ -WARNING: No swap limit support -Labels: - storage=ssd - staging=true -Experimental: false -Insecure Registries: - 127.0.0.0/8 -Registry Mirrors: - http://192.168.1.2/ - http://registry-mirror.example.com:5000/ -Live Restore Enabled: false +Client: + Debug Mode: true + +Server: + Containers: 14 + Running: 3 + Paused: 1 + Stopped: 10 + Images: 52 + Server Version: 1.13.0 + Storage Driver: overlay2 + Backing Filesystem: extfs + Supports d_type: true + Native Overlay Diff: false + Logging Driver: json-file + Cgroup Driver: cgroupfs + Plugins: + Volume: local + Network: bridge host macvlan null overlay + Swarm: active + NodeID: rdjq45w1op418waxlairloqbm + Is Manager: true + ClusterID: te8kdyw33n36fqiz74bfjeixd + Managers: 1 + Nodes: 2 + Orchestration: + Task History Retention Limit: 5 + Raft: + Snapshot Interval: 10000 + Number of Old Snapshots to Retain: 0 + Heartbeat Tick: 1 + Election Tick: 3 + Dispatcher: + Heartbeat Period: 5 seconds + CA Configuration: + Expiry Duration: 3 months + Root Rotation In Progress: false + Node Address: 172.16.66.128 172.16.66.129 + Manager Addresses: + 172.16.66.128:2477 + Runtimes: runc + Default Runtime: runc + Init Binary: docker-init + containerd version: 8517738ba4b82aff5662c97ca4627e7e4d03b531 + runc version: ac031b5bf1cc92239461125f4c1ffb760522bbf2 + init version: N/A (expected: v0.13.0) + Security Options: + apparmor + seccomp + Profile: default + Kernel Version: 4.4.0-31-generic + Operating System: Ubuntu 16.04.1 LTS + OSType: linux + Architecture: x86_64 + CPUs: 2 + Total Memory: 1.937 GiB + Name: ubuntu + ID: H52R:7ZR6:EIIA:76JG:ORIY:BVKF:GSFU:HNPG:B5MK:APSC:SZ3Q:N326 + Docker Root Dir: /var/lib/docker + Debug Mode: true + File Descriptors: 30 + Goroutines: 123 + System Time: 2016-11-12T17:24:37.955404361-08:00 + EventsListeners: 0 + Http Proxy: http://test:test@proxy.example.com:8080 + Https Proxy: https://test:test@proxy.example.com:8080 + No Proxy: localhost,127.0.0.1,docker-registry.somecorporation.com + Registry: https://index.docker.io/v1/ + WARNING: No swap limit support + Labels: + storage=ssd + staging=true + Experimental: false + Insecure Registries: + 127.0.0.0/8 + Registry Mirrors: + http://192.168.1.2/ + http://registry-mirror.example.com:5000/ + Live Restore Enabled: false ``` The global `-D` option causes all `docker` commands to output debug information. @@ -217,41 +213,39 @@ Here is a sample output for a daemon running on Windows Server 2016: ```none E:\docker>docker info -Client ------- -Debug Mode: false - -Server ------- -Containers: 1 - Running: 0 - Paused: 0 - Stopped: 1 -Images: 17 -Server Version: 1.13.0 -Storage Driver: windowsfilter - Windows: -Logging Driver: json-file -Plugins: - Volume: local - Network: nat null overlay -Swarm: inactive -Default Isolation: process -Kernel Version: 10.0 14393 (14393.206.amd64fre.rs1_release.160912-1937) -Operating System: Windows Server 2016 Datacenter -OSType: windows -Architecture: x86_64 -CPUs: 8 -Total Memory: 3.999 GiB -Name: WIN-V0V70C0LU5P -ID: NYMS:B5VK:UMSL:FVDZ:EWB5:FKVK:LPFL:FJMQ:H6FT:BZJ6:L2TD:XH62 -Docker Root Dir: C:\control -Debug Mode: false -Registry: https://index.docker.io/v1/ -Insecure Registries: - 127.0.0.0/8 -Registry Mirrors: - http://192.168.1.2/ - http://registry-mirror.example.com:5000/ -Live Restore Enabled: false +Client: + Debug Mode: false + +Server: + Containers: 1 + Running: 0 + Paused: 0 + Stopped: 1 + Images: 17 + Server Version: 1.13.0 + Storage Driver: windowsfilter + Windows: + Logging Driver: json-file + Plugins: + Volume: local + Network: nat null overlay + Swarm: inactive + Default Isolation: process + Kernel Version: 10.0 14393 (14393.206.amd64fre.rs1_release.160912-1937) + Operating System: Windows Server 2016 Datacenter + OSType: windows + Architecture: x86_64 + CPUs: 8 + Total Memory: 3.999 GiB + Name: WIN-V0V70C0LU5P + ID: NYMS:B5VK:UMSL:FVDZ:EWB5:FKVK:LPFL:FJMQ:H6FT:BZJ6:L2TD:XH62 + Docker Root Dir: C:\control + Debug Mode: false + Registry: https://index.docker.io/v1/ + Insecure Registries: + 127.0.0.0/8 + Registry Mirrors: + http://192.168.1.2/ + http://registry-mirror.example.com:5000/ + Live Restore Enabled: false ``` diff --git a/man/src/system/info.md b/man/src/system/info.md index 774f72f465a3..14c04a31a538 100644 --- a/man/src/system/info.md +++ b/man/src/system/info.md @@ -24,87 +24,85 @@ Here is a sample output for a daemon running on Ubuntu, using the overlay2 storage driver: $ docker -D info - Client - ------ - Debug Mode: true - - Server - ------ - Containers: 14 - Running: 3 - Paused: 1 - Stopped: 10 - Images: 52 - Server Version: 1.13.0 - Storage Driver: overlay2 - Backing Filesystem: extfs - Supports d_type: true - Native Overlay Diff: false - Logging Driver: json-file - Cgroup Driver: cgroupfs - Plugins: - Volume: local - Network: bridge host macvlan null overlay - Swarm: active - NodeID: rdjq45w1op418waxlairloqbm - Is Manager: true - ClusterID: te8kdyw33n36fqiz74bfjeixd - Managers: 1 - Nodes: 2 - Orchestration: - Task History Retention Limit: 5 - Raft: - Snapshot Interval: 10000 - Number of Old Snapshots to Retain: 0 - Heartbeat Tick: 1 - Election Tick: 3 - Dispatcher: - Heartbeat Period: 5 seconds - CA Configuration: - Expiry Duration: 3 months - Node Address: 172.16.66.128 172.16.66.129 - Manager Addresses: - 172.16.66.128:2477 - Runtimes: runc - Default Runtime: runc - Init Binary: docker-init - containerd version: 8517738ba4b82aff5662c97ca4627e7e4d03b531 - runc version: ac031b5bf1cc92239461125f4c1ffb760522bbf2 - init version: N/A (expected: v0.13.0) - Security Options: - apparmor - seccomp - Profile: default - Kernel Version: 4.4.0-31-generic - Operating System: Ubuntu 16.04.1 LTS - OSType: linux - Architecture: x86_64 - CPUs: 2 - Total Memory: 1.937 GiB - Name: ubuntu - ID: H52R:7ZR6:EIIA:76JG:ORIY:BVKF:GSFU:HNPG:B5MK:APSC:SZ3Q:N326 - Docker Root Dir: /var/lib/docker - Debug Mode: true - File Descriptors: 30 - Goroutines: 123 - System Time: 2016-11-12T17:24:37.955404361-08:00 - EventsListeners: 0 - Http Proxy: http://test:test@proxy.example.com:8080 - Https Proxy: https://test:test@proxy.example.com:8080 - No Proxy: localhost,127.0.0.1,docker-registry.somecorporation.com - Registry: https://index.docker.io/v1/ - WARNING: No swap limit support - Labels: - storage=ssd - staging=true - Experimental: false - Insecure Registries: - 127.0.0.0/8 - Registry Mirrors: - http://192.168.1.2/ - http://registry-mirror.example.com:5000/ - Live Restore Enabled: false - + Client: + Debug Mode: true + + Server: + Containers: 14 + Running: 3 + Paused: 1 + Stopped: 10 + Images: 52 + Server Version: 1.13.0 + Storage Driver: overlay2 + Backing Filesystem: extfs + Supports d_type: true + Native Overlay Diff: false + Logging Driver: json-file + Cgroup Driver: cgroupfs + Plugins: + Volume: local + Network: bridge host macvlan null overlay + Swarm: active + NodeID: rdjq45w1op418waxlairloqbm + Is Manager: true + ClusterID: te8kdyw33n36fqiz74bfjeixd + Managers: 1 + Nodes: 2 + Orchestration: + Task History Retention Limit: 5 + Raft: + Snapshot Interval: 10000 + Number of Old Snapshots to Retain: 0 + Heartbeat Tick: 1 + Election Tick: 3 + Dispatcher: + Heartbeat Period: 5 seconds + CA Configuration: + Expiry Duration: 3 months + Node Address: 172.16.66.128 172.16.66.129 + Manager Addresses: + 172.16.66.128:2477 + Runtimes: runc + Default Runtime: runc + Init Binary: docker-init + containerd version: 8517738ba4b82aff5662c97ca4627e7e4d03b531 + runc version: ac031b5bf1cc92239461125f4c1ffb760522bbf2 + init version: N/A (expected: v0.13.0) + Security Options: + apparmor + seccomp + Profile: default + Kernel Version: 4.4.0-31-generic + Operating System: Ubuntu 16.04.1 LTS + OSType: linux + Architecture: x86_64 + CPUs: 2 + Total Memory: 1.937 GiB + Name: ubuntu + ID: H52R:7ZR6:EIIA:76JG:ORIY:BVKF:GSFU:HNPG:B5MK:APSC:SZ3Q:N326 + Docker Root Dir: /var/lib/docker + Debug Mode: true + File Descriptors: 30 + Goroutines: 123 + System Time: 2016-11-12T17:24:37.955404361-08:00 + EventsListeners: 0 + Http Proxy: http://test:test@proxy.example.com:8080 + Https Proxy: https://test:test@proxy.example.com:8080 + No Proxy: localhost,127.0.0.1,docker-registry.somecorporation.com + Registry: https://index.docker.io/v1/ + WARNING: No swap limit support + Labels: + storage=ssd + staging=true + Experimental: false + Insecure Registries: + 127.0.0.0/8 + Registry Mirrors: + http://192.168.1.2/ + http://registry-mirror.example.com:5000/ + Live Restore Enabled: false + The global `-D` option tells all `docker` commands to output debug information. @@ -114,59 +112,57 @@ using the devicemapper storage driver. As can be seen in the output, additional information about the devicemapper storage driver is shown: $ docker info - Client - ------ - Debug Mode: false - - Server - ------ - Containers: 14 - Running: 3 - Paused: 1 - Stopped: 10 - Untagged Images: 52 - Server Version: 1.10.3 - Storage Driver: devicemapper - Pool Name: docker-202:2-25583803-pool - Pool Blocksize: 65.54 kB - Base Device Size: 10.74 GB - Backing Filesystem: xfs - Data file: /dev/loop0 - Metadata file: /dev/loop1 - Data Space Used: 1.68 GB - Data Space Total: 107.4 GB - Data Space Available: 7.548 GB - Metadata Space Used: 2.322 MB - Metadata Space Total: 2.147 GB - Metadata Space Available: 2.145 GB - Udev Sync Supported: true - Deferred Removal Enabled: false - Deferred Deletion Enabled: false - Deferred Deleted Device Count: 0 - Data loop file: /var/lib/docker/devicemapper/devicemapper/data - Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata - Library Version: 1.02.107-RHEL7 (2015-12-01) - Execution Driver: native-0.2 - Logging Driver: json-file - Plugins: - Volume: local - Network: null host bridge - Kernel Version: 3.10.0-327.el7.x86_64 - Operating System: Red Hat Enterprise Linux Server 7.2 (Maipo) - OSType: linux - Architecture: x86_64 - CPUs: 1 - Total Memory: 991.7 MiB - Name: ip-172-30-0-91.ec2.internal - ID: I54V:OLXT:HVMM:TPKO:JPHQ:CQCD:JNLC:O3BZ:4ZVJ:43XJ:PFHZ:6N2S - Docker Root Dir: /var/lib/docker - Debug Mode: false - Username: gordontheturtle - Registry: https://index.docker.io/v1/ - Insecure registries: - myinsecurehost:5000 - 127.0.0.0/8 - + Client: + Debug Mode: false + + Server: + Containers: 14 + Running: 3 + Paused: 1 + Stopped: 10 + Untagged Images: 52 + Server Version: 1.10.3 + Storage Driver: devicemapper + Pool Name: docker-202:2-25583803-pool + Pool Blocksize: 65.54 kB + Base Device Size: 10.74 GB + Backing Filesystem: xfs + Data file: /dev/loop0 + Metadata file: /dev/loop1 + Data Space Used: 1.68 GB + Data Space Total: 107.4 GB + Data Space Available: 7.548 GB + Metadata Space Used: 2.322 MB + Metadata Space Total: 2.147 GB + Metadata Space Available: 2.145 GB + Udev Sync Supported: true + Deferred Removal Enabled: false + Deferred Deletion Enabled: false + Deferred Deleted Device Count: 0 + Data loop file: /var/lib/docker/devicemapper/devicemapper/data + Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata + Library Version: 1.02.107-RHEL7 (2015-12-01) + Execution Driver: native-0.2 + Logging Driver: json-file + Plugins: + Volume: local + Network: null host bridge + Kernel Version: 3.10.0-327.el7.x86_64 + Operating System: Red Hat Enterprise Linux Server 7.2 (Maipo) + OSType: linux + Architecture: x86_64 + CPUs: 1 + Total Memory: 991.7 MiB + Name: ip-172-30-0-91.ec2.internal + ID: I54V:OLXT:HVMM:TPKO:JPHQ:CQCD:JNLC:O3BZ:4ZVJ:43XJ:PFHZ:6N2S + Docker Root Dir: /var/lib/docker + Debug Mode: false + Username: gordontheturtle + Registry: https://index.docker.io/v1/ + Insecure registries: + myinsecurehost:5000 + 127.0.0.0/8 + You can also specify the output format: $ docker info --format '{{json .}}' From a07637ae31590f8edda1db44426497e69c9c3d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Scala?= Date: Fri, 9 Nov 2018 08:35:34 +0100 Subject: [PATCH 15/60] Updates ssh connhelper error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: François Scala --- cli/connhelper/ssh/ssh.go | 14 +++++++------- cli/connhelper/ssh/ssh_test.go | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cli/connhelper/ssh/ssh.go b/cli/connhelper/ssh/ssh.go index 0f2cfabc07ef..f134df1386bd 100644 --- a/cli/connhelper/ssh/ssh.go +++ b/cli/connhelper/ssh/ssh.go @@ -12,7 +12,7 @@ import ( func New(daemonURL string) (string, []string, error) { sp, err := parseSSHURL(daemonURL) if err != nil { - return "", nil, err + return "", nil, errors.Wrap(err, "SSH host connection is not valid") } return "ssh", append(sp.Args(), []string{"--", "docker", "system", "dial-stdio"}...), nil } @@ -23,7 +23,7 @@ func parseSSHURL(daemonURL string) (*sshSpec, error) { return nil, err } if u.Scheme != "ssh" { - return nil, errors.Errorf("expected scheme ssh, got %s", u.Scheme) + return nil, errors.Errorf("expected scheme ssh, got %q", u.Scheme) } var sp sshSpec @@ -31,22 +31,22 @@ func parseSSHURL(daemonURL string) (*sshSpec, error) { if u.User != nil { sp.user = u.User.Username() if _, ok := u.User.Password(); ok { - return nil, errors.New("ssh helper does not accept plain-text password") + return nil, errors.New("plain-text password is not supported") } } sp.host = u.Hostname() if sp.host == "" { - return nil, errors.Errorf("host is not specified") + return nil, errors.Errorf("no host specified") } sp.port = u.Port() if u.Path != "" { - return nil, errors.Errorf("extra path: %s", u.Path) + return nil, errors.Errorf("extra path after the host: %q", u.Path) } if u.RawQuery != "" { - return nil, errors.Errorf("extra query: %s", u.RawQuery) + return nil, errors.Errorf("extra query after the host: %q", u.RawQuery) } if u.Fragment != "" { - return nil, errors.Errorf("extra fragment: %s", u.Fragment) + return nil, errors.Errorf("extra fragment after the host: %q", u.Fragment) } return &sp, err } diff --git a/cli/connhelper/ssh/ssh_test.go b/cli/connhelper/ssh/ssh_test.go index 30a638545cfc..8e1d33d89537 100644 --- a/cli/connhelper/ssh/ssh_test.go +++ b/cli/connhelper/ssh/ssh_test.go @@ -29,27 +29,27 @@ func TestParseSSHURL(t *testing.T) { }, { url: "ssh://me:passw0rd@foo", - expectedError: "ssh helper does not accept plain-text password", + expectedError: "plain-text password is not supported", }, { url: "ssh://foo/bar", - expectedError: "extra path", + expectedError: `extra path after the host: "/bar"`, }, { url: "ssh://foo?bar", - expectedError: "extra query", + expectedError: `extra query after the host: "bar"`, }, { url: "ssh://foo#bar", - expectedError: "extra fragment", + expectedError: `extra fragment after the host: "bar"`, }, { url: "ssh://", - expectedError: "host is not specified", + expectedError: "no host specified", }, { url: "foo://bar", - expectedError: "expected scheme ssh", + expectedError: `expected scheme ssh, got "foo"`, }, } for _, tc := range testCases { From eb0ba4f8d54eb243565e8479f9d20f279b84d8cb Mon Sep 17 00:00:00 2001 From: Silvin Lubecki Date: Mon, 28 Jan 2019 14:30:31 +0100 Subject: [PATCH 16/60] Extract streams helpers from command package to their own package to remove a cyclic dependency from command to internal/containerizedengine Aliasing old types * streams.InStream -> streams.In * streams.NewInStream -> streams.NewIn * streams.OutStream -> streams.Out * streams.NewOutStream -> streams.NewOut Signed-off-by: Silvin Lubecki --- cli/command/context/export-import_test.go | 4 +- cli/command/image/build_test.go | 4 +- cli/command/image/trust.go | 3 +- cli/command/out.go | 50 ------------------- cli/command/registry.go | 3 +- cli/command/stack/kubernetes/deploy.go | 8 +-- cli/command/streams.go | 23 +++++++++ cli/command/swarm/unlock.go | 3 +- cli/command/swarm/unlock_test.go | 4 +- cli/command/utils.go | 3 +- cli/command/volume/prune_test.go | 6 +-- cli/{command => streams}/in.go | 26 +++++----- cli/streams/out.go | 50 +++++++++++++++++++ cli/{command => streams}/stream.go | 14 +++--- .../containerizedengine/containerd_test.go | 6 +-- internal/containerizedengine/update_test.go | 20 ++++---- internal/test/cli.go | 17 ++++--- 17 files changed, 136 insertions(+), 108 deletions(-) delete mode 100644 cli/command/out.go create mode 100644 cli/command/streams.go rename cli/{command => streams}/in.go (59%) create mode 100644 cli/streams/out.go rename cli/{command => streams}/stream.go (63%) diff --git a/cli/command/context/export-import_test.go b/cli/command/context/export-import_test.go index aac9beddeb1c..94d8e6aafe92 100644 --- a/cli/command/context/export-import_test.go +++ b/cli/command/context/export-import_test.go @@ -8,7 +8,7 @@ import ( "path/filepath" "testing" - "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/streams" "gotest.tools/assert" ) @@ -53,7 +53,7 @@ func TestExportImportPipe(t *testing.T) { dest: "-", })) assert.Equal(t, cli.ErrBuffer().String(), "") - cli.SetIn(command.NewInStream(ioutil.NopCloser(bytes.NewBuffer(cli.OutBuffer().Bytes())))) + cli.SetIn(streams.NewIn(ioutil.NopCloser(bytes.NewBuffer(cli.OutBuffer().Bytes())))) cli.OutBuffer().Reset() cli.ErrBuffer().Reset() assert.NilError(t, runImport(cli, "test2", "-")) diff --git a/cli/command/image/build_test.go b/cli/command/image/build_test.go index abd5a7aa8e70..402aa3ae1a8f 100644 --- a/cli/command/image/build_test.go +++ b/cli/command/image/build_test.go @@ -13,7 +13,7 @@ import ( "sort" "testing" - "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/streams" "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/archive" @@ -39,7 +39,7 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) { FROM alpine:3.6 COPY foo / `) - cli.SetIn(command.NewInStream(ioutil.NopCloser(dockerfile))) + cli.SetIn(streams.NewIn(ioutil.NopCloser(dockerfile))) dir := fs.NewDir(t, t.Name(), fs.WithFile("foo", "some content")) diff --git a/cli/command/image/trust.go b/cli/command/image/trust.go index 3278fa815c85..75f3ab1ccc83 100644 --- a/cli/command/image/trust.go +++ b/cli/command/image/trust.go @@ -10,6 +10,7 @@ import ( "sort" "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/streams" "github.com/docker/cli/cli/trust" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" @@ -296,7 +297,7 @@ func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth tru out := cli.Out() if opts.quiet { - out = command.NewOutStream(ioutil.Discard) + out = streams.NewOut(ioutil.Discard) } return jsonmessage.DisplayJSONMessagesToStream(responseBody, out, nil) } diff --git a/cli/command/out.go b/cli/command/out.go deleted file mode 100644 index 89cc5d3aa12c..000000000000 --- a/cli/command/out.go +++ /dev/null @@ -1,50 +0,0 @@ -package command - -import ( - "io" - "os" - - "github.com/docker/docker/pkg/term" - "github.com/sirupsen/logrus" -) - -// OutStream is an output stream used by the DockerCli to write normal program -// output. -type OutStream struct { - CommonStream - out io.Writer -} - -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 { - return 0, 0 - } - ws, err := term.GetWinsize(o.fd) - if err != nil { - logrus.Debugf("Error getting size: %s", err) - if ws == nil { - return 0, 0 - } - } - return uint(ws.Height), uint(ws.Width) -} - -// NewOutStream returns a new OutStream object from a Writer -func NewOutStream(out io.Writer) *OutStream { - fd, isTerminal := term.GetFdInfo(out) - return &OutStream{CommonStream: CommonStream{fd: fd, isTerminal: isTerminal}, out: out} -} diff --git a/cli/command/registry.go b/cli/command/registry.go index c12843693ea7..f0276680bd8f 100644 --- a/cli/command/registry.go +++ b/cli/command/registry.go @@ -12,6 +12,7 @@ import ( "strings" "github.com/docker/cli/cli/debug" + "github.com/docker/cli/cli/streams" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" @@ -101,7 +102,7 @@ func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, is func ConfigureAuth(cli Cli, flUser, flPassword string, authconfig *types.AuthConfig, isDefaultRegistry bool) error { // On Windows, force the use of the regular OS stdin stream. Fixes #14336/#14210 if runtime.GOOS == "windows" { - cli.SetIn(NewInStream(os.Stdin)) + cli.SetIn(streams.NewIn(os.Stdin)) } // Some links documenting this: diff --git a/cli/command/stack/kubernetes/deploy.go b/cli/command/stack/kubernetes/deploy.go index 638daddd2f08..84fdc638c2e6 100644 --- a/cli/command/stack/kubernetes/deploy.go +++ b/cli/command/stack/kubernetes/deploy.go @@ -4,9 +4,9 @@ import ( "fmt" "io" - "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/stack/options" composetypes "github.com/docker/cli/cli/compose/types" + "github.com/docker/cli/cli/streams" "github.com/morikuni/aec" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" ) @@ -117,7 +117,7 @@ func metaStateFromStatus(status serviceStatus) metaServiceState { } type forwardOnlyStatusDisplay struct { - o *command.OutStream + o *streams.Out states map[string]metaServiceState } @@ -130,7 +130,7 @@ func (d *forwardOnlyStatusDisplay) OnStatus(status serviceStatus) { } type interactiveStatusDisplay struct { - o *command.OutStream + o *streams.Out statuses []serviceStatus } @@ -163,7 +163,7 @@ func displayInteractiveServiceStatus(status serviceStatus, o io.Writer) { status.podsReady, status.podsPending, totalFailed, status.podsTotal) } -func newStatusDisplay(o *command.OutStream) statusDisplay { +func newStatusDisplay(o *streams.Out) statusDisplay { if !o.IsTerminal() { return &forwardOnlyStatusDisplay{o: o, states: map[string]metaServiceState{}} } diff --git a/cli/command/streams.go b/cli/command/streams.go new file mode 100644 index 000000000000..fa435e1643f2 --- /dev/null +++ b/cli/command/streams.go @@ -0,0 +1,23 @@ +package command + +import ( + "github.com/docker/cli/cli/streams" +) + +// InStream is an input stream used by the DockerCli to read user input +// Deprecated: Use github.com/docker/cli/cli/streams.In instead +type InStream = streams.In + +// OutStream is an output stream used by the DockerCli to write normal program +// output. +// Deprecated: Use github.com/docker/cli/cli/streams.Out instead +type OutStream = streams.Out + +var ( + // NewInStream returns a new InStream object from a ReadCloser + // Deprecated: Use github.com/docker/cli/cli/streams.NewIn instead + NewInStream = streams.NewIn + // NewOutStream returns a new OutStream object from a Writer + // Deprecated: Use github.com/docker/cli/cli/streams.NewOut instead + NewOutStream = streams.NewOut +) diff --git a/cli/command/swarm/unlock.go b/cli/command/swarm/unlock.go index 7d0dce68ae1a..35f995bd5b33 100644 --- a/cli/command/swarm/unlock.go +++ b/cli/command/swarm/unlock.go @@ -9,6 +9,7 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/streams" "github.com/docker/docker/api/types/swarm" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -59,7 +60,7 @@ func runUnlock(dockerCli command.Cli) error { return client.SwarmUnlock(ctx, req) } -func readKey(in *command.InStream, prompt string) (string, error) { +func readKey(in *streams.In, prompt string) (string, error) { if in.IsTerminal() { fmt.Print(prompt) dt, err := terminal.ReadPassword(int(in.FD())) diff --git a/cli/command/swarm/unlock_test.go b/cli/command/swarm/unlock_test.go index 8eb2ecd4f03d..f576dc34cfb4 100644 --- a/cli/command/swarm/unlock_test.go +++ b/cli/command/swarm/unlock_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/streams" "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" @@ -92,7 +92,7 @@ func TestSwarmUnlock(t *testing.T) { return nil }, }) - dockerCli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input)))) + dockerCli.SetIn(streams.NewIn(ioutil.NopCloser(strings.NewReader(input)))) cmd := newUnlockCommand(dockerCli) assert.NilError(t, cmd.Execute()) } diff --git a/cli/command/utils.go b/cli/command/utils.go index 13954c010145..26f53814cdf9 100644 --- a/cli/command/utils.go +++ b/cli/command/utils.go @@ -9,6 +9,7 @@ import ( "runtime" "strings" + "github.com/docker/cli/cli/streams" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/pkg/system" "github.com/spf13/pflag" @@ -80,7 +81,7 @@ func PromptForConfirmation(ins io.Reader, outs io.Writer, message string) bool { // On Windows, force the use of the regular OS stdin stream. if runtime.GOOS == "windows" { - ins = NewInStream(os.Stdin) + ins = streams.NewIn(os.Stdin) } reader := bufio.NewReader(ins) diff --git a/cli/command/volume/prune_test.go b/cli/command/volume/prune_test.go index 79d4d52e320e..800eaa730f43 100644 --- a/cli/command/volume/prune_test.go +++ b/cli/command/volume/prune_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/streams" "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" @@ -86,7 +86,7 @@ func TestVolumePrunePromptYes(t *testing.T) { volumePruneFunc: simplePruneFunc, }) - cli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input)))) + cli.SetIn(streams.NewIn(ioutil.NopCloser(strings.NewReader(input)))) cmd := NewPruneCommand(cli) assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "volume-prune-yes.golden") @@ -102,7 +102,7 @@ func TestVolumePrunePromptNo(t *testing.T) { volumePruneFunc: simplePruneFunc, }) - cli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input)))) + cli.SetIn(streams.NewIn(ioutil.NopCloser(strings.NewReader(input)))) cmd := NewPruneCommand(cli) assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "volume-prune-no.golden") diff --git a/cli/command/in.go b/cli/streams/in.go similarity index 59% rename from cli/command/in.go rename to cli/streams/in.go index 54855c6dc216..931c434e3730 100644 --- a/cli/command/in.go +++ b/cli/streams/in.go @@ -1,4 +1,4 @@ -package command +package streams import ( "errors" @@ -9,33 +9,33 @@ import ( "github.com/docker/docker/pkg/term" ) -// InStream is an input stream used by the DockerCli to read user input -type InStream struct { - CommonStream +// In is an input stream used by the DockerCli to read user input +type In struct { + commonStream in io.ReadCloser } -func (i *InStream) Read(p []byte) (int, error) { +func (i *In) Read(p []byte) (int, error) { return i.in.Read(p) } // Close implements the Closer interface -func (i *InStream) Close() error { +func (i *In) 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 { +func (i *In) SetRawTerminal() (err error) { + if os.Getenv("NORAW") != "" || !i.commonStream.isTerminal { return nil } - i.CommonStream.state, err = term.SetRawTerminal(i.CommonStream.fd) + 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 { +func (i *In) CheckTty(attachStdin, ttyMode bool) error { // In order to attach to a container tty, input stream for the client must // be a tty itself: redirecting or piping the client standard input is // incompatible with `docker run -t`, `docker exec -t` or `docker attach`. @@ -49,8 +49,8 @@ func (i *InStream) CheckTty(attachStdin, ttyMode bool) error { return nil } -// NewInStream returns a new InStream object from a ReadCloser -func NewInStream(in io.ReadCloser) *InStream { +// NewIn returns a new In object from a ReadCloser +func NewIn(in io.ReadCloser) *In { fd, isTerminal := term.GetFdInfo(in) - return &InStream{CommonStream: CommonStream{fd: fd, isTerminal: isTerminal}, in: in} + return &In{commonStream: commonStream{fd: fd, isTerminal: isTerminal}, in: in} } diff --git a/cli/streams/out.go b/cli/streams/out.go new file mode 100644 index 000000000000..036f49377140 --- /dev/null +++ b/cli/streams/out.go @@ -0,0 +1,50 @@ +package streams + +import ( + "io" + "os" + + "github.com/docker/docker/pkg/term" + "github.com/sirupsen/logrus" +) + +// Out is an output stream used by the DockerCli to write normal program +// output. +type Out struct { + commonStream + out io.Writer +} + +func (o *Out) Write(p []byte) (int, error) { + return o.out.Write(p) +} + +// SetRawTerminal sets raw mode on the input terminal +func (o *Out) 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 *Out) GetTtySize() (uint, uint) { + if !o.isTerminal { + return 0, 0 + } + ws, err := term.GetWinsize(o.fd) + if err != nil { + logrus.Debugf("Error getting size: %s", err) + if ws == nil { + return 0, 0 + } + } + return uint(ws.Height), uint(ws.Width) +} + +// NewOut returns a new Out object from a Writer +func NewOut(out io.Writer) *Out { + fd, isTerminal := term.GetFdInfo(out) + return &Out{commonStream: commonStream{fd: fd, isTerminal: isTerminal}, out: out} +} diff --git a/cli/command/stream.go b/cli/streams/stream.go similarity index 63% rename from cli/command/stream.go rename to cli/streams/stream.go index 71a43fa2e9c2..f97bc69fdd6d 100644 --- a/cli/command/stream.go +++ b/cli/streams/stream.go @@ -1,34 +1,34 @@ -package command +package streams import ( "github.com/docker/docker/pkg/term" ) -// CommonStream is an input stream used by the DockerCli to read user input -type CommonStream struct { +// 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 { +func (s *commonStream) FD() uintptr { return s.fd } // IsTerminal returns true if this stream is connected to a terminal -func (s *CommonStream) IsTerminal() bool { +func (s *commonStream) IsTerminal() bool { return s.isTerminal } // RestoreTerminal restores normal mode to the terminal -func (s *CommonStream) RestoreTerminal() { +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) { +func (s *commonStream) SetIsTerminal(isTerminal bool) { s.isTerminal = isTerminal } diff --git a/internal/containerizedengine/containerd_test.go b/internal/containerizedengine/containerd_test.go index cca86be55b4a..0a724a492453 100644 --- a/internal/containerizedengine/containerd_test.go +++ b/internal/containerizedengine/containerd_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/containerd/containerd" - "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/streams" "github.com/docker/docker/api/types" "gotest.tools/assert" ) @@ -24,7 +24,7 @@ func TestPullWithAuthPullFail(t *testing.T) { } imageName := "testnamegoeshere" - _, err := client.pullWithAuth(ctx, imageName, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}) + _, err := client.pullWithAuth(ctx, imageName, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{}) assert.ErrorContains(t, err, "pull failure") } @@ -40,6 +40,6 @@ func TestPullWithAuthPullPass(t *testing.T) { } imageName := "testnamegoeshere" - _, err := client.pullWithAuth(ctx, imageName, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}) + _, err := client.pullWithAuth(ctx, imageName, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{}) assert.NilError(t, err) } diff --git a/internal/containerizedengine/update_test.go b/internal/containerizedengine/update_test.go index 25ad7ab5a90a..24b51d9f5369 100644 --- a/internal/containerizedengine/update_test.go +++ b/internal/containerizedengine/update_test.go @@ -11,7 +11,7 @@ import ( "github.com/containerd/containerd" "github.com/containerd/containerd/cio" "github.com/containerd/containerd/errdefs" - "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/streams" "github.com/docker/cli/internal/versions" clitypes "github.com/docker/cli/types" "github.com/docker/docker/api/types" @@ -45,21 +45,21 @@ func TestActivateImagePermutations(t *testing.T) { RuntimeMetadataDir: tmpdir, } - err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}) + err = client.ActivateEngine(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{}) assert.ErrorContains(t, err, expectedError.Error()) assert.Equal(t, lookedup, fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, clitypes.EnterpriseEngineImage, opts.EngineVersion)) metadata = clitypes.RuntimeMetadata{EngineImage: clitypes.CommunityEngineImage} err = versions.WriteRuntimeMetadata(tmpdir, &metadata) assert.NilError(t, err) - err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}) + err = client.ActivateEngine(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{}) assert.ErrorContains(t, err, expectedError.Error()) assert.Equal(t, lookedup, fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, clitypes.EnterpriseEngineImage, opts.EngineVersion)) metadata = clitypes.RuntimeMetadata{EngineImage: clitypes.CommunityEngineImage + "-dm"} err = versions.WriteRuntimeMetadata(tmpdir, &metadata) assert.NilError(t, err) - err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}) + err = client.ActivateEngine(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{}) assert.ErrorContains(t, err, expectedError.Error()) assert.Equal(t, lookedup, fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, clitypes.EnterpriseEngineImage+"-dm", opts.EngineVersion)) } @@ -110,7 +110,7 @@ func TestActivateConfigFailure(t *testing.T) { RuntimeMetadataDir: tmpdir, } - err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}) + err = client.ActivateEngine(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{}) assert.ErrorContains(t, err, "config lookup failure") } @@ -152,7 +152,7 @@ func TestActivateDoUpdateFail(t *testing.T) { RuntimeMetadataDir: tmpdir, } - err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}) + err = client.ActivateEngine(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{}) assert.ErrorContains(t, err, "check for image") assert.ErrorContains(t, err, "something went wrong") } @@ -174,7 +174,7 @@ func TestDoUpdateNoVersion(t *testing.T) { } client := baseClient{} - err = client.DoUpdate(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}) + err = client.DoUpdate(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{}) assert.ErrorContains(t, err, "pick the version you") } @@ -202,7 +202,7 @@ func TestDoUpdateImageMiscError(t *testing.T) { }, } - err = client.DoUpdate(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}) + err = client.DoUpdate(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{}) assert.ErrorContains(t, err, "check for image") assert.ErrorContains(t, err, "something went wrong") } @@ -234,7 +234,7 @@ func TestDoUpdatePullFail(t *testing.T) { }, } - err = client.DoUpdate(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}) + err = client.DoUpdate(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{}) assert.ErrorContains(t, err, "unable to pull") assert.ErrorContains(t, err, "pull failure") } @@ -280,7 +280,7 @@ func TestActivateDoUpdateVerifyImageName(t *testing.T) { RuntimeMetadataDir: tmpdir, } - err = client.ActivateEngine(ctx, opts, command.NewOutStream(&bytes.Buffer{}), &types.AuthConfig{}) + err = client.ActivateEngine(ctx, opts, streams.NewOut(&bytes.Buffer{}), &types.AuthConfig{}) assert.ErrorContains(t, err, "check for image") assert.ErrorContains(t, err, "something went wrong") expectedImage := fmt.Sprintf("%s/%s:%s", opts.RegistryPrefix, opts.EngineImage, opts.EngineVersion) diff --git a/internal/test/cli.go b/internal/test/cli.go index 164488ca2e10..12b8b621d970 100644 --- a/internal/test/cli.go +++ b/internal/test/cli.go @@ -13,6 +13,7 @@ import ( "github.com/docker/cli/cli/context/store" manifeststore "github.com/docker/cli/cli/manifest/store" registryclient "github.com/docker/cli/cli/registry/client" + "github.com/docker/cli/cli/streams" "github.com/docker/cli/cli/trust" clitypes "github.com/docker/cli/types" "github.com/docker/docker/client" @@ -29,10 +30,10 @@ type FakeCli struct { command.DockerCli client client.APIClient configfile *configfile.ConfigFile - out *command.OutStream + out *streams.Out outBuffer *bytes.Buffer err *bytes.Buffer - in *command.InStream + in *streams.In server command.ServerInfo clientInfoFunc clientInfoFuncType notaryClientFunc NotaryClientFuncType @@ -51,10 +52,10 @@ func NewFakeCli(client client.APIClient, opts ...func(*FakeCli)) *FakeCli { errBuffer := new(bytes.Buffer) c := &FakeCli{ client: client, - out: command.NewOutStream(outBuffer), + out: streams.NewOut(outBuffer), outBuffer: outBuffer, err: errBuffer, - in: command.NewInStream(ioutil.NopCloser(strings.NewReader(""))), + in: streams.NewIn(ioutil.NopCloser(strings.NewReader(""))), // Use an empty string for filename so that tests don't create configfiles // Set cli.ConfigFile().Filename to a tempfile to support Save. configfile: configfile.New(""), @@ -66,7 +67,7 @@ func NewFakeCli(client client.APIClient, opts ...func(*FakeCli)) *FakeCli { } // SetIn sets the input of the cli to the specified ReadCloser -func (c *FakeCli) SetIn(in *command.InStream) { +func (c *FakeCli) SetIn(in *streams.In) { c.in = in } @@ -76,7 +77,7 @@ func (c *FakeCli) SetErr(err *bytes.Buffer) { } // SetOut sets the stdout stream for the cli to the specified io.Writer -func (c *FakeCli) SetOut(out *command.OutStream) { +func (c *FakeCli) SetOut(out *streams.Out) { c.out = out } @@ -106,7 +107,7 @@ func (c *FakeCli) Client() client.APIClient { } // Out returns the output stream (stdout) the cli should write on -func (c *FakeCli) Out() *command.OutStream { +func (c *FakeCli) Out() *streams.Out { return c.out } @@ -116,7 +117,7 @@ func (c *FakeCli) Err() io.Writer { } // In returns the input stream the cli will use -func (c *FakeCli) In() *command.InStream { +func (c *FakeCli) In() *streams.In { return c.in } From 7f207f3f957ed3f5129aeb22bef2a429c14caf22 Mon Sep 17 00:00:00 2001 From: Silvin Lubecki Date: Mon, 28 Jan 2019 14:52:58 +0100 Subject: [PATCH 17/60] Introduce functional arguments to NewDockerCli for a more stable API. Signed-off-by: Silvin Lubecki --- cli/command/cli.go | 63 +++++++++++++++++++++------ cli/command/cli_options.go | 89 ++++++++++++++++++++++++++++++++++++++ cli/command/cli_test.go | 45 +++++++++++++++++++ cmd/docker/docker.go | 27 ++++-------- cmd/docker/docker_test.go | 22 ++++++---- docs/yaml/generate.go | 7 +-- man/generate.go | 7 +-- 7 files changed, 214 insertions(+), 46 deletions(-) create mode 100644 cli/command/cli_options.go diff --git a/cli/command/cli.go b/cli/command/cli.go index 5da0f01ddcf3..33eeb10949ae 100644 --- a/cli/command/cli.go +++ b/cli/command/cli.go @@ -19,12 +19,15 @@ import ( cliflags "github.com/docker/cli/cli/flags" manifeststore "github.com/docker/cli/cli/manifest/store" registryclient "github.com/docker/cli/cli/registry/client" + "github.com/docker/cli/cli/streams" "github.com/docker/cli/cli/trust" + "github.com/docker/cli/internal/containerizedengine" dopts "github.com/docker/cli/opts" clitypes "github.com/docker/cli/types" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/client" + "github.com/docker/docker/pkg/term" "github.com/docker/go-connections/tlsconfig" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -35,18 +38,19 @@ import ( // Streams is an interface which exposes the standard input and output streams type Streams interface { - In() *InStream - Out() *OutStream + In() *streams.In + Out() *streams.Out Err() io.Writer } // Cli represents the docker command line client. type Cli interface { Client() client.APIClient - Out() *OutStream + Out() *streams.Out Err() io.Writer - In() *InStream - SetIn(in *InStream) + In() *streams.In + SetIn(in *streams.In) + Apply(ops ...DockerCliOption) error ConfigFile() *configfile.ConfigFile ServerInfo() ServerInfo ClientInfo() ClientInfo @@ -66,8 +70,8 @@ type Cli interface { // Instances of the client can be returned from NewDockerCli. type DockerCli struct { configFile *configfile.ConfigFile - in *InStream - out *OutStream + in *streams.In + out *streams.Out err io.Writer client client.APIClient serverInfo ServerInfo @@ -96,7 +100,7 @@ func (cli *DockerCli) Client() client.APIClient { } // Out returns the writer used for stdout -func (cli *DockerCli) Out() *OutStream { +func (cli *DockerCli) Out() *streams.Out { return cli.out } @@ -106,12 +110,12 @@ func (cli *DockerCli) Err() io.Writer { } // SetIn sets the reader used for stdin -func (cli *DockerCli) SetIn(in *InStream) { +func (cli *DockerCli) SetIn(in *streams.In) { cli.in = in } // In returns the reader used for stdin -func (cli *DockerCli) In() *InStream { +func (cli *DockerCli) In() *streams.In { return cli.in } @@ -393,6 +397,16 @@ func (cli *DockerCli) DockerEndpoint() docker.Endpoint { return cli.dockerEndpoint } +// Apply all the operation on the cli +func (cli *DockerCli) Apply(ops ...DockerCliOption) error { + for _, op := range ops { + if err := op(cli); err != nil { + return err + } + } + return nil +} + // ServerInfo stores details about the supported features and platform of the // server type ServerInfo struct { @@ -407,9 +421,32 @@ type ClientInfo struct { DefaultVersion string } -// NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err. -func NewDockerCli(in io.ReadCloser, out, err io.Writer, isTrusted bool, containerizedFn func(string) (clitypes.ContainerizedClient, error)) *DockerCli { - return &DockerCli{in: NewInStream(in), out: NewOutStream(out), err: err, contentTrust: isTrusted, newContainerizeClient: containerizedFn} +// NewDockerCli returns a DockerCli instance with all operators applied on it. +// It applies by default the standard streams, the content trust from +// environment and the default containerized client constructor operations. +func NewDockerCli(ops ...DockerCliOption) (*DockerCli, error) { + cli := &DockerCli{} + defaultOps := []DockerCliOption{ + WithContentTrustFromEnv(), + WithContainerizedClient(containerizedengine.NewClient), + } + ops = append(defaultOps, ops...) + if err := cli.Apply(ops...); err != nil { + return nil, err + } + if cli.out == nil || cli.in == nil || cli.err == nil { + stdin, stdout, stderr := term.StdStreams() + if cli.in == nil { + cli.in = streams.NewIn(stdin) + } + if cli.out == nil { + cli.out = streams.NewOut(stdout) + } + if cli.err == nil { + cli.err = stderr + } + } + return cli, nil } func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (string, error) { diff --git a/cli/command/cli_options.go b/cli/command/cli_options.go new file mode 100644 index 000000000000..43db626211b7 --- /dev/null +++ b/cli/command/cli_options.go @@ -0,0 +1,89 @@ +package command + +import ( + "io" + "os" + "strconv" + + "github.com/docker/cli/cli/streams" + clitypes "github.com/docker/cli/types" + "github.com/docker/docker/pkg/term" +) + +// DockerCliOption applies a modification on a DockerCli. +type DockerCliOption func(cli *DockerCli) error + +// WithStandardStreams sets a cli in, out and err streams with the standard streams. +func WithStandardStreams() DockerCliOption { + return func(cli *DockerCli) error { + // Set terminal emulation based on platform as required. + stdin, stdout, stderr := term.StdStreams() + cli.in = streams.NewIn(stdin) + cli.out = streams.NewOut(stdout) + cli.err = stderr + return nil + } +} + +// WithCombinedStreams uses the same stream for the output and error streams. +func WithCombinedStreams(combined io.Writer) DockerCliOption { + return func(cli *DockerCli) error { + cli.out = streams.NewOut(combined) + cli.err = combined + return nil + } +} + +// WithInputStream sets a cli input stream. +func WithInputStream(in io.ReadCloser) DockerCliOption { + return func(cli *DockerCli) error { + cli.in = streams.NewIn(in) + return nil + } +} + +// WithOutputStream sets a cli output stream. +func WithOutputStream(out io.Writer) DockerCliOption { + return func(cli *DockerCli) error { + cli.out = streams.NewOut(out) + return nil + } +} + +// WithErrorStream sets a cli error stream. +func WithErrorStream(err io.Writer) DockerCliOption { + return func(cli *DockerCli) error { + cli.err = err + return nil + } +} + +// WithContentTrustFromEnv enables content trust on a cli from environment variable DOCKER_CONTENT_TRUST value. +func WithContentTrustFromEnv() DockerCliOption { + return func(cli *DockerCli) error { + cli.contentTrust = false + if e := os.Getenv("DOCKER_CONTENT_TRUST"); e != "" { + if t, err := strconv.ParseBool(e); t || err != nil { + // treat any other value as true + cli.contentTrust = true + } + } + return nil + } +} + +// WithContentTrust enables content trust on a cli. +func WithContentTrust(enabled bool) DockerCliOption { + return func(cli *DockerCli) error { + cli.contentTrust = enabled + return nil + } +} + +// WithContainerizedClient sets the containerized client constructor on a cli. +func WithContainerizedClient(containerizedFn func(string) (clitypes.ContainerizedClient, error)) DockerCliOption { + return func(cli *DockerCli) error { + cli.newContainerizeClient = containerizedFn + return nil + } +} diff --git a/cli/command/cli_test.go b/cli/command/cli_test.go index 71029e9107f3..e83941ffc6ca 100644 --- a/cli/command/cli_test.go +++ b/cli/command/cli_test.go @@ -1,8 +1,11 @@ package command import ( + "bytes" "context" "crypto/x509" + "fmt" + "io/ioutil" "os" "runtime" "testing" @@ -10,6 +13,7 @@ import ( cliconfig "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/flags" + clitypes "github.com/docker/cli/types" "github.com/docker/docker/api" "github.com/docker/docker/api/types" "github.com/docker/docker/client" @@ -247,3 +251,44 @@ func TestGetClientWithPassword(t *testing.T) { }) } } + +func TestNewDockerCliAndOperators(t *testing.T) { + // Test default operations and also overriding default ones + cli, err := NewDockerCli( + WithContentTrust(true), + WithContainerizedClient(func(string) (clitypes.ContainerizedClient, error) { return nil, nil }), + ) + assert.NilError(t, err) + // Check streams are initialized + assert.Check(t, cli.In() != nil) + assert.Check(t, cli.Out() != nil) + assert.Check(t, cli.Err() != nil) + assert.Equal(t, cli.ContentTrustEnabled(), true) + client, err := cli.NewContainerizedEngineClient("") + assert.NilError(t, err) + assert.Equal(t, client, nil) + + // Apply can modify a dockerCli after construction + inbuf := bytes.NewBuffer([]byte("input")) + outbuf := bytes.NewBuffer(nil) + errbuf := bytes.NewBuffer(nil) + cli.Apply( + WithInputStream(ioutil.NopCloser(inbuf)), + WithOutputStream(outbuf), + WithErrorStream(errbuf), + ) + // Check input stream + inputStream, err := ioutil.ReadAll(cli.In()) + assert.NilError(t, err) + assert.Equal(t, string(inputStream), "input") + // Check output stream + fmt.Fprintf(cli.Out(), "output") + outputStream, err := ioutil.ReadAll(outbuf) + assert.NilError(t, err) + assert.Equal(t, string(outputStream), "output") + // Check error stream + fmt.Fprintf(cli.Err(), "error") + errStream, err := ioutil.ReadAll(errbuf) + assert.NilError(t, err) + assert.Equal(t, string(errStream), "error") +} diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index ab31ad499ef2..5909953be3db 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "os" - "strconv" "strings" "github.com/docker/cli/cli" @@ -13,10 +12,8 @@ import ( cliconfig "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/debug" cliflags "github.com/docker/cli/cli/flags" - "github.com/docker/cli/internal/containerizedengine" "github.com/docker/docker/api/types/versions" "github.com/docker/docker/client" - "github.com/docker/docker/pkg/term" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -170,17 +167,19 @@ func noArgs(cmd *cobra.Command, args []string) error { } func main() { - // Set terminal emulation based on platform as required. - stdin, stdout, stderr := term.StdStreams() - logrus.SetOutput(stderr) + dockerCli, err := command.NewDockerCli() + if err != nil { + fmt.Fprintln(dockerCli.Err(), err) + os.Exit(1) + } + logrus.SetOutput(dockerCli.Err()) - dockerCli := command.NewDockerCli(stdin, stdout, stderr, contentTrustEnabled(), containerizedengine.NewClient) cmd := newDockerCommand(dockerCli) if err := cmd.Execute(); err != nil { if sterr, ok := err.(cli.StatusError); ok { if sterr.Status != "" { - fmt.Fprintln(stderr, sterr.Status) + fmt.Fprintln(dockerCli.Err(), sterr.Status) } // StatusError should only be used for errors, and all errors should // have a non-zero exit status, so never exit with 0 @@ -189,21 +188,11 @@ func main() { } os.Exit(sterr.StatusCode) } - fmt.Fprintln(stderr, err) + fmt.Fprintln(dockerCli.Err(), err) os.Exit(1) } } -func contentTrustEnabled() bool { - if e := os.Getenv("DOCKER_CONTENT_TRUST"); e != "" { - if t, err := strconv.ParseBool(e); t || err != nil { - // treat any other value as true - return true - } - } - return false -} - func dockerPreRun(opts *cliflags.ClientOptions) { cliflags.SetLogLevel(opts.Common.LogLevel) diff --git a/cmd/docker/docker_test.go b/cmd/docker/docker_test.go index 7c6e4526f433..e0f23747c0d5 100644 --- a/cmd/docker/docker_test.go +++ b/cmd/docker/docker_test.go @@ -25,27 +25,33 @@ func TestClientDebugEnabled(t *testing.T) { assert.Check(t, is.Equal(logrus.DebugLevel, logrus.GetLevel())) } +var discard = ioutil.NopCloser(bytes.NewBuffer(nil)) + func TestExitStatusForInvalidSubcommandWithHelpFlag(t *testing.T) { - discard := ioutil.Discard - cmd := newDockerCommand(command.NewDockerCli(os.Stdin, discard, discard, false, nil)) + cli, err := command.NewDockerCli(command.WithInputStream(discard), command.WithCombinedStreams(ioutil.Discard)) + assert.NilError(t, err) + cmd := newDockerCommand(cli) cmd.SetArgs([]string{"help", "invalid"}) - err := cmd.Execute() + err = cmd.Execute() assert.Error(t, err, "unknown help topic: invalid") } func TestExitStatusForInvalidSubcommand(t *testing.T) { - discard := ioutil.Discard - cmd := newDockerCommand(command.NewDockerCli(os.Stdin, discard, discard, false, nil)) + cli, err := command.NewDockerCli(command.WithInputStream(discard), command.WithCombinedStreams(ioutil.Discard)) + assert.NilError(t, err) + cmd := newDockerCommand(cli) cmd.SetArgs([]string{"invalid"}) - err := cmd.Execute() + err = cmd.Execute() assert.Check(t, is.ErrorContains(err, "docker: 'invalid' is not a docker command.")) } func TestVersion(t *testing.T) { var b bytes.Buffer - cmd := newDockerCommand(command.NewDockerCli(os.Stdin, &b, &b, false, nil)) + cli, err := command.NewDockerCli(command.WithInputStream(discard), command.WithCombinedStreams(&b)) + assert.NilError(t, err) + cmd := newDockerCommand(cli) cmd.SetArgs([]string{"--version"}) - err := cmd.Execute() + err = cmd.Execute() assert.NilError(t, err) assert.Check(t, is.Contains(b.String(), "Docker version")) } diff --git a/docs/yaml/generate.go b/docs/yaml/generate.go index 09550b2fad98..fdc5a2522ff3 100644 --- a/docs/yaml/generate.go +++ b/docs/yaml/generate.go @@ -10,7 +10,6 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/commands" - "github.com/docker/docker/pkg/term" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -18,8 +17,10 @@ import ( const descriptionSourcePath = "docs/reference/commandline/" func generateCliYaml(opts *options) error { - stdin, stdout, stderr := term.StdStreams() - dockerCli := command.NewDockerCli(stdin, stdout, stderr, false, nil) + dockerCli, err := command.NewDockerCli() + if err != nil { + return err + } cmd := &cobra.Command{Use: "docker"} commands.AddCommands(cmd, dockerCli) disableFlagsInUseLine(cmd) diff --git a/man/generate.go b/man/generate.go index e5e480be3f32..63c4219d798a 100644 --- a/man/generate.go +++ b/man/generate.go @@ -11,7 +11,6 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/commands" - "github.com/docker/docker/pkg/term" "github.com/spf13/cobra" "github.com/spf13/cobra/doc" "github.com/spf13/pflag" @@ -37,8 +36,10 @@ func generateManPages(opts *options) error { header.Date = &now } - stdin, stdout, stderr := term.StdStreams() - dockerCli := command.NewDockerCli(stdin, stdout, stderr, false, nil) + dockerCli, err := command.NewDockerCli() + if err != nil { + return err + } cmd := &cobra.Command{Use: "docker"} commands.AddCommands(cmd, dockerCli) source := filepath.Join(opts.source, descriptionSourcePath) From e9b7db5b4c1a3bf86909ef83e978d3f710549f03 Mon Sep 17 00:00:00 2001 From: Simon Ferquel Date: Mon, 28 Jan 2019 16:54:09 +0100 Subject: [PATCH 18/60] Vendoring update for pull secrets and policies Signed-off-by: Simon Ferquel --- vendor.conf | 2 +- .../docker/compose-on-kubernetes/api/compose/v1alpha3/stack.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/vendor.conf b/vendor.conf index d026a893f976..88d21f77836d 100755 --- a/vendor.conf +++ b/vendor.conf @@ -14,7 +14,7 @@ github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 # v1.1.0 github.com/dgrijalva/jwt-go a2c85815a77d0f951e33ba4db5ae93629a1530af github.com/docker/distribution 83389a148052d74ac602f5f1d62f86ff2f3c4aa5 github.com/docker/docker f76d6a078d881f410c00e8d900dcdfc2e026c841 -github.com/docker/compose-on-kubernetes 1559927c6b456d56cc9c9b05438252ebb646640b # master w/ v1alpha3 +github.com/docker/compose-on-kubernetes 356b2919c496f7e988f6e0dfe7e67d919602e14e # master w/ v1alpha3+pullsecrets+pull-policy github.com/docker/docker-credential-helpers 5241b46610f2491efdf9d1c85f1ddf5b02f6d962 # the docker/go package contains a customized version of canonical/json # and is used by Notary. The package is periodically rebased on current Go versions. diff --git a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/stack.go b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/stack.go index 67057ad6df45..665117c25404 100644 --- a/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/stack.go +++ b/vendor/github.com/docker/compose-on-kubernetes/api/compose/v1alpha3/stack.go @@ -97,6 +97,8 @@ type ServiceConfig struct { User *int64 `json:"user,omitempty"` Volumes []ServiceVolumeConfig `json:"volumes,omitempty"` WorkingDir string `json:"working_dir,omitempty"` + PullSecret string `json:"pull_secret,omitempty"` + PullPolicy string `json:"pull_policy,omitempty"` } // ServicePortConfig is the port configuration for a service From 3126920af14ea5127e00a2f8f9d8e07a6c3f6ff9 Mon Sep 17 00:00:00 2001 From: Simon Ferquel Date: Mon, 21 Jan 2019 09:37:20 +0100 Subject: [PATCH 19/60] Add context store config options and expose context commands This will allow plugins to have custom typed endpoints, as well as create/remove/update contexts with the exact same results as the main CLI (thinking of things like `docker ee login https://my-ucp-server --context ucp-prod)` Signed-off-by: Simon Ferquel --- cli/command/cli.go | 22 ++++--- cli/command/cli_options.go | 17 ++++++ cli/command/context/create.go | 52 ++++++++-------- cli/command/context/create_test.go | 72 +++++++++++------------ cli/command/context/export-import_test.go | 34 +++++------ cli/command/context/export.go | 46 ++++++++------- cli/command/context/import.go | 5 +- cli/command/context/list_test.go | 12 ++-- cli/command/context/remove.go | 16 ++--- cli/command/context/remove_test.go | 8 +-- cli/command/context/update.go | 56 +++++++++--------- cli/command/context/update_test.go | 52 ++++++++-------- cli/command/context/use.go | 42 +++++++------ cli/command/context/use_test.go | 6 +- cli/context/store/storeconfig.go | 5 ++ cli/context/store/storeconfig_test.go | 31 ++++++++++ 16 files changed, 273 insertions(+), 203 deletions(-) create mode 100644 cli/context/store/storeconfig_test.go diff --git a/cli/command/cli.go b/cli/command/cli.go index 33eeb10949ae..fc557635c483 100644 --- a/cli/command/cli.go +++ b/cli/command/cli.go @@ -81,14 +81,9 @@ type DockerCli struct { contextStore store.Store currentContext string dockerEndpoint docker.Endpoint + contextStoreConfig store.Config } -var storeConfig = store.NewConfig( - func() interface{} { return &DockerContext{} }, - store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }), - store.EndpointTypeGetter(kubcontext.KubernetesEndpoint, func() interface{} { return &kubcontext.EndpointMeta{} }), -) - // DefaultVersion returns api.defaultVersion or DOCKER_API_VERSION if specified. func (cli *DockerCli) DefaultVersion() string { return cli.clientInfo.DefaultVersion @@ -184,7 +179,7 @@ func (cli *DockerCli) RegistryClient(allowInsecure bool) registryclient.Registry func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error { cli.configFile = cliconfig.LoadDefaultConfigFile(cli.err) var err error - cli.contextStore = store.New(cliconfig.ContextStoreDir(), storeConfig) + cli.contextStore = store.New(cliconfig.ContextStoreDir(), cli.contextStoreConfig) cli.currentContext, err = resolveContextName(opts.Common, cli.configFile, cli.contextStore) if err != nil { return err @@ -226,7 +221,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error { // NewAPIClientFromFlags creates a new APIClient from command line flags func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile.ConfigFile) (client.APIClient, error) { - store := store.New(cliconfig.ContextStoreDir(), storeConfig) + store := store.New(cliconfig.ContextStoreDir(), defaultContextStoreConfig()) contextName, err := resolveContextName(opts, configFile, store) if err != nil { return nil, err @@ -372,7 +367,7 @@ func (cli *DockerCli) StackOrchestrator(flagValue string) (Orchestrator, error) if currentContext != "" { contextstore := cli.contextStore if contextstore == nil { - contextstore = store.New(cliconfig.ContextStoreDir(), storeConfig) + contextstore = store.New(cliconfig.ContextStoreDir(), cli.contextStoreConfig) } ctxRaw, err := contextstore.GetContextMetadata(currentContext) if store.IsErrContextDoesNotExist(err) { @@ -430,6 +425,7 @@ func NewDockerCli(ops ...DockerCliOption) (*DockerCli, error) { WithContentTrustFromEnv(), WithContainerizedClient(containerizedengine.NewClient), } + cli.contextStoreConfig = defaultContextStoreConfig() ops = append(defaultOps, ops...) if err := cli.Apply(ops...); err != nil { return nil, err @@ -501,3 +497,11 @@ func resolveContextName(opts *cliflags.CommonOptions, config *configfile.ConfigF } return "", nil } + +func defaultContextStoreConfig() store.Config { + return store.NewConfig( + func() interface{} { return &DockerContext{} }, + store.EndpointTypeGetter(docker.DockerEndpoint, func() interface{} { return &docker.EndpointMeta{} }), + store.EndpointTypeGetter(kubcontext.KubernetesEndpoint, func() interface{} { return &kubcontext.EndpointMeta{} }), + ) +} diff --git a/cli/command/cli_options.go b/cli/command/cli_options.go index 43db626211b7..4f48ca4ef250 100644 --- a/cli/command/cli_options.go +++ b/cli/command/cli_options.go @@ -1,10 +1,14 @@ package command import ( + "fmt" "io" "os" "strconv" + "github.com/docker/cli/cli/context/docker" + "github.com/docker/cli/cli/context/kubernetes" + "github.com/docker/cli/cli/context/store" "github.com/docker/cli/cli/streams" clitypes "github.com/docker/cli/types" "github.com/docker/docker/pkg/term" @@ -87,3 +91,16 @@ func WithContainerizedClient(containerizedFn func(string) (clitypes.Containerize return nil } } + +// WithContextEndpointType add support for an additional typed endpoint in the context store +// Plugins should use this to store additional endpoints configuration in the context store +func WithContextEndpointType(endpointName string, endpointType store.TypeGetter) DockerCliOption { + return func(cli *DockerCli) error { + switch endpointName { + case docker.DockerEndpoint, kubernetes.KubernetesEndpoint: + return fmt.Errorf("cannot change %q endpoint type", endpointName) + } + cli.contextStoreConfig.SetEndpoint(endpointName, endpointType) + return nil + } +} diff --git a/cli/command/context/create.go b/cli/command/context/create.go index c51d5214b503..ce53fcffdf3f 100644 --- a/cli/command/context/create.go +++ b/cli/command/context/create.go @@ -14,12 +14,13 @@ import ( "github.com/spf13/cobra" ) -type createOptions struct { - name string - description string - defaultStackOrchestrator string - docker map[string]string - kubernetes map[string]string +// CreateOptions are the options used for creating a context +type CreateOptions struct { + Name string + Description string + DefaultStackOrchestrator string + Docker map[string]string + Kubernetes map[string]string } func longCreateDescription() string { @@ -43,52 +44,53 @@ func longCreateDescription() string { } func newCreateCommand(dockerCli command.Cli) *cobra.Command { - opts := &createOptions{} + opts := &CreateOptions{} cmd := &cobra.Command{ Use: "create [OPTIONS] CONTEXT", Short: "Create a context", Args: cli.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - opts.name = args[0] - return runCreate(dockerCli, opts) + opts.Name = args[0] + return RunCreate(dockerCli, opts) }, Long: longCreateDescription(), } flags := cmd.Flags() - flags.StringVar(&opts.description, "description", "", "Description of the context") + flags.StringVar(&opts.Description, "description", "", "Description of the context") flags.StringVar( - &opts.defaultStackOrchestrator, + &opts.DefaultStackOrchestrator, "default-stack-orchestrator", "", "Default orchestrator for stack operations to use with this context (swarm|kubernetes|all)") - flags.StringToStringVar(&opts.docker, "docker", nil, "set the docker endpoint") - flags.StringToStringVar(&opts.kubernetes, "kubernetes", nil, "set the kubernetes endpoint") + flags.StringToStringVar(&opts.Docker, "docker", nil, "set the docker endpoint") + flags.StringToStringVar(&opts.Kubernetes, "kubernetes", nil, "set the kubernetes endpoint") return cmd } -func runCreate(cli command.Cli, o *createOptions) error { +// RunCreate creates a Docker context +func RunCreate(cli command.Cli, o *CreateOptions) error { s := cli.ContextStore() - if err := checkContextNameForCreation(s, o.name); err != nil { + if err := checkContextNameForCreation(s, o.Name); err != nil { return err } - stackOrchestrator, err := command.NormalizeOrchestrator(o.defaultStackOrchestrator) + stackOrchestrator, err := command.NormalizeOrchestrator(o.DefaultStackOrchestrator) if err != nil { return errors.Wrap(err, "unable to parse default-stack-orchestrator") } contextMetadata := store.ContextMetadata{ Endpoints: make(map[string]interface{}), Metadata: command.DockerContext{ - Description: o.description, + Description: o.Description, StackOrchestrator: stackOrchestrator, }, - Name: o.name, + Name: o.Name, } - if o.docker == nil { + if o.Docker == nil { return errors.New("docker endpoint configuration is required") } contextTLSData := store.ContextTLSData{ Endpoints: make(map[string]store.EndpointTLSData), } - dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(cli, o.docker) + dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(cli, o.Docker) if err != nil { return errors.Wrap(err, "unable to create docker endpoint config") } @@ -96,8 +98,8 @@ func runCreate(cli command.Cli, o *createOptions) error { if dockerTLS != nil { contextTLSData.Endpoints[docker.DockerEndpoint] = *dockerTLS } - if o.kubernetes != nil { - kubernetesEP, kubernetesTLS, err := getKubernetesEndpointMetadataAndTLS(cli, o.kubernetes) + if o.Kubernetes != nil { + kubernetesEP, kubernetesTLS, err := getKubernetesEndpointMetadataAndTLS(cli, o.Kubernetes) if err != nil { return errors.Wrap(err, "unable to create kubernetes endpoint config") } @@ -117,11 +119,11 @@ func runCreate(cli command.Cli, o *createOptions) error { if err := s.CreateOrUpdateContext(contextMetadata); err != nil { return err } - if err := s.ResetContextTLSMaterial(o.name, &contextTLSData); err != nil { + if err := s.ResetContextTLSMaterial(o.Name, &contextTLSData); err != nil { return err } - fmt.Fprintln(cli.Out(), o.name) - fmt.Fprintf(cli.Err(), "Successfully created context %q\n", o.name) + fmt.Fprintln(cli.Out(), o.Name) + fmt.Fprintf(cli.Err(), "Successfully created context %q\n", o.Name) return nil } diff --git a/cli/command/context/create_test.go b/cli/command/context/create_test.go index 52521f9f7e96..161d50b71dc9 100644 --- a/cli/command/context/create_test.go +++ b/cli/command/context/create_test.go @@ -46,68 +46,68 @@ func TestCreateInvalids(t *testing.T) { defer cleanup() assert.NilError(t, cli.ContextStore().CreateOrUpdateContext(store.ContextMetadata{Name: "existing-context"})) tests := []struct { - options createOptions + options CreateOptions expecterErr string }{ { expecterErr: `context name cannot be empty`, }, { - options: createOptions{ - name: " ", + options: CreateOptions{ + Name: " ", }, expecterErr: `context name " " is invalid`, }, { - options: createOptions{ - name: "existing-context", + options: CreateOptions{ + Name: "existing-context", }, expecterErr: `context "existing-context" already exists`, }, { - options: createOptions{ - name: "invalid-docker-host", - docker: map[string]string{ + options: CreateOptions{ + Name: "invalid-docker-host", + Docker: map[string]string{ keyHost: "some///invalid/host", }, }, expecterErr: `unable to parse docker host`, }, { - options: createOptions{ - name: "invalid-orchestrator", - defaultStackOrchestrator: "invalid", + options: CreateOptions{ + Name: "invalid-orchestrator", + DefaultStackOrchestrator: "invalid", }, expecterErr: `specified orchestrator "invalid" is invalid, please use either kubernetes, swarm or all`, }, { - options: createOptions{ - name: "orchestrator-swarm-no-endpoint", - defaultStackOrchestrator: "swarm", + options: CreateOptions{ + Name: "orchestrator-swarm-no-endpoint", + DefaultStackOrchestrator: "swarm", }, expecterErr: `docker endpoint configuration is required`, }, { - options: createOptions{ - name: "orchestrator-kubernetes-no-endpoint", - defaultStackOrchestrator: "kubernetes", - docker: map[string]string{}, + options: CreateOptions{ + Name: "orchestrator-kubernetes-no-endpoint", + DefaultStackOrchestrator: "kubernetes", + Docker: map[string]string{}, }, expecterErr: `cannot specify orchestrator "kubernetes" without configuring a Kubernetes endpoint`, }, { - options: createOptions{ - name: "orchestrator-all-no-endpoint", - defaultStackOrchestrator: "all", - docker: map[string]string{}, + options: CreateOptions{ + Name: "orchestrator-all-no-endpoint", + DefaultStackOrchestrator: "all", + Docker: map[string]string{}, }, expecterErr: `cannot specify orchestrator "all" without configuring a Kubernetes endpoint`, }, } for _, tc := range tests { tc := tc - t.Run(tc.options.name, func(t *testing.T) { - err := runCreate(cli, &tc.options) + t.Run(tc.options.Name, func(t *testing.T) { + err := RunCreate(cli, &tc.options) assert.ErrorContains(t, err, tc.expecterErr) }) } @@ -117,10 +117,10 @@ func TestCreateOrchestratorSwarm(t *testing.T) { cli, cleanup := makeFakeCli(t) defer cleanup() - err := runCreate(cli, &createOptions{ - name: "test", - defaultStackOrchestrator: "swarm", - docker: map[string]string{}, + err := RunCreate(cli, &CreateOptions{ + Name: "test", + DefaultStackOrchestrator: "swarm", + Docker: map[string]string{}, }) assert.NilError(t, err) assert.Equal(t, "test\n", cli.OutBuffer().String()) @@ -131,9 +131,9 @@ func TestCreateOrchestratorEmpty(t *testing.T) { cli, cleanup := makeFakeCli(t) defer cleanup() - err := runCreate(cli, &createOptions{ - name: "test", - docker: map[string]string{}, + err := RunCreate(cli, &CreateOptions{ + Name: "test", + Docker: map[string]string{}, }) assert.NilError(t, err) } @@ -156,13 +156,13 @@ func createTestContextWithKube(t *testing.T, cli command.Cli) { revert := env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig") defer revert() - err := runCreate(cli, &createOptions{ - name: "test", - defaultStackOrchestrator: "all", - kubernetes: map[string]string{ + err := RunCreate(cli, &CreateOptions{ + Name: "test", + DefaultStackOrchestrator: "all", + Kubernetes: map[string]string{ keyFromCurrent: "true", }, - docker: map[string]string{}, + Docker: map[string]string{}, }) assert.NilError(t, err) } diff --git a/cli/command/context/export-import_test.go b/cli/command/context/export-import_test.go index 94d8e6aafe92..9a7a41a2b824 100644 --- a/cli/command/context/export-import_test.go +++ b/cli/command/context/export-import_test.go @@ -21,14 +21,14 @@ func TestExportImportWithFile(t *testing.T) { defer cleanup() createTestContextWithKube(t, cli) cli.ErrBuffer().Reset() - assert.NilError(t, runExport(cli, &exportOptions{ - contextName: "test", - dest: contextFile, + assert.NilError(t, RunExport(cli, &ExportOptions{ + ContextName: "test", + Dest: contextFile, })) assert.Equal(t, cli.ErrBuffer().String(), fmt.Sprintf("Written file %q\n", contextFile)) cli.OutBuffer().Reset() cli.ErrBuffer().Reset() - assert.NilError(t, runImport(cli, "test2", contextFile)) + assert.NilError(t, RunImport(cli, "test2", contextFile)) context1, err := cli.ContextStore().GetContextMetadata("test") assert.NilError(t, err) context2, err := cli.ContextStore().GetContextMetadata("test2") @@ -48,15 +48,15 @@ func TestExportImportPipe(t *testing.T) { createTestContextWithKube(t, cli) cli.ErrBuffer().Reset() cli.OutBuffer().Reset() - assert.NilError(t, runExport(cli, &exportOptions{ - contextName: "test", - dest: "-", + assert.NilError(t, RunExport(cli, &ExportOptions{ + ContextName: "test", + Dest: "-", })) assert.Equal(t, cli.ErrBuffer().String(), "") cli.SetIn(streams.NewIn(ioutil.NopCloser(bytes.NewBuffer(cli.OutBuffer().Bytes())))) cli.OutBuffer().Reset() cli.ErrBuffer().Reset() - assert.NilError(t, runImport(cli, "test2", "-")) + assert.NilError(t, RunImport(cli, "test2", "-")) context1, err := cli.ContextStore().GetContextMetadata("test") assert.NilError(t, err) context2, err := cli.ContextStore().GetContextMetadata("test2") @@ -79,18 +79,18 @@ func TestExportKubeconfig(t *testing.T) { defer cleanup() createTestContextWithKube(t, cli) cli.ErrBuffer().Reset() - assert.NilError(t, runExport(cli, &exportOptions{ - contextName: "test", - dest: contextFile, - kubeconfig: true, + assert.NilError(t, RunExport(cli, &ExportOptions{ + ContextName: "test", + Dest: contextFile, + Kubeconfig: true, })) assert.Equal(t, cli.ErrBuffer().String(), fmt.Sprintf("Written file %q\n", contextFile)) - assert.NilError(t, runCreate(cli, &createOptions{ - name: "test2", - kubernetes: map[string]string{ + assert.NilError(t, RunCreate(cli, &CreateOptions{ + Name: "test2", + Kubernetes: map[string]string{ keyKubeconfig: contextFile, }, - docker: map[string]string{}, + Docker: map[string]string{}, })) validateTestKubeEndpoint(t, cli.ContextStore(), "test2") } @@ -105,6 +105,6 @@ func TestExportExistingFile(t *testing.T) { createTestContextWithKube(t, cli) cli.ErrBuffer().Reset() assert.NilError(t, ioutil.WriteFile(contextFile, []byte{}, 0644)) - err = runExport(cli, &exportOptions{contextName: "test", dest: contextFile}) + err = RunExport(cli, &ExportOptions{ContextName: "test", Dest: contextFile}) assert.Assert(t, os.IsExist(err)) } diff --git a/cli/command/context/export.go b/cli/command/context/export.go index 060abf977dcd..870efcc3a407 100644 --- a/cli/command/context/export.go +++ b/cli/command/context/export.go @@ -15,36 +15,37 @@ import ( "k8s.io/client-go/tools/clientcmd" ) -type exportOptions struct { - kubeconfig bool - contextName string - dest string +// ExportOptions are the options used for exporting a context +type ExportOptions struct { + Kubeconfig bool + ContextName string + Dest string } func newExportCommand(dockerCli command.Cli) *cobra.Command { - opts := &exportOptions{} + opts := &ExportOptions{} cmd := &cobra.Command{ Use: "export [OPTIONS] CONTEXT [FILE|-]", Short: "Export a context to a tar or kubeconfig file", Args: cli.RequiresRangeArgs(1, 2), RunE: func(cmd *cobra.Command, args []string) error { - opts.contextName = args[0] + opts.ContextName = args[0] if len(args) == 2 { - opts.dest = args[1] + opts.Dest = args[1] } else { - opts.dest = opts.contextName - if opts.kubeconfig { - opts.dest += ".kubeconfig" + opts.Dest = opts.ContextName + if opts.Kubeconfig { + opts.Dest += ".kubeconfig" } else { - opts.dest += ".dockercontext" + opts.Dest += ".dockercontext" } } - return runExport(dockerCli, opts) + return RunExport(dockerCli, opts) }, } flags := cmd.Flags() - flags.BoolVar(&opts.kubeconfig, "kubeconfig", false, "Export as a kubeconfig file") + flags.BoolVar(&opts.Kubeconfig, "kubeconfig", false, "Export as a kubeconfig file") return cmd } @@ -74,24 +75,25 @@ func writeTo(dockerCli command.Cli, reader io.Reader, dest string) error { return nil } -func runExport(dockerCli command.Cli, opts *exportOptions) error { - if err := validateContextName(opts.contextName); err != nil { +// RunExport exports a Docker context +func RunExport(dockerCli command.Cli, opts *ExportOptions) error { + if err := validateContextName(opts.ContextName); err != nil { return err } - ctxMeta, err := dockerCli.ContextStore().GetContextMetadata(opts.contextName) + ctxMeta, err := dockerCli.ContextStore().GetContextMetadata(opts.ContextName) if err != nil { return err } - if !opts.kubeconfig { - reader := store.Export(opts.contextName, dockerCli.ContextStore()) + if !opts.Kubeconfig { + reader := store.Export(opts.ContextName, dockerCli.ContextStore()) defer reader.Close() - return writeTo(dockerCli, reader, opts.dest) + return writeTo(dockerCli, reader, opts.Dest) } kubernetesEndpointMeta := kubernetes.EndpointFromContext(ctxMeta) if kubernetesEndpointMeta == nil { - return fmt.Errorf("context %q has no kubernetes endpoint", opts.contextName) + return fmt.Errorf("context %q has no kubernetes endpoint", opts.ContextName) } - kubernetesEndpoint, err := kubernetesEndpointMeta.WithTLSData(dockerCli.ContextStore(), opts.contextName) + kubernetesEndpoint, err := kubernetesEndpointMeta.WithTLSData(dockerCli.ContextStore(), opts.ContextName) if err != nil { return err } @@ -104,5 +106,5 @@ func runExport(dockerCli command.Cli, opts *exportOptions) error { if err != nil { return err } - return writeTo(dockerCli, bytes.NewBuffer(data), opts.dest) + return writeTo(dockerCli, bytes.NewBuffer(data), opts.Dest) } diff --git a/cli/command/context/import.go b/cli/command/context/import.go index b1f68ec4eec2..27d5432e4802 100644 --- a/cli/command/context/import.go +++ b/cli/command/context/import.go @@ -17,13 +17,14 @@ func newImportCommand(dockerCli command.Cli) *cobra.Command { Short: "Import a context from a tar file", Args: cli.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - return runImport(dockerCli, args[0], args[1]) + return RunImport(dockerCli, args[0], args[1]) }, } return cmd } -func runImport(dockerCli command.Cli, name string, source string) error { +// RunImport imports a Docker context +func RunImport(dockerCli command.Cli, name string, source string) error { if err := checkContextNameForCreation(dockerCli.ContextStore(), name); err != nil { return err } diff --git a/cli/command/context/list_test.go b/cli/command/context/list_test.go index 1edf34ae2a9c..902f620c2821 100644 --- a/cli/command/context/list_test.go +++ b/cli/command/context/list_test.go @@ -14,12 +14,12 @@ func createTestContextWithKubeAndSwarm(t *testing.T, cli command.Cli, name strin revert := env.Patch(t, "KUBECONFIG", "./testdata/test-kubeconfig") defer revert() - err := runCreate(cli, &createOptions{ - name: name, - defaultStackOrchestrator: orchestrator, - description: "description of " + name, - kubernetes: map[string]string{keyFromCurrent: "true"}, - docker: map[string]string{keyHost: "https://someswarmserver"}, + err := RunCreate(cli, &CreateOptions{ + Name: name, + DefaultStackOrchestrator: orchestrator, + Description: "description of " + name, + Kubernetes: map[string]string{keyFromCurrent: "true"}, + Docker: map[string]string{keyHost: "https://someswarmserver"}, }) assert.NilError(t, err) } diff --git a/cli/command/context/remove.go b/cli/command/context/remove.go index bacff0b0b821..9b7411df8255 100644 --- a/cli/command/context/remove.go +++ b/cli/command/context/remove.go @@ -10,32 +10,34 @@ import ( "github.com/spf13/cobra" ) -type removeOptions struct { - force bool +// RemoveOptions are the options used to remove contexts +type RemoveOptions struct { + Force bool } func newRemoveCommand(dockerCli command.Cli) *cobra.Command { - var opts removeOptions + var opts RemoveOptions cmd := &cobra.Command{ Use: "rm CONTEXT [CONTEXT...]", Aliases: []string{"remove"}, Short: "Remove one or more contexts", Args: cli.RequiresMinArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return runRemove(dockerCli, opts, args) + return RunRemove(dockerCli, opts, args) }, } - cmd.Flags().BoolVarP(&opts.force, "force", "f", false, "Force the removal of a context in use") + cmd.Flags().BoolVarP(&opts.Force, "force", "f", false, "Force the removal of a context in use") return cmd } -func runRemove(dockerCli command.Cli, opts removeOptions, names []string) error { +// RunRemove removes one or more contexts +func RunRemove(dockerCli command.Cli, opts RemoveOptions, names []string) error { var errs []string currentCtx := dockerCli.CurrentContext() for _, name := range names { if name == "default" { errs = append(errs, `default: context "default" cannot be removed`) - } else if err := doRemove(dockerCli, name, name == currentCtx, opts.force); err != nil { + } else if err := doRemove(dockerCli, name, name == currentCtx, opts.Force); err != nil { errs = append(errs, fmt.Sprintf("%s: %s", name, err)) } else { fmt.Fprintln(dockerCli.Out(), name) diff --git a/cli/command/context/remove_test.go b/cli/command/context/remove_test.go index bc6438fb787e..53ad12f516d3 100644 --- a/cli/command/context/remove_test.go +++ b/cli/command/context/remove_test.go @@ -17,7 +17,7 @@ func TestRemove(t *testing.T) { defer cleanup() createTestContextWithKubeAndSwarm(t, cli, "current", "all") createTestContextWithKubeAndSwarm(t, cli, "other", "all") - assert.NilError(t, runRemove(cli, removeOptions{}, []string{"other"})) + assert.NilError(t, RunRemove(cli, RemoveOptions{}, []string{"other"})) _, err := cli.ContextStore().GetContextMetadata("current") assert.NilError(t, err) _, err = cli.ContextStore().GetContextMetadata("other") @@ -29,7 +29,7 @@ func TestRemoveNotAContext(t *testing.T) { defer cleanup() createTestContextWithKubeAndSwarm(t, cli, "current", "all") createTestContextWithKubeAndSwarm(t, cli, "other", "all") - err := runRemove(cli, removeOptions{}, []string{"not-a-context"}) + err := RunRemove(cli, RemoveOptions{}, []string{"not-a-context"}) assert.ErrorContains(t, err, `context "not-a-context" does not exist`) } @@ -39,7 +39,7 @@ func TestRemoveCurrent(t *testing.T) { createTestContextWithKubeAndSwarm(t, cli, "current", "all") createTestContextWithKubeAndSwarm(t, cli, "other", "all") cli.SetCurrentContext("current") - err := runRemove(cli, removeOptions{}, []string{"current"}) + err := RunRemove(cli, RemoveOptions{}, []string{"current"}) assert.ErrorContains(t, err, "current: context is in use, set -f flag to force remove") } @@ -57,7 +57,7 @@ func TestRemoveCurrentForce(t *testing.T) { createTestContextWithKubeAndSwarm(t, cli, "current", "all") createTestContextWithKubeAndSwarm(t, cli, "other", "all") cli.SetCurrentContext("current") - assert.NilError(t, runRemove(cli, removeOptions{force: true}, []string{"current"})) + assert.NilError(t, RunRemove(cli, RemoveOptions{Force: true}, []string{"current"})) reloadedConfig, err := config.Load(configDir) assert.NilError(t, err) assert.Equal(t, "", reloadedConfig.CurrentContext) diff --git a/cli/command/context/update.go b/cli/command/context/update.go index 24fae3b61f3b..67e1bc0fcbce 100644 --- a/cli/command/context/update.go +++ b/cli/command/context/update.go @@ -14,12 +14,13 @@ import ( "github.com/spf13/cobra" ) -type updateOptions struct { - name string - description string - defaultStackOrchestrator string - docker map[string]string - kubernetes map[string]string +// UpdateOptions are the options used to update a context +type UpdateOptions struct { + Name string + Description string + DefaultStackOrchestrator string + Docker map[string]string + Kubernetes map[string]string } func longUpdateDescription() string { @@ -43,34 +44,35 @@ func longUpdateDescription() string { } func newUpdateCommand(dockerCli command.Cli) *cobra.Command { - opts := &updateOptions{} + opts := &UpdateOptions{} cmd := &cobra.Command{ Use: "update [OPTIONS] CONTEXT", Short: "Update a context", Args: cli.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - opts.name = args[0] - return runUpdate(dockerCli, opts) + opts.Name = args[0] + return RunUpdate(dockerCli, opts) }, Long: longUpdateDescription(), } flags := cmd.Flags() - flags.StringVar(&opts.description, "description", "", "Description of the context") + flags.StringVar(&opts.Description, "description", "", "Description of the context") flags.StringVar( - &opts.defaultStackOrchestrator, + &opts.DefaultStackOrchestrator, "default-stack-orchestrator", "", "Default orchestrator for stack operations to use with this context (swarm|kubernetes|all)") - flags.StringToStringVar(&opts.docker, "docker", nil, "set the docker endpoint") - flags.StringToStringVar(&opts.kubernetes, "kubernetes", nil, "set the kubernetes endpoint") + flags.StringToStringVar(&opts.Docker, "docker", nil, "set the docker endpoint") + flags.StringToStringVar(&opts.Kubernetes, "kubernetes", nil, "set the kubernetes endpoint") return cmd } -func runUpdate(cli command.Cli, o *updateOptions) error { - if err := validateContextName(o.name); err != nil { +// RunUpdate updates a Docker context +func RunUpdate(cli command.Cli, o *UpdateOptions) error { + if err := validateContextName(o.Name); err != nil { return err } s := cli.ContextStore() - c, err := s.GetContextMetadata(o.name) + c, err := s.GetContextMetadata(o.Name) if err != nil { return err } @@ -78,31 +80,31 @@ func runUpdate(cli command.Cli, o *updateOptions) error { if err != nil { return err } - if o.defaultStackOrchestrator != "" { - stackOrchestrator, err := command.NormalizeOrchestrator(o.defaultStackOrchestrator) + if o.DefaultStackOrchestrator != "" { + stackOrchestrator, err := command.NormalizeOrchestrator(o.DefaultStackOrchestrator) if err != nil { return errors.Wrap(err, "unable to parse default-stack-orchestrator") } dockerContext.StackOrchestrator = stackOrchestrator } - if o.description != "" { - dockerContext.Description = o.description + if o.Description != "" { + dockerContext.Description = o.Description } c.Metadata = dockerContext tlsDataToReset := make(map[string]*store.EndpointTLSData) - if o.docker != nil { - dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(cli, o.docker) + if o.Docker != nil { + dockerEP, dockerTLS, err := getDockerEndpointMetadataAndTLS(cli, o.Docker) if err != nil { return errors.Wrap(err, "unable to create docker endpoint config") } c.Endpoints[docker.DockerEndpoint] = dockerEP tlsDataToReset[docker.DockerEndpoint] = dockerTLS } - if o.kubernetes != nil { - kubernetesEP, kubernetesTLS, err := getKubernetesEndpointMetadataAndTLS(cli, o.kubernetes) + if o.Kubernetes != nil { + kubernetesEP, kubernetesTLS, err := getKubernetesEndpointMetadataAndTLS(cli, o.Kubernetes) if err != nil { return errors.Wrap(err, "unable to create kubernetes endpoint config") } @@ -120,13 +122,13 @@ func runUpdate(cli command.Cli, o *updateOptions) error { return err } for ep, tlsData := range tlsDataToReset { - if err := s.ResetContextEndpointTLSMaterial(o.name, ep, tlsData); err != nil { + if err := s.ResetContextEndpointTLSMaterial(o.Name, ep, tlsData); err != nil { return err } } - fmt.Fprintln(cli.Out(), o.name) - fmt.Fprintf(cli.Err(), "Successfully updated context %q\n", o.name) + fmt.Fprintln(cli.Out(), o.Name) + fmt.Fprintf(cli.Err(), "Successfully updated context %q\n", o.Name) return nil } diff --git a/cli/command/context/update_test.go b/cli/command/context/update_test.go index 49109bf9dd4d..61310e7a6c14 100644 --- a/cli/command/context/update_test.go +++ b/cli/command/context/update_test.go @@ -13,17 +13,17 @@ import ( func TestUpdateDescriptionOnly(t *testing.T) { cli, cleanup := makeFakeCli(t) defer cleanup() - err := runCreate(cli, &createOptions{ - name: "test", - defaultStackOrchestrator: "swarm", - docker: map[string]string{}, + err := RunCreate(cli, &CreateOptions{ + Name: "test", + DefaultStackOrchestrator: "swarm", + Docker: map[string]string{}, }) assert.NilError(t, err) cli.OutBuffer().Reset() cli.ErrBuffer().Reset() - assert.NilError(t, runUpdate(cli, &updateOptions{ - name: "test", - description: "description", + assert.NilError(t, RunUpdate(cli, &UpdateOptions{ + Name: "test", + Description: "description", })) c, err := cli.ContextStore().GetContextMetadata("test") assert.NilError(t, err) @@ -40,9 +40,9 @@ func TestUpdateDockerOnly(t *testing.T) { cli, cleanup := makeFakeCli(t) defer cleanup() createTestContextWithKubeAndSwarm(t, cli, "test", "swarm") - assert.NilError(t, runUpdate(cli, &updateOptions{ - name: "test", - docker: map[string]string{ + assert.NilError(t, RunUpdate(cli, &UpdateOptions{ + Name: "test", + Docker: map[string]string{ keyHost: "tcp://some-host", }, })) @@ -60,15 +60,15 @@ func TestUpdateDockerOnly(t *testing.T) { func TestUpdateStackOrchestratorStrategy(t *testing.T) { cli, cleanup := makeFakeCli(t) defer cleanup() - err := runCreate(cli, &createOptions{ - name: "test", - defaultStackOrchestrator: "swarm", - docker: map[string]string{}, + err := RunCreate(cli, &CreateOptions{ + Name: "test", + DefaultStackOrchestrator: "swarm", + Docker: map[string]string{}, }) assert.NilError(t, err) - err = runUpdate(cli, &updateOptions{ - name: "test", - defaultStackOrchestrator: "kubernetes", + err = RunUpdate(cli, &UpdateOptions{ + Name: "test", + DefaultStackOrchestrator: "kubernetes", }) assert.ErrorContains(t, err, `cannot specify orchestrator "kubernetes" without configuring a Kubernetes endpoint`) } @@ -77,9 +77,9 @@ func TestUpdateStackOrchestratorStrategyRemoveKubeEndpoint(t *testing.T) { cli, cleanup := makeFakeCli(t) defer cleanup() createTestContextWithKubeAndSwarm(t, cli, "test", "kubernetes") - err := runUpdate(cli, &updateOptions{ - name: "test", - kubernetes: map[string]string{}, + err := RunUpdate(cli, &UpdateOptions{ + Name: "test", + Kubernetes: map[string]string{}, }) assert.ErrorContains(t, err, `cannot specify orchestrator "kubernetes" without configuring a Kubernetes endpoint`) } @@ -87,14 +87,14 @@ func TestUpdateStackOrchestratorStrategyRemoveKubeEndpoint(t *testing.T) { func TestUpdateInvalidDockerHost(t *testing.T) { cli, cleanup := makeFakeCli(t) defer cleanup() - err := runCreate(cli, &createOptions{ - name: "test", - docker: map[string]string{}, + err := RunCreate(cli, &CreateOptions{ + Name: "test", + Docker: map[string]string{}, }) assert.NilError(t, err) - err = runUpdate(cli, &updateOptions{ - name: "test", - docker: map[string]string{ + err = RunUpdate(cli, &UpdateOptions{ + Name: "test", + Docker: map[string]string{ keyHost: "some///invalid/host", }, }) diff --git a/cli/command/context/use.go b/cli/command/context/use.go index bdffda3c9fb6..e6b296d96500 100644 --- a/cli/command/context/use.go +++ b/cli/command/context/use.go @@ -14,26 +14,30 @@ func newUseCommand(dockerCli command.Cli) *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { name := args[0] - - if err := validateContextName(name); err != nil && name != "default" { - return err - } - if _, err := dockerCli.ContextStore().GetContextMetadata(name); err != nil && name != "default" { - return err - } - configValue := name - if configValue == "default" { - configValue = "" - } - dockerConfig := dockerCli.ConfigFile() - dockerConfig.CurrentContext = configValue - if err := dockerConfig.Save(); err != nil { - return err - } - fmt.Fprintln(dockerCli.Out(), name) - fmt.Fprintf(dockerCli.Err(), "Current context is now %q\n", name) - return nil + return RunUse(dockerCli, name) }, } return cmd } + +// RunUse set the current Docker context +func RunUse(dockerCli command.Cli, name string) error { + if err := validateContextName(name); err != nil && name != "default" { + return err + } + if _, err := dockerCli.ContextStore().GetContextMetadata(name); err != nil && name != "default" { + return err + } + configValue := name + if configValue == "default" { + configValue = "" + } + dockerConfig := dockerCli.ConfigFile() + dockerConfig.CurrentContext = configValue + if err := dockerConfig.Save(); err != nil { + return err + } + fmt.Fprintln(dockerCli.Out(), name) + fmt.Fprintf(dockerCli.Err(), "Current context is now %q\n", name) + return nil +} diff --git a/cli/command/context/use_test.go b/cli/command/context/use_test.go index 7fd309b47af4..139189af3559 100644 --- a/cli/command/context/use_test.go +++ b/cli/command/context/use_test.go @@ -20,9 +20,9 @@ func TestUse(t *testing.T) { testCfg := configfile.New(configFilePath) cli, cleanup := makeFakeCli(t, withCliConfig(testCfg)) defer cleanup() - err = runCreate(cli, &createOptions{ - name: "test", - docker: map[string]string{}, + err = RunCreate(cli, &CreateOptions{ + Name: "test", + Docker: map[string]string{}, }) assert.NilError(t, err) assert.NilError(t, newUseCommand(cli).RunE(nil, []string{"test"})) diff --git a/cli/context/store/storeconfig.go b/cli/context/store/storeconfig.go index 9746d93d7738..b282a9d10ee5 100644 --- a/cli/context/store/storeconfig.go +++ b/cli/context/store/storeconfig.go @@ -25,6 +25,11 @@ type Config struct { endpointTypes map[string]TypeGetter } +// SetEndpoint set an endpoint typing information +func (c Config) SetEndpoint(name string, getter TypeGetter) { + c.endpointTypes[name] = getter +} + // NewConfig creates a config object func NewConfig(contextType TypeGetter, endpoints ...NamedTypeGetter) Config { res := Config{ diff --git a/cli/context/store/storeconfig_test.go b/cli/context/store/storeconfig_test.go new file mode 100644 index 000000000000..8cc5c790709e --- /dev/null +++ b/cli/context/store/storeconfig_test.go @@ -0,0 +1,31 @@ +package store + +import ( + "testing" + + "gotest.tools/assert" +) + +type testCtx struct{} +type testEP1 struct{} +type testEP2 struct{} +type testEP3 struct{} + +func TestConfigModification(t *testing.T) { + cfg := NewConfig(func() interface{} { return &testCtx{} }, EndpointTypeGetter("ep1", func() interface{} { return &testEP1{} })) + assert.Equal(t, &testCtx{}, cfg.contextType()) + assert.Equal(t, &testEP1{}, cfg.endpointTypes["ep1"]()) + cfgCopy := cfg + + // modify existing endpoint + cfg.SetEndpoint("ep1", func() interface{} { return &testEP2{} }) + // add endpoint + cfg.SetEndpoint("ep2", func() interface{} { return &testEP3{} }) + assert.Equal(t, &testCtx{}, cfg.contextType()) + assert.Equal(t, &testEP2{}, cfg.endpointTypes["ep1"]()) + assert.Equal(t, &testEP3{}, cfg.endpointTypes["ep2"]()) + // check it applied on already initialized store + assert.Equal(t, &testCtx{}, cfgCopy.contextType()) + assert.Equal(t, &testEP2{}, cfgCopy.endpointTypes["ep1"]()) + assert.Equal(t, &testEP3{}, cfgCopy.endpointTypes["ep2"]()) +} From 158a7668865c97c44bce6b164b45a11cdaac3edc Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Mon, 10 Dec 2018 14:41:22 +0000 Subject: [PATCH 20/60] Fold `dockerPreRun` into `DockerCli.Initialize` All of the current callers follow the pattern: dockerPreRun(opts) err := dockerCli.Initialize(opts) ... So there is no semantic change into merging the content of `dockerPreRun` into the head of `Initialize`. I'm about to add a new caller outside of the `cmd/docker` package and this seems preferable exporting `DockerPreRun`. Signed-off-by: Ian Campbell --- cli/command/cli.go | 11 +++++++++++ cmd/docker/docker.go | 15 --------------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/cli/command/cli.go b/cli/command/cli.go index fc557635c483..a8711de70c9a 100644 --- a/cli/command/cli.go +++ b/cli/command/cli.go @@ -16,6 +16,7 @@ import ( "github.com/docker/cli/cli/context/docker" kubcontext "github.com/docker/cli/cli/context/kubernetes" "github.com/docker/cli/cli/context/store" + "github.com/docker/cli/cli/debug" cliflags "github.com/docker/cli/cli/flags" manifeststore "github.com/docker/cli/cli/manifest/store" registryclient "github.com/docker/cli/cli/registry/client" @@ -177,6 +178,16 @@ func (cli *DockerCli) RegistryClient(allowInsecure bool) registryclient.Registry // Initialize the dockerCli runs initialization that must happen after command // line flags are parsed. func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error { + cliflags.SetLogLevel(opts.Common.LogLevel) + + if opts.ConfigDir != "" { + cliconfig.SetDir(opts.ConfigDir) + } + + if opts.Common.Debug { + debug.Enable() + } + cli.configFile = cliconfig.LoadDefaultConfigFile(cli.err) var err error cli.contextStore = store.New(cliconfig.ContextStoreDir(), cli.contextStoreConfig) diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index 5909953be3db..b16b3ec19476 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -10,7 +10,6 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/commands" cliconfig "github.com/docker/cli/cli/config" - "github.com/docker/cli/cli/debug" cliflags "github.com/docker/cli/cli/flags" "github.com/docker/docker/api/types/versions" "github.com/docker/docker/client" @@ -36,7 +35,6 @@ func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command { PersistentPreRunE: func(cmd *cobra.Command, args []string) error { // flags must be the top-level command flags, not cmd.Flags() opts.Common.SetDefaultOptions(flags) - dockerPreRun(opts) if err := dockerCli.Initialize(opts); err != nil { return err } @@ -144,7 +142,6 @@ func initializeDockerCli(dockerCli *command.DockerCli, flags *pflag.FlagSet, opt // when using --help, PersistentPreRun is not called, so initialization is needed. // flags must be the top-level command flags, not cmd.Flags() opts.Common.SetDefaultOptions(flags) - dockerPreRun(opts) return dockerCli.Initialize(opts) } @@ -193,18 +190,6 @@ func main() { } } -func dockerPreRun(opts *cliflags.ClientOptions) { - cliflags.SetLogLevel(opts.Common.LogLevel) - - if opts.ConfigDir != "" { - cliconfig.SetDir(opts.ConfigDir) - } - - if opts.Common.Debug { - debug.Enable() - } -} - type versionDetails interface { Client() client.APIClient ClientInfo() command.ClientInfo From ccef1598b198ab53049cd50968cf7976803b96be Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Mon, 17 Dec 2018 16:59:11 +0000 Subject: [PATCH 21/60] Move `disableFlagsInUseLine` from `main` into our `cli` library ... and expose. I would like to use this from another site. This implies also moving (and exposing) the `visitAll` helper. Unit test them while I'm here. Signed-off-by: Ian Campbell --- cli/cobra.go | 19 +++++++++++++++++++ cli/cobra_test.go | 30 ++++++++++++++++++++++++++++++ cmd/docker/docker.go | 21 ++------------------- 3 files changed, 51 insertions(+), 19 deletions(-) create mode 100644 cli/cobra_test.go diff --git a/cli/cobra.go b/cli/cobra.go index a7431a08766c..b0c50a2aa9f4 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -46,6 +46,25 @@ func FlagErrorFunc(cmd *cobra.Command, err error) error { } } +// VisitAll will traverse all commands from the root. +// This is different from the VisitAll of cobra.Command where only parents +// are checked. +func VisitAll(root *cobra.Command, fn func(*cobra.Command)) { + for _, cmd := range root.Commands() { + VisitAll(cmd, fn) + } + fn(root) +} + +// DisableFlagsInUseLine sets the DisableFlagsInUseLine flag on all +// commands within the tree rooted at cmd. +func DisableFlagsInUseLine(cmd *cobra.Command) { + VisitAll(cmd, func(ccmd *cobra.Command) { + // do not add a `[flags]` to the end of the usage line. + ccmd.DisableFlagsInUseLine = true + }) +} + var helpCommand = &cobra.Command{ Use: "help [command]", Short: "Help about the command", diff --git a/cli/cobra_test.go b/cli/cobra_test.go new file mode 100644 index 000000000000..8c9cf1b19fc6 --- /dev/null +++ b/cli/cobra_test.go @@ -0,0 +1,30 @@ +package cli + +import ( + "testing" + + "github.com/spf13/cobra" + "gotest.tools/assert" +) + +func TestVisitAll(t *testing.T) { + root := &cobra.Command{Use: "root"} + sub1 := &cobra.Command{Use: "sub1"} + sub1sub1 := &cobra.Command{Use: "sub1sub1"} + sub1sub2 := &cobra.Command{Use: "sub1sub2"} + sub2 := &cobra.Command{Use: "sub2"} + + root.AddCommand(sub1, sub2) + sub1.AddCommand(sub1sub1, sub1sub2) + + // Take the opportunity to test DisableFlagsInUseLine too + DisableFlagsInUseLine(root) + + var visited []string + VisitAll(root, func(ccmd *cobra.Command) { + visited = append(visited, ccmd.Name()) + assert.Assert(t, ccmd.DisableFlagsInUseLine, "DisableFlagsInUseLine not set on %q", ccmd.Name()) + }) + expected := []string{"sub1sub1", "sub1sub2", "sub1", "sub2", "root"} + assert.DeepEqual(t, expected, visited) +} diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index b16b3ec19476..69d773aa2ef4 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -57,19 +57,12 @@ func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command { cmd.SetOutput(dockerCli.Out()) commands.AddCommands(cmd, dockerCli) - disableFlagsInUseLine(cmd) + cli.DisableFlagsInUseLine(cmd) setValidateArgs(dockerCli, cmd, flags, opts) return cmd } -func disableFlagsInUseLine(cmd *cobra.Command) { - visitAll(cmd, func(ccmd *cobra.Command) { - // do not add a `[flags]` to the end of the usage line. - ccmd.DisableFlagsInUseLine = true - }) -} - func setFlagErrorFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag.FlagSet, opts *cliflags.ClientOptions) { // When invoking `docker stack --nonsense`, we need to make sure FlagErrorFunc return appropriate // output if the feature is not supported. @@ -111,7 +104,7 @@ func setValidateArgs(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pf // As a result, here we replace the existing Args validation func to a wrapper, // where the wrapper will check to see if the feature is supported or not. // The Args validation error will only be returned if the feature is supported. - visitAll(cmd, func(ccmd *cobra.Command) { + cli.VisitAll(cmd, func(ccmd *cobra.Command) { // if there is no tags for a command or any of its parent, // there is no need to wrap the Args validation. if !hasTags(ccmd) { @@ -145,16 +138,6 @@ func initializeDockerCli(dockerCli *command.DockerCli, flags *pflag.FlagSet, opt return dockerCli.Initialize(opts) } -// visitAll will traverse all commands from the root. -// This is different from the VisitAll of cobra.Command where only parents -// are checked. -func visitAll(root *cobra.Command, fn func(*cobra.Command)) { - for _, cmd := range root.Commands() { - visitAll(cmd, fn) - } - fn(root) -} - func noArgs(cmd *cobra.Command, args []string) error { if len(args) == 0 { return nil From 38645ca44a6a7f44978de42a42238bd2be63590e Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Tue, 18 Dec 2018 10:02:47 +0000 Subject: [PATCH 22/60] Refactor common bits of `SetupRootCommand` I'm shortly going to add a second user to setup plugins, which will want to reuse the common bits. Signed-off-by: Ian Campbell --- cli/cobra.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cli/cobra.go b/cli/cobra.go index b0c50a2aa9f4..1e83a0ef4efa 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -9,9 +9,7 @@ import ( "github.com/spf13/cobra" ) -// SetupRootCommand sets default usage, help, and error handling for the -// root command. -func SetupRootCommand(rootCmd *cobra.Command) { +func setupCommonRootCommand(rootCmd *cobra.Command) { cobra.AddTemplateFunc("hasSubCommands", hasSubCommands) cobra.AddTemplateFunc("hasManagementSubCommands", hasManagementSubCommands) cobra.AddTemplateFunc("operationSubCommands", operationSubCommands) @@ -22,6 +20,13 @@ func SetupRootCommand(rootCmd *cobra.Command) { rootCmd.SetHelpTemplate(helpTemplate) rootCmd.SetFlagErrorFunc(FlagErrorFunc) rootCmd.SetHelpCommand(helpCommand) +} + +// SetupRootCommand sets default usage, help, and error handling for the +// root command. +func SetupRootCommand(rootCmd *cobra.Command) { + setupCommonRootCommand(rootCmd) + rootCmd.SetVersionTemplate("Docker version {{.Version}}\n") rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage") From c5168117af97d951013b2baf0948157fb59faf7e Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Tue, 18 Dec 2018 10:16:52 +0000 Subject: [PATCH 23/60] Push setup of opts and default flagset into SetupRootCommand I'm shortly going to add a second user (plugins) which want to share some behaviour. Signed-off-by: Ian Campbell --- cli/cobra.go | 19 ++++++++++++++++--- cmd/docker/docker.go | 13 +++++-------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/cli/cobra.go b/cli/cobra.go index 1e83a0ef4efa..ce4284a0fded 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -4,12 +4,21 @@ import ( "fmt" "strings" + cliconfig "github.com/docker/cli/cli/config" + cliflags "github.com/docker/cli/cli/flags" "github.com/docker/docker/pkg/term" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) -func setupCommonRootCommand(rootCmd *cobra.Command) { +func setupCommonRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *pflag.FlagSet) { + opts := cliflags.NewClientOptions() + flags := rootCmd.Flags() + + flags.StringVar(&opts.ConfigDir, "config", cliconfig.Dir(), "Location of client config files") + opts.Common.InstallFlags(flags) + cobra.AddTemplateFunc("hasSubCommands", hasSubCommands) cobra.AddTemplateFunc("hasManagementSubCommands", hasManagementSubCommands) cobra.AddTemplateFunc("operationSubCommands", operationSubCommands) @@ -20,18 +29,22 @@ func setupCommonRootCommand(rootCmd *cobra.Command) { rootCmd.SetHelpTemplate(helpTemplate) rootCmd.SetFlagErrorFunc(FlagErrorFunc) rootCmd.SetHelpCommand(helpCommand) + + return opts, flags } // SetupRootCommand sets default usage, help, and error handling for the // root command. -func SetupRootCommand(rootCmd *cobra.Command) { - setupCommonRootCommand(rootCmd) +func SetupRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *pflag.FlagSet) { + opts, flags := setupCommonRootCommand(rootCmd) rootCmd.SetVersionTemplate("Docker version {{.Version}}\n") rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage") rootCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help") rootCmd.PersistentFlags().Lookup("help").Hidden = true + + return opts, flags } // FlagErrorFunc prints an error message which matches the format of the diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index 69d773aa2ef4..e64fe2afd8cf 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -9,7 +9,6 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/commands" - cliconfig "github.com/docker/cli/cli/config" cliflags "github.com/docker/cli/cli/flags" "github.com/docker/docker/api/types/versions" "github.com/docker/docker/client" @@ -19,8 +18,10 @@ import ( ) func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command { - opts := cliflags.NewClientOptions() - var flags *pflag.FlagSet + var ( + opts *cliflags.ClientOptions + flags *pflag.FlagSet + ) cmd := &cobra.Command{ Use: "docker [OPTIONS] COMMAND [ARG...]", @@ -43,12 +44,8 @@ func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command { Version: fmt.Sprintf("%s, build %s", cli.Version, cli.GitCommit), DisableFlagsInUseLine: true, } - cli.SetupRootCommand(cmd) - - flags = cmd.Flags() + opts, flags = cli.SetupRootCommand(cmd) flags.BoolP("version", "v", false, "Print version information and quit") - flags.StringVar(&opts.ConfigDir, "config", cliconfig.Dir(), "Location of client config files") - opts.Common.InstallFlags(flags) setFlagErrorFunc(dockerCli, cmd, flags, opts) From 20c19830a95455e8562551aad52c715ad0807cc6 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Tue, 8 Jan 2019 15:03:51 +0000 Subject: [PATCH 24/60] Move versioning variables to a separate package. This helps to avoid circular includes, by separating the pure data out from the actual functionality in the cli subpackage, allowing other code which is imported to access the data. Signed-off-by: Ian Campbell --- cli/command/cli.go | 4 ++-- cli/command/system/version.go | 9 +++++---- cli/{ => version}/version.go | 2 +- cmd/docker/docker.go | 3 ++- scripts/build/.variables | 8 ++++---- 5 files changed, 14 insertions(+), 12 deletions(-) rename cli/{ => version}/version.go (92%) diff --git a/cli/command/cli.go b/cli/command/cli.go index a8711de70c9a..28936fc3bf56 100644 --- a/cli/command/cli.go +++ b/cli/command/cli.go @@ -8,7 +8,6 @@ import ( "runtime" "strconv" - "github.com/docker/cli/cli" "github.com/docker/cli/cli/config" cliconfig "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config/configfile" @@ -22,6 +21,7 @@ import ( registryclient "github.com/docker/cli/cli/registry/client" "github.com/docker/cli/cli/streams" "github.com/docker/cli/cli/trust" + "github.com/docker/cli/cli/version" "github.com/docker/cli/internal/containerizedengine" dopts "github.com/docker/cli/opts" clitypes "github.com/docker/cli/types" @@ -472,7 +472,7 @@ func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (string, error // UserAgent returns the user agent string used for making API requests func UserAgent() string { - return "Docker-Client/" + cli.Version + " (" + runtime.GOOS + ")" + return "Docker-Client/" + version.Version + " (" + runtime.GOOS + ")" } // resolveContextName resolves the current context name with the following rules: diff --git a/cli/command/system/version.go b/cli/command/system/version.go index a8f0beb1fdc0..2e2ae2ee3acc 100644 --- a/cli/command/system/version.go +++ b/cli/command/system/version.go @@ -12,6 +12,7 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" kubecontext "github.com/docker/cli/cli/context/kubernetes" + "github.com/docker/cli/cli/version" "github.com/docker/cli/kubernetes" "github.com/docker/cli/templates" "github.com/docker/docker/api/types" @@ -135,13 +136,13 @@ func runVersion(dockerCli command.Cli, opts *versionOptions) error { vd := versionInfo{ Client: clientVersion{ - Platform: struct{ Name string }{cli.PlatformName}, - Version: cli.Version, + Platform: struct{ Name string }{version.PlatformName}, + Version: version.Version, APIVersion: dockerCli.Client().ClientVersion(), DefaultAPIVersion: dockerCli.DefaultVersion(), GoVersion: runtime.Version(), - GitCommit: cli.GitCommit, - BuildTime: reformatDate(cli.BuildTime), + GitCommit: version.GitCommit, + BuildTime: reformatDate(version.BuildTime), Os: runtime.GOOS, Arch: runtime.GOARCH, Experimental: dockerCli.ClientInfo().HasExperimental, diff --git a/cli/version.go b/cli/version/version.go similarity index 92% rename from cli/version.go rename to cli/version/version.go index c4120b9585fb..a263b9a73133 100644 --- a/cli/version.go +++ b/cli/version/version.go @@ -1,4 +1,4 @@ -package cli +package version // Default build-time variable. // These values are overridden via ldflags diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index e64fe2afd8cf..f8890304ecfb 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -10,6 +10,7 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/commands" cliflags "github.com/docker/cli/cli/flags" + "github.com/docker/cli/cli/version" "github.com/docker/docker/api/types/versions" "github.com/docker/docker/client" "github.com/sirupsen/logrus" @@ -41,7 +42,7 @@ func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command { } return isSupported(cmd, dockerCli) }, - Version: fmt.Sprintf("%s, build %s", cli.Version, cli.GitCommit), + Version: fmt.Sprintf("%s, build %s", version.Version, version.GitCommit), DisableFlagsInUseLine: true, } opts, flags = cli.SetupRootCommand(cmd) diff --git a/scripts/build/.variables b/scripts/build/.variables index 208f44c3164d..8b13cd55c86c 100755 --- a/scripts/build/.variables +++ b/scripts/build/.variables @@ -8,15 +8,15 @@ BUILDTIME=${BUILDTIME:-$(date --utc --rfc-3339 ns 2> /dev/null | sed -e 's/ /T/' PLATFORM_LDFLAGS= if test -n "${PLATFORM}"; then - PLATFORM_LDFLAGS="-X \"github.com/docker/cli/cli.PlatformName=${PLATFORM}\"" + PLATFORM_LDFLAGS="-X \"github.com/docker/cli/cli/version.PlatformName=${PLATFORM}\"" fi export LDFLAGS="\ -w \ ${PLATFORM_LDFLAGS} \ - -X \"github.com/docker/cli/cli.GitCommit=${GITCOMMIT}\" \ - -X \"github.com/docker/cli/cli.BuildTime=${BUILDTIME}\" \ - -X \"github.com/docker/cli/cli.Version=${VERSION}\" \ + -X \"github.com/docker/cli/cli/version.GitCommit=${GITCOMMIT}\" \ + -X \"github.com/docker/cli/cli/version.BuildTime=${BUILDTIME}\" \ + -X \"github.com/docker/cli/cli/version.Version=${VERSION}\" \ ${LDFLAGS:-} \ " From eab40a5974206421705faed08e538c958806acbf Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Thu, 10 Jan 2019 15:49:06 +0000 Subject: [PATCH 25/60] cli/config: Add a helper to resolve a file within the config dir Signed-off-by: Ian Campbell --- cli/config/config.go | 5 +++++ cli/config/config_test.go | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/cli/config/config.go b/cli/config/config.go index 64f8d3b49c67..f773abee4091 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -46,6 +46,11 @@ func SetDir(dir string) { configDir = dir } +// Path returns the path to a file relative to the config dir +func Path(p ...string) string { + return filepath.Join(append([]string{Dir()}, p...)...) +} + // LegacyLoadFromReader is a convenience function that creates a ConfigFile object from // a non-nested reader func LegacyLoadFromReader(configData io.Reader) (*configfile.ConfigFile, error) { diff --git a/cli/config/config_test.go b/cli/config/config_test.go index 11c8dd6de5f2..85288291b461 100644 --- a/cli/config/config_test.go +++ b/cli/config/config_test.go @@ -548,3 +548,17 @@ func TestLoadDefaultConfigFile(t *testing.T) { assert.Check(t, is.DeepEqual(expected, configFile)) } + +func TestConfigPath(t *testing.T) { + oldDir := Dir() + + SetDir("dummy1") + f1 := Path("a", "b") + assert.Equal(t, f1, filepath.Join("dummy1", "a", "b")) + + SetDir("dummy2") + f2 := Path("c", "d") + assert.Equal(t, f2, filepath.Join("dummy2", "c", "d")) + + SetDir(oldDir) +} From 8cf946d1bcbe847d02312eb6d472b6b25bb9628e Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Mon, 28 Jan 2019 17:38:33 +0000 Subject: [PATCH 26/60] Unit test for WithContentTrustFromEnv I authored this for `contentTrustEnabled` prior to 7f207f3f957e, so this now tests the funcation argument version. Signed-off-by: Ian Campbell --- cli/command/cli_options_test.go | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 cli/command/cli_options_test.go diff --git a/cli/command/cli_options_test.go b/cli/command/cli_options_test.go new file mode 100644 index 000000000000..10dcad8b3dbc --- /dev/null +++ b/cli/command/cli_options_test.go @@ -0,0 +1,37 @@ +package command + +import ( + "os" + "testing" + + "gotest.tools/assert" +) + +func contentTrustEnabled(t *testing.T) bool { + var cli DockerCli + assert.NilError(t, WithContentTrustFromEnv()(&cli)) + return cli.contentTrust +} + +// NB: Do not t.Parallel() this test -- it messes with the process environment. +func TestWithContentTrustFromEnv(t *testing.T) { + envvar := "DOCKER_CONTENT_TRUST" + if orig, ok := os.LookupEnv(envvar); ok { + defer func() { + os.Setenv(envvar, orig) + }() + } else { + defer func() { + os.Unsetenv(envvar) + }() + } + + os.Setenv(envvar, "true") + assert.Assert(t, contentTrustEnabled(t)) + os.Setenv(envvar, "false") + assert.Assert(t, !contentTrustEnabled(t)) + os.Setenv(envvar, "invalid") + assert.Assert(t, contentTrustEnabled(t)) + os.Unsetenv(envvar) + assert.Assert(t, !contentTrustEnabled(t)) +} From e96240427f94c0b9473f8d4aeaed81938cf15610 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Mon, 10 Dec 2018 15:30:19 +0000 Subject: [PATCH 27/60] Add basic framework for writing a CLI plugin That is, the helper to be used from the plugin's `main`. Also add a `helloworld` plugin example and build integration. Signed-off-by: Ian Campbell --- Makefile | 12 ++++ cli-plugins/examples/helloworld/main.go | 38 ++++++++++ cli-plugins/manager/metadata.go | 25 +++++++ cli-plugins/plugin/plugin.go | 96 +++++++++++++++++++++++++ cli/cobra.go | 12 ++++ docker.Makefile | 11 +++ dockerfiles/Dockerfile.e2e | 1 + scripts/build/plugins | 21 ++++++ scripts/build/plugins-osx | 18 +++++ scripts/build/plugins-windows | 14 ++++ 10 files changed, 248 insertions(+) create mode 100644 cli-plugins/examples/helloworld/main.go create mode 100644 cli-plugins/manager/metadata.go create mode 100644 cli-plugins/plugin/plugin.go create mode 100755 scripts/build/plugins create mode 100755 scripts/build/plugins-osx create mode 100755 scripts/build/plugins-windows diff --git a/Makefile b/Makefile index 6f0ff4b00890..aaf5317fe60e 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,10 @@ binary: ## build executable for Linux @echo "WARNING: binary creates a Linux executable. Use cross for macOS or Windows." ./scripts/build/binary +.PHONY: plugins +plugins: ## build example CLI plugins + ./scripts/build/plugins + .PHONY: cross cross: ## build executable for macOS and Windows ./scripts/build/cross @@ -42,10 +46,18 @@ cross: ## build executable for macOS and Windows binary-windows: ## build executable for Windows ./scripts/build/windows +.PHONY: plugins-windows +plugins-windows: ## build example CLI plugins for Windows + ./scripts/build/plugins-windows + .PHONY: binary-osx binary-osx: ## build executable for macOS ./scripts/build/osx +.PHONY: plugins-osx +plugins-osx: ## build example CLI plugins for macOS + ./scripts/build/plugins-osx + .PHONY: dynbinary dynbinary: ## build dynamically linked binary ./scripts/build/dynbinary diff --git a/cli-plugins/examples/helloworld/main.go b/cli-plugins/examples/helloworld/main.go new file mode 100644 index 000000000000..a319ee623f6c --- /dev/null +++ b/cli-plugins/examples/helloworld/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "fmt" + + "github.com/docker/cli/cli-plugins/manager" + "github.com/docker/cli/cli-plugins/plugin" + "github.com/docker/cli/cli/command" + "github.com/spf13/cobra" +) + +func main() { + plugin.Run(func(dockerCli command.Cli) *cobra.Command { + goodbye := &cobra.Command{ + Use: "goodbye", + Short: "Say Goodbye instead of Hello", + Run: func(cmd *cobra.Command, _ []string) { + fmt.Fprintln(dockerCli.Out(), "Goodbye World!") + }, + } + + cmd := &cobra.Command{ + Use: "helloworld", + Short: "A basic Hello World plugin for tests", + Run: func(cmd *cobra.Command, args []string) { + fmt.Fprintln(dockerCli.Out(), "Hello World!") + }, + } + + cmd.AddCommand(goodbye) + return cmd + }, + manager.Metadata{ + SchemaVersion: "0.1.0", + Vendor: "Docker Inc.", + Version: "0.1.0", + }) +} diff --git a/cli-plugins/manager/metadata.go b/cli-plugins/manager/metadata.go new file mode 100644 index 000000000000..2d5734e8682c --- /dev/null +++ b/cli-plugins/manager/metadata.go @@ -0,0 +1,25 @@ +package manager + +const ( + // NamePrefix is the prefix required on all plugin binary names + NamePrefix = "docker-" + + // MetadataSubcommandName is the name of the plugin subcommand + // which must be supported by every plugin and returns the + // plugin metadata. + MetadataSubcommandName = "docker-cli-plugin-metadata" +) + +// Metadata provided by the plugin +type Metadata struct { + // SchemaVersion describes the version of this struct. Mandatory, must be "0.1.0" + SchemaVersion string + // Vendor is the name of the plugin vendor. Mandatory + Vendor string + // Version is the optional version of this plugin. + Version string + // ShortDescription should be suitable for a single line help message. + ShortDescription string + // URL is a pointer to the plugin's homepage. + URL string +} diff --git a/cli-plugins/plugin/plugin.go b/cli-plugins/plugin/plugin.go new file mode 100644 index 000000000000..ce2bd0bd6f50 --- /dev/null +++ b/cli-plugins/plugin/plugin.go @@ -0,0 +1,96 @@ +package plugin + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/docker/cli/cli" + "github.com/docker/cli/cli-plugins/manager" + "github.com/docker/cli/cli/command" + cliflags "github.com/docker/cli/cli/flags" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +// Run is the top-level entry point to the CLI plugin framework. It should be called from your plugin's `main()` function. +func Run(makeCmd func(command.Cli) *cobra.Command, meta manager.Metadata) { + dockerCli, err := command.NewDockerCli() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + plugin := makeCmd(dockerCli) + + cmd := newPluginCommand(dockerCli, plugin, meta) + + if err := cmd.Execute(); err != nil { + if sterr, ok := err.(cli.StatusError); ok { + if sterr.Status != "" { + fmt.Fprintln(dockerCli.Err(), sterr.Status) + } + // StatusError should only be used for errors, and all errors should + // have a non-zero exit status, so never exit with 0 + if sterr.StatusCode == 0 { + os.Exit(1) + } + os.Exit(sterr.StatusCode) + } + fmt.Fprintln(dockerCli.Err(), err) + os.Exit(1) + } +} + +func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager.Metadata) *cobra.Command { + var ( + opts *cliflags.ClientOptions + flags *pflag.FlagSet + ) + + name := plugin.Use + fullname := manager.NamePrefix + name + + cmd := &cobra.Command{ + Use: fmt.Sprintf("docker [OPTIONS] %s [ARG...]", name), + Short: fullname + " is a Docker CLI plugin", + SilenceUsage: true, + SilenceErrors: true, + TraverseChildren: true, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + // flags must be the top-level command flags, not cmd.Flags() + opts.Common.SetDefaultOptions(flags) + return dockerCli.Initialize(opts) + }, + DisableFlagsInUseLine: true, + } + opts, flags = cli.SetupPluginRootCommand(cmd) + + cmd.SetOutput(dockerCli.Out()) + + cmd.AddCommand( + plugin, + newMetadataSubcommand(plugin, meta), + ) + + cli.DisableFlagsInUseLine(cmd) + + return cmd +} + +func newMetadataSubcommand(plugin *cobra.Command, meta manager.Metadata) *cobra.Command { + if meta.ShortDescription == "" { + meta.ShortDescription = plugin.Short + } + cmd := &cobra.Command{ + Use: manager.MetadataSubcommandName, + Hidden: true, + RunE: func(cmd *cobra.Command, args []string) error { + enc := json.NewEncoder(os.Stdout) + enc.SetEscapeHTML(false) + enc.SetIndent("", " ") + return enc.Encode(meta) + }, + } + return cmd +} diff --git a/cli/cobra.go b/cli/cobra.go index ce4284a0fded..8acce9478392 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -12,6 +12,8 @@ import ( "github.com/spf13/pflag" ) +// setupCommonRootCommand contains the setup common to +// SetupRootCommand and SetupPluginRootCommand. func setupCommonRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *pflag.FlagSet) { opts := cliflags.NewClientOptions() flags := rootCmd.Flags() @@ -47,6 +49,16 @@ func SetupRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *pflag.F return opts, flags } +// SetupPluginRootCommand sets default usage, help and error handling for a plugin root command. +func SetupPluginRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *pflag.FlagSet) { + opts, flags := setupCommonRootCommand(rootCmd) + + rootCmd.PersistentFlags().BoolP("help", "", false, "Print usage") + rootCmd.PersistentFlags().Lookup("help").Hidden = true + + return opts, flags +} + // FlagErrorFunc prints an error message which matches the format of the // docker/cli/cli error messages func FlagErrorFunc(cmd *cobra.Command, err error) error { diff --git a/docker.Makefile b/docker.Makefile index 19bbe02b6b2d..6de7683c645b 100644 --- a/docker.Makefile +++ b/docker.Makefile @@ -56,6 +56,9 @@ binary: build_binary_native_image ## build the CLI build: binary ## alias for binary +plugins: build_binary_native_image ## build the CLI plugin examples + docker run --rm $(ENVVARS) $(MOUNTS) $(BINARY_NATIVE_IMAGE_NAME) ./scripts/build/plugins + .PHONY: clean clean: build_docker_image ## clean build artifacts docker run --rm $(ENVVARS) $(MOUNTS) $(DEV_DOCKER_IMAGE_NAME) make clean @@ -76,10 +79,18 @@ cross: build_cross_image ## build the CLI for macOS and Windows binary-windows: build_cross_image ## build the CLI for Windows docker run --rm $(ENVVARS) $(MOUNTS) $(CROSS_IMAGE_NAME) make $@ +.PHONY: plugins-windows +plugins-windows: build_cross_image ## build the example CLI plugins for Windows + docker run --rm $(ENVVARS) $(MOUNTS) $(CROSS_IMAGE_NAME) make $@ + .PHONY: binary-osx binary-osx: build_cross_image ## build the CLI for macOS docker run --rm $(ENVVARS) $(MOUNTS) $(CROSS_IMAGE_NAME) make $@ +.PHONY: plugins-osx +plugins-osx: build_cross_image ## build the example CLI plugins for macOS + docker run --rm $(ENVVARS) $(MOUNTS) $(CROSS_IMAGE_NAME) make $@ + .PHONY: dev dev: build_docker_image ## start a build container in interactive mode for in-container development docker run -ti --rm $(ENVVARS) $(MOUNTS) \ diff --git a/dockerfiles/Dockerfile.e2e b/dockerfiles/Dockerfile.e2e index 68368c097029..b2f57e0a1069 100644 --- a/dockerfiles/Dockerfile.e2e +++ b/dockerfiles/Dockerfile.e2e @@ -38,5 +38,6 @@ ARG VERSION ARG GITCOMMIT ENV VERSION=${VERSION} GITCOMMIT=${GITCOMMIT} RUN ./scripts/build/binary +RUN ./scripts/build/plugins CMD ./scripts/test/e2e/entry diff --git a/scripts/build/plugins b/scripts/build/plugins new file mode 100755 index 000000000000..bc14748b921f --- /dev/null +++ b/scripts/build/plugins @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# +# Build a static binary for the host OS/ARCH +# + +set -eu -o pipefail + +source ./scripts/build/.variables + +mkdir -p "build/plugins-${GOOS}-${GOARCH}" +for p in cli-plugins/examples/* ; do + [ -d "$p" ] || continue + + n=$(basename "$p") + + TARGET="build/plugins-${GOOS}-${GOARCH}/docker-${n}" + + echo "Building statically linked $TARGET" + export CGO_ENABLED=0 + go build -o "${TARGET}" --ldflags "${LDFLAGS}" "github.com/docker/cli/${p}" +done diff --git a/scripts/build/plugins-osx b/scripts/build/plugins-osx new file mode 100755 index 000000000000..8e870f4f05bb --- /dev/null +++ b/scripts/build/plugins-osx @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# +# Build a static binary for the host OS/ARCH +# + +set -eu -o pipefail + +source ./scripts/build/.variables + +export CGO_ENABLED=1 +export GOOS=darwin +export GOARCH=amd64 +export CC=o64-clang +export CXX=o64-clang++ +export LDFLAGS="$LDFLAGS -linkmode external -s" +export LDFLAGS_STATIC_DOCKER='-extld='${CC} + +source ./scripts/build/plugins diff --git a/scripts/build/plugins-windows b/scripts/build/plugins-windows new file mode 100755 index 000000000000..607ad6dc12a7 --- /dev/null +++ b/scripts/build/plugins-windows @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# +# Build a static binary for the host OS/ARCH +# + +set -eu -o pipefail + +source ./scripts/build/.variables +export CC=x86_64-w64-mingw32-gcc +export CGO_ENABLED=1 +export GOOS=windows +export GOARCH=amd64 + +source ./scripts/build/plugins From f65d5365a227cb3fddb8367a7c100ab9cec01275 Mon Sep 17 00:00:00 2001 From: Silvin Lubecki Date: Tue, 29 Jan 2019 16:21:39 +0100 Subject: [PATCH 28/60] Fix using a nil dockerCli if an error occurred during cli creation, using the standard error stream instead. Signed-off-by: Silvin Lubecki --- cmd/docker/docker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index 5909953be3db..a08cc06dc49d 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -169,7 +169,7 @@ func noArgs(cmd *cobra.Command, args []string) error { func main() { dockerCli, err := command.NewDockerCli() if err != nil { - fmt.Fprintln(dockerCli.Err(), err) + fmt.Fprintln(os.Stderr, err) os.Exit(1) } logrus.SetOutput(dockerCli.Err()) From d184c0908a1c46f125de61b12b92f648bb5fd500 Mon Sep 17 00:00:00 2001 From: Simon Ferquel Date: Mon, 28 Jan 2019 16:54:38 +0100 Subject: [PATCH 29/60] Add support for pull secrets and policies Signed-off-by: Simon Ferquel --- cli/command/stack/kubernetes/convert.go | 109 +++++++++++++++--- cli/command/stack/kubernetes/convert_test.go | 74 ++++++++++++ cli/command/stack/kubernetes/stackclient.go | 4 +- .../testdata/compose-with-pull-policy.yml | 5 + .../testdata/compose-with-pull-secret.yml | 5 + 5 files changed, 180 insertions(+), 17 deletions(-) create mode 100644 cli/command/stack/kubernetes/testdata/compose-with-pull-policy.yml create mode 100644 cli/command/stack/kubernetes/testdata/compose-with-pull-secret.yml diff --git a/cli/command/stack/kubernetes/convert.go b/cli/command/stack/kubernetes/convert.go index 21500d7c5838..a9cf5ba35149 100644 --- a/cli/command/stack/kubernetes/convert.go +++ b/cli/command/stack/kubernetes/convert.go @@ -19,14 +19,23 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +const ( + // pullSecretExtraField is an extra field on ServiceConfigs usable to reference a pull secret + pullSecretExtraField = "x-pull-secret" + // pullPolicyExtraField is an extra field on ServiceConfigs usable to specify a pull policy + pullPolicyExtraField = "x-pull-policy" +) + // NewStackConverter returns a converter from types.Config (compose) to the specified // stack version or error out if the version is not supported or existent. func NewStackConverter(version string) (StackConverter, error) { switch version { case "v1beta1": return stackV1Beta1Converter{}, nil - case "v1beta2", "v1alpha3": - return stackV1Beta2OrHigherConverter{}, nil + case "v1beta2": + return stackV1Beta2Converter{}, nil + case "v1alpha3": + return stackV1Alpha3Converter{}, nil default: return nil, errors.Errorf("stack version %s unsupported", version) } @@ -41,7 +50,7 @@ type stackV1Beta1Converter struct{} func (s stackV1Beta1Converter) FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) { cfg.Version = v1beta1.MaxComposeVersion - st, err := fromCompose(stderr, name, cfg) + st, err := fromCompose(stderr, name, cfg, v1beta1Capabilities) if err != nil { return Stack{}, err } @@ -62,16 +71,26 @@ func (s stackV1Beta1Converter) FromCompose(stderr io.Writer, name string, cfg *c return st, nil } -type stackV1Beta2OrHigherConverter struct{} +type stackV1Beta2Converter struct{} -func (s stackV1Beta2OrHigherConverter) FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) { - return fromCompose(stderr, name, cfg) +func (s stackV1Beta2Converter) FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) { + return fromCompose(stderr, name, cfg, v1beta2Capabilities) } -func fromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) { +type stackV1Alpha3Converter struct{} + +func (s stackV1Alpha3Converter) FromCompose(stderr io.Writer, name string, cfg *composetypes.Config) (Stack, error) { + return fromCompose(stderr, name, cfg, v1alpha3Capabilities) +} + +func fromCompose(stderr io.Writer, name string, cfg *composetypes.Config, capabilities composeCapabilities) (Stack, error) { + spec, err := fromComposeConfig(stderr, cfg, capabilities) + if err != nil { + return Stack{}, err + } return Stack{ Name: name, - Spec: fromComposeConfig(stderr, cfg), + Spec: spec, }, nil } @@ -95,11 +114,15 @@ func stackFromV1beta1(in *v1beta1.Stack) (Stack, error) { if err != nil { return Stack{}, err } + spec, err := fromComposeConfig(ioutil.Discard, cfg, v1beta1Capabilities) + if err != nil { + return Stack{}, err + } return Stack{ Name: in.ObjectMeta.Name, Namespace: in.ObjectMeta.Namespace, ComposeFile: in.Spec.ComposeFile, - Spec: fromComposeConfig(ioutil.Discard, cfg), + Spec: spec, }, nil } @@ -162,20 +185,24 @@ func stackToV1alpha3(s Stack) *latest.Stack { } } -func fromComposeConfig(stderr io.Writer, c *composeTypes.Config) *latest.StackSpec { +func fromComposeConfig(stderr io.Writer, c *composeTypes.Config, capabilities composeCapabilities) (*latest.StackSpec, error) { if c == nil { - return nil + return nil, nil } warnUnsupportedFeatures(stderr, c) serviceConfigs := make([]latest.ServiceConfig, len(c.Services)) for i, s := range c.Services { - serviceConfigs[i] = fromComposeServiceConfig(s) + svc, err := fromComposeServiceConfig(s, capabilities) + if err != nil { + return nil, err + } + serviceConfigs[i] = svc } return &latest.StackSpec{ Services: serviceConfigs, Secrets: fromComposeSecrets(c.Secrets), Configs: fromComposeConfigs(c.Configs), - } + }, nil } func fromComposeSecrets(s map[string]composeTypes.SecretConfig) map[string]latest.SecretConfig { @@ -216,8 +243,13 @@ func fromComposeConfigs(s map[string]composeTypes.ConfigObjConfig) map[string]la return m } -func fromComposeServiceConfig(s composeTypes.ServiceConfig) latest.ServiceConfig { - var userID *int64 +func fromComposeServiceConfig(s composeTypes.ServiceConfig, capabilities composeCapabilities) (latest.ServiceConfig, error) { + var ( + userID *int64 + pullSecret string + pullPolicy string + err error + ) if s.User != "" { numerical, err := strconv.Atoi(s.User) if err == nil { @@ -225,6 +257,20 @@ func fromComposeServiceConfig(s composeTypes.ServiceConfig) latest.ServiceConfig userID = &unixUserID } } + pullSecret, err = resolveServiceExtra(s, pullSecretExtraField) + if err != nil { + return latest.ServiceConfig{}, err + } + pullPolicy, err = resolveServiceExtra(s, pullPolicyExtraField) + if err != nil { + return latest.ServiceConfig{}, err + } + if pullSecret != "" && !capabilities.hasPullSecrets { + return latest.ServiceConfig{}, errors.Errorf("stack API version %s does not support pull secrets (field %q), please use version v1alpha3 or higher", capabilities.apiVersion, pullSecretExtraField) + } + if pullPolicy != "" && !capabilities.hasPullPolicies { + return latest.ServiceConfig{}, errors.Errorf("stack API version %s does not support pull policies (field %q), please use version v1alpha3 or higher", capabilities.apiVersion, pullPolicyExtraField) + } return latest.ServiceConfig{ Name: s.Name, CapAdd: s.CapAdd, @@ -260,7 +306,20 @@ func fromComposeServiceConfig(s composeTypes.ServiceConfig) latest.ServiceConfig User: userID, Volumes: fromComposeServiceVolumeConfig(s.Volumes), WorkingDir: s.WorkingDir, + PullSecret: pullSecret, + PullPolicy: pullPolicy, + }, nil +} + +func resolveServiceExtra(s composeTypes.ServiceConfig, field string) (string, error) { + if iface, ok := s.Extras[field]; ok { + value, ok := iface.(string) + if !ok { + return "", errors.Errorf("field %q: value %v type is %T, should be a string", field, iface, iface) + } + return value, nil } + return "", nil } func fromComposePorts(ports []composeTypes.ServicePortConfig) []latest.ServicePortConfig { @@ -421,3 +480,23 @@ func fromComposeServiceVolumeConfig(vs []composeTypes.ServiceVolumeConfig) []lat } return volumes } + +var ( + v1beta1Capabilities = composeCapabilities{ + apiVersion: "v1beta1", + } + v1beta2Capabilities = composeCapabilities{ + apiVersion: "v1beta2", + } + v1alpha3Capabilities = composeCapabilities{ + apiVersion: "v1alpha3", + hasPullSecrets: true, + hasPullPolicies: true, + } +) + +type composeCapabilities struct { + apiVersion string + hasPullSecrets bool + hasPullPolicies bool +} diff --git a/cli/command/stack/kubernetes/convert_test.go b/cli/command/stack/kubernetes/convert_test.go index 3f7f9e967c47..e8ce63a67c66 100644 --- a/cli/command/stack/kubernetes/convert_test.go +++ b/cli/command/stack/kubernetes/convert_test.go @@ -1,9 +1,13 @@ package kubernetes import ( + "fmt" + "io/ioutil" "path/filepath" "testing" + "github.com/docker/cli/cli/compose/loader" + composetypes "github.com/docker/cli/cli/compose/types" "github.com/docker/compose-on-kubernetes/api/compose/v1alpha3" "github.com/docker/compose-on-kubernetes/api/compose/v1beta1" "github.com/docker/compose-on-kubernetes/api/compose/v1beta2" @@ -161,3 +165,73 @@ func TestConvertFromToV1alpha3(t *testing.T) { gotBack := stackToV1alpha3(result) assert.DeepEqual(t, stackv1alpha3, gotBack) } + +func loadTestStackWith(t *testing.T, with string) *composetypes.Config { + t.Helper() + filePath := fmt.Sprintf("testdata/compose-with-%s.yml", with) + data, err := ioutil.ReadFile(filePath) + assert.NilError(t, err) + yamlData, err := loader.ParseYAML(data) + assert.NilError(t, err) + cfg, err := loader.Load(composetypes.ConfigDetails{ + ConfigFiles: []composetypes.ConfigFile{ + {Config: yamlData, Filename: filePath}, + }, + }) + assert.NilError(t, err) + return cfg +} + +func TestHandlePullSecret(t *testing.T) { + testData := loadTestStackWith(t, "pull-secret") + cases := []struct { + version string + err string + }{ + {version: "v1beta1", err: `stack API version v1beta1 does not support pull secrets (field "x-pull-secret"), please use version v1alpha3 or higher`}, + {version: "v1beta2", err: `stack API version v1beta2 does not support pull secrets (field "x-pull-secret"), please use version v1alpha3 or higher`}, + {version: "v1alpha3"}, + } + + for _, c := range cases { + t.Run(c.version, func(t *testing.T) { + conv, err := NewStackConverter(c.version) + assert.NilError(t, err) + s, err := conv.FromCompose(ioutil.Discard, "test", testData) + if c.err != "" { + assert.Error(t, err, c.err) + + } else { + assert.NilError(t, err) + assert.Equal(t, s.Spec.Services[0].PullSecret, "some-secret") + } + }) + } +} + +func TestHandlePullPolicy(t *testing.T) { + testData := loadTestStackWith(t, "pull-policy") + cases := []struct { + version string + err string + }{ + {version: "v1beta1", err: `stack API version v1beta1 does not support pull policies (field "x-pull-policy"), please use version v1alpha3 or higher`}, + {version: "v1beta2", err: `stack API version v1beta2 does not support pull policies (field "x-pull-policy"), please use version v1alpha3 or higher`}, + {version: "v1alpha3"}, + } + + for _, c := range cases { + t.Run(c.version, func(t *testing.T) { + conv, err := NewStackConverter(c.version) + assert.NilError(t, err) + s, err := conv.FromCompose(ioutil.Discard, "test", testData) + if c.err != "" { + assert.Error(t, err, c.err) + + } else { + assert.NilError(t, err) + assert.Equal(t, s.Spec.Services[0].PullPolicy, "Never") + } + }) + } +} diff --git a/cli/command/stack/kubernetes/stackclient.go b/cli/command/stack/kubernetes/stackclient.go index 53670e4d80c4..5ce4480ebb00 100644 --- a/cli/command/stack/kubernetes/stackclient.go +++ b/cli/command/stack/kubernetes/stackclient.go @@ -125,7 +125,7 @@ func verify(services corev1.ServiceInterface, stackName string, service string) // stackV1Beta2 implements stackClient interface and talks to compose component v1beta2. type stackV1Beta2 struct { - stackV1Beta2OrHigherConverter + stackV1Beta2Converter stacks composev1beta2.StackInterface } @@ -203,7 +203,7 @@ func (s *stackV1Beta2) IsColliding(servicesClient corev1.ServiceInterface, st St // stackV1Beta2 implements stackClient interface and talks to compose component v1beta2. type stackV1Alpha3 struct { - stackV1Beta2OrHigherConverter + stackV1Alpha3Converter stacks composev1alpha3.StackInterface } diff --git a/cli/command/stack/kubernetes/testdata/compose-with-pull-policy.yml b/cli/command/stack/kubernetes/testdata/compose-with-pull-policy.yml new file mode 100644 index 000000000000..328416ee1c9d --- /dev/null +++ b/cli/command/stack/kubernetes/testdata/compose-with-pull-policy.yml @@ -0,0 +1,5 @@ +version: "3.7" +services: + test: + image: "some-image" + x-pull-policy: "Never" \ No newline at end of file diff --git a/cli/command/stack/kubernetes/testdata/compose-with-pull-secret.yml b/cli/command/stack/kubernetes/testdata/compose-with-pull-secret.yml new file mode 100644 index 000000000000..8510f5c541b0 --- /dev/null +++ b/cli/command/stack/kubernetes/testdata/compose-with-pull-secret.yml @@ -0,0 +1,5 @@ +version: "3.7" +services: + test: + image: "some-private-image" + x-pull-secret: "some-secret" \ No newline at end of file From 99fb2c1baa2b8edd87ae10b1fc6f65aebe7a18e7 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Wed, 30 Jan 2019 01:06:09 +0100 Subject: [PATCH 30/60] Hide "builder" and "network" commands on old API versions - The `/build/prune` endpoint was added in API v1.31 - The `/network` endpoints were added in API v1.21 This patch hides these commands on older API versions Before this change: ``` DOCKER_API_VERSION=1.0 docker ... Management Commands: builder Manage builds container Manage containers image Manage images manifest Manage Docker image manifests and manifest lists network Manage networks system Manage Docker trust Manage trust on Docker images ``` After this change ``` DOCKER_API_VERSION=1.0 docker ... Management Commands: container Manage containers image Manage images manifest Manage Docker image manifests and manifest lists system Manage Docker trust Manage trust on Docker images ``` Signed-off-by: Sebastiaan van Stijn --- cli/command/builder/cmd.go | 9 +++++---- cli/command/network/cmd.go | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cli/command/builder/cmd.go b/cli/command/builder/cmd.go index 288322afa365..b5f4d0d9eb96 100644 --- a/cli/command/builder/cmd.go +++ b/cli/command/builder/cmd.go @@ -10,10 +10,11 @@ import ( // NewBuilderCommand returns a cobra command for `builder` subcommands func NewBuilderCommand(dockerCli command.Cli) *cobra.Command { cmd := &cobra.Command{ - Use: "builder", - Short: "Manage builds", - Args: cli.NoArgs, - RunE: command.ShowHelp(dockerCli.Err()), + Use: "builder", + Short: "Manage builds", + Args: cli.NoArgs, + RunE: command.ShowHelp(dockerCli.Err()), + Annotations: map[string]string{"version": "1.31"}, } cmd.AddCommand( NewPruneCommand(dockerCli), diff --git a/cli/command/network/cmd.go b/cli/command/network/cmd.go index 48edf1c4e34e..028e1377f07f 100644 --- a/cli/command/network/cmd.go +++ b/cli/command/network/cmd.go @@ -10,10 +10,11 @@ import ( // NewNetworkCommand returns a cobra command for `network` subcommands func NewNetworkCommand(dockerCli command.Cli) *cobra.Command { cmd := &cobra.Command{ - Use: "network", - Short: "Manage networks", - Args: cli.NoArgs, - RunE: command.ShowHelp(dockerCli.Err()), + Use: "network", + Short: "Manage networks", + Args: cli.NoArgs, + RunE: command.ShowHelp(dockerCli.Err()), + Annotations: map[string]string{"version": "1.21"}, } cmd.AddCommand( newConnectCommand(dockerCli), From f1f31abbe5c69e258095118f3e8060b82b445d09 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Tue, 11 Dec 2018 14:03:47 +0000 Subject: [PATCH 31/60] Add support for running a CLI plugin Also includes the scaffolding for finding a validating plugin candidates. Argument validation is moved to RunE to support this, so `noArgs` is removed. Signed-off-by: Ian Campbell --- cli-plugins/examples/helloworld/main.go | 2 +- cli-plugins/manager/candidate.go | 23 ++++++ cli-plugins/manager/candidate_test.go | 90 ++++++++++++++++++++ cli-plugins/manager/manager.go | 93 +++++++++++++++++++++ cli-plugins/manager/manager_test.go | 36 ++++++++ cli-plugins/manager/manager_unix.go | 8 ++ cli-plugins/manager/manager_windows.go | 10 +++ cli-plugins/manager/plugin.go | 104 ++++++++++++++++++++++++ cli/config/configfile/file.go | 1 + cmd/docker/docker.go | 24 +++--- 10 files changed, 380 insertions(+), 11 deletions(-) create mode 100644 cli-plugins/manager/candidate.go create mode 100644 cli-plugins/manager/candidate_test.go create mode 100644 cli-plugins/manager/manager.go create mode 100644 cli-plugins/manager/manager_test.go create mode 100644 cli-plugins/manager/manager_unix.go create mode 100644 cli-plugins/manager/manager_windows.go create mode 100644 cli-plugins/manager/plugin.go diff --git a/cli-plugins/examples/helloworld/main.go b/cli-plugins/examples/helloworld/main.go index a319ee623f6c..e79e32ce5436 100644 --- a/cli-plugins/examples/helloworld/main.go +++ b/cli-plugins/examples/helloworld/main.go @@ -33,6 +33,6 @@ func main() { manager.Metadata{ SchemaVersion: "0.1.0", Vendor: "Docker Inc.", - Version: "0.1.0", + Version: "testing", }) } diff --git a/cli-plugins/manager/candidate.go b/cli-plugins/manager/candidate.go new file mode 100644 index 000000000000..2000e5b142f6 --- /dev/null +++ b/cli-plugins/manager/candidate.go @@ -0,0 +1,23 @@ +package manager + +import ( + "os/exec" +) + +// Candidate represents a possible plugin candidate, for mocking purposes +type Candidate interface { + Path() string + Metadata() ([]byte, error) +} + +type candidate struct { + path string +} + +func (c *candidate) Path() string { + return c.path +} + +func (c *candidate) Metadata() ([]byte, error) { + return exec.Command(c.path, MetadataSubcommandName).Output() +} diff --git a/cli-plugins/manager/candidate_test.go b/cli-plugins/manager/candidate_test.go new file mode 100644 index 000000000000..11384226a5d4 --- /dev/null +++ b/cli-plugins/manager/candidate_test.go @@ -0,0 +1,90 @@ +package manager + +import ( + "fmt" + "strings" + "testing" + + "github.com/spf13/cobra" + "gotest.tools/assert" +) + +type fakeCandidate struct { + path string + exec bool + meta string +} + +func (c *fakeCandidate) Path() string { + return c.path +} + +func (c *fakeCandidate) Metadata() ([]byte, error) { + if !c.exec { + return nil, fmt.Errorf("faked a failure to exec %q", c.path) + } + return []byte(c.meta), nil +} + +func TestValidateCandidate(t *testing.T) { + var ( + goodPluginName = NamePrefix + "goodplugin" + + builtinName = NamePrefix + "builtin" + builtinAlias = NamePrefix + "alias" + + badPrefixPath = "/usr/local/libexec/cli-plugins/wobble" + badNamePath = "/usr/local/libexec/cli-plugins/docker-123456" + goodPluginPath = "/usr/local/libexec/cli-plugins/" + goodPluginName + ) + + fakeroot := &cobra.Command{Use: "docker"} + fakeroot.AddCommand(&cobra.Command{ + Use: strings.TrimPrefix(builtinName, NamePrefix), + Aliases: []string{ + strings.TrimPrefix(builtinAlias, NamePrefix), + }, + }) + + for _, tc := range []struct { + c *fakeCandidate + + // Either err or invalid may be non-empty, but not both (both can be empty for a good plugin). + err string + invalid string + }{ + /* Each failing one of the tests */ + {c: &fakeCandidate{path: ""}, err: "plugin candidate path cannot be empty"}, + {c: &fakeCandidate{path: badPrefixPath}, err: fmt.Sprintf("does not have %q prefix", NamePrefix)}, + {c: &fakeCandidate{path: badNamePath}, invalid: "did not match"}, + {c: &fakeCandidate{path: builtinName}, invalid: `plugin "builtin" duplicates builtin command`}, + {c: &fakeCandidate{path: builtinAlias}, invalid: `plugin "alias" duplicates an alias of builtin command "builtin"`}, + {c: &fakeCandidate{path: goodPluginPath, exec: false}, invalid: fmt.Sprintf("failed to fetch metadata: faked a failure to exec %q", goodPluginPath)}, + {c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `xyzzy`}, invalid: "invalid character"}, + {c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{}`}, invalid: `plugin SchemaVersion "" is not valid`}, + {c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{"SchemaVersion": "xyzzy"}`}, invalid: `plugin SchemaVersion "xyzzy" is not valid`}, + {c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{"SchemaVersion": "0.1.0"}`}, invalid: "plugin metadata does not define a vendor"}, + {c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{"SchemaVersion": "0.1.0", "Vendor": ""}`}, invalid: "plugin metadata does not define a vendor"}, + // This one should work + {c: &fakeCandidate{path: goodPluginPath, exec: true, meta: `{"SchemaVersion": "0.1.0", "Vendor": "e2e-testing"}`}}, + } { + p, err := newPlugin(tc.c, fakeroot) + if tc.err != "" { + assert.ErrorContains(t, err, tc.err) + } else if tc.invalid != "" { + assert.NilError(t, err) + assert.ErrorContains(t, p.Err, tc.invalid) + } else { + assert.NilError(t, err) + assert.Equal(t, NamePrefix+p.Name, goodPluginName) + assert.Equal(t, p.SchemaVersion, "0.1.0") + assert.Equal(t, p.Vendor, "e2e-testing") + } + } +} + +func TestCandidatePath(t *testing.T) { + exp := "/some/path" + cand := &candidate{path: exp} + assert.Equal(t, exp, cand.Path()) +} diff --git a/cli-plugins/manager/manager.go b/cli-plugins/manager/manager.go new file mode 100644 index 000000000000..882d2ee2ea98 --- /dev/null +++ b/cli-plugins/manager/manager.go @@ -0,0 +1,93 @@ +package manager + +import ( + "os" + "os/exec" + "path/filepath" + "runtime" + + "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/config" + "github.com/spf13/cobra" +) + +// errPluginNotFound is the error returned when a plugin could not be found. +type errPluginNotFound string + +func (e errPluginNotFound) NotFound() {} + +func (e errPluginNotFound) Error() string { + return "Error: No such CLI plugin: " + string(e) +} + +type notFound interface{ NotFound() } + +// IsNotFound is true if the given error is due to a plugin not being found. +func IsNotFound(err error) bool { + _, ok := err.(notFound) + return ok +} + +var defaultUserPluginDir = config.Path("cli-plugins") + +func getPluginDirs(dockerCli command.Cli) []string { + var pluginDirs []string + + if cfg := dockerCli.ConfigFile(); cfg != nil { + pluginDirs = append(pluginDirs, cfg.CLIPluginsExtraDirs...) + } + pluginDirs = append(pluginDirs, defaultUserPluginDir) + pluginDirs = append(pluginDirs, defaultSystemPluginDirs...) + return pluginDirs +} + +// PluginRunCommand returns an "os/exec".Cmd which when .Run() will execute the named plugin. +// The rootcmd argument is referenced to determine the set of builtin commands in order to detect conficts. +// The error returned satisfies the IsNotFound() predicate if no plugin was found or if the first candidate plugin was invalid somehow. +func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command) (*exec.Cmd, error) { + // This uses the full original args, not the args which may + // have been provided by cobra to our caller. This is because + // they lack e.g. global options which we must propagate here. + args := os.Args[1:] + if !pluginNameRe.MatchString(name) { + // We treat this as "not found" so that callers will + // fallback to their "invalid" command path. + return nil, errPluginNotFound(name) + } + exename := NamePrefix + name + if runtime.GOOS == "windows" { + exename = exename + ".exe" + } + for _, d := range getPluginDirs(dockerCli) { + path := filepath.Join(d, exename) + + // We stat here rather than letting the exec tell us + // ENOENT because the latter does not distinguish a + // file not existing from its dynamic loader or one of + // its libraries not existing. + if _, err := os.Stat(path); os.IsNotExist(err) { + continue + } + + c := &candidate{path: path} + plugin, err := newPlugin(c, rootcmd) + if err != nil { + return nil, err + } + if plugin.Err != nil { + return nil, errPluginNotFound(name) + } + cmd := exec.Command(plugin.Path, args...) + // Using dockerCli.{In,Out,Err}() here results in a hang until something is input. + // See: - https://github.com/golang/go/issues/10338 + // - https://github.com/golang/go/commit/d000e8742a173aa0659584aa01b7ba2834ba28ab + // os.Stdin is a *os.File which avoids this behaviour. We don't need the functionality + // of the wrappers here anyway. + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return cmd, nil + } + return nil, errPluginNotFound(name) +} diff --git a/cli-plugins/manager/manager_test.go b/cli-plugins/manager/manager_test.go new file mode 100644 index 000000000000..3a62b911fab1 --- /dev/null +++ b/cli-plugins/manager/manager_test.go @@ -0,0 +1,36 @@ +package manager + +import ( + "strings" + "testing" + + "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/internal/test" + "gotest.tools/assert" +) + +func TestErrPluginNotFound(t *testing.T) { + var err error = errPluginNotFound("test") + err.(errPluginNotFound).NotFound() + assert.Error(t, err, "Error: No such CLI plugin: test") + assert.Assert(t, IsNotFound(err)) + assert.Assert(t, !IsNotFound(nil)) +} + +func TestGetPluginDirs(t *testing.T) { + cli := test.NewFakeCli(nil) + + expected := []string{defaultUserPluginDir} + expected = append(expected, defaultSystemPluginDirs...) + + assert.Equal(t, strings.Join(expected, ":"), strings.Join(getPluginDirs(cli), ":")) + + extras := []string{ + "foo", "bar", "baz", + } + expected = append(extras, expected...) + cli.SetConfigFile(&configfile.ConfigFile{ + CLIPluginsExtraDirs: extras, + }) + assert.DeepEqual(t, expected, getPluginDirs(cli)) +} diff --git a/cli-plugins/manager/manager_unix.go b/cli-plugins/manager/manager_unix.go new file mode 100644 index 000000000000..f586acbd8da2 --- /dev/null +++ b/cli-plugins/manager/manager_unix.go @@ -0,0 +1,8 @@ +// +build !windows + +package manager + +var defaultSystemPluginDirs = []string{ + "/usr/local/lib/docker/cli-plugins", "/usr/local/libexec/docker/cli-plugins", + "/usr/lib/docker/cli-plugins", "/usr/libexec/docker/cli-plugins", +} diff --git a/cli-plugins/manager/manager_windows.go b/cli-plugins/manager/manager_windows.go new file mode 100644 index 000000000000..b62868580360 --- /dev/null +++ b/cli-plugins/manager/manager_windows.go @@ -0,0 +1,10 @@ +package manager + +import ( + "os" + "path/filepath" +) + +var defaultSystemPluginDirs = []string{ + filepath.Join(os.Getenv("ProgramData"), "Docker", "cli-plugins"), +} diff --git a/cli-plugins/manager/plugin.go b/cli-plugins/manager/plugin.go new file mode 100644 index 000000000000..8a07c5853726 --- /dev/null +++ b/cli-plugins/manager/plugin.go @@ -0,0 +1,104 @@ +package manager + +import ( + "encoding/json" + "path/filepath" + "regexp" + "runtime" + "strings" + + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + pluginNameRe = regexp.MustCompile("^[a-z][a-z0-9]*$") +) + +// Plugin represents a potential plugin with all it's metadata. +type Plugin struct { + Metadata + + Name string + Path string + + // Err is non-nil if the plugin failed one of the candidate tests. + Err error `json:",omitempty"` + + // ShadowedPaths contains the paths of any other plugins which this plugin takes precedence over. + ShadowedPaths []string `json:",omitempty"` +} + +// newPlugin determines if the given candidate is valid and returns a +// Plugin. If the candidate fails one of the tests then `Plugin.Err` +// is set, but the `Plugin` is still returned with no error. An error +// is only returned due to a non-recoverable error. +func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) { + path := c.Path() + if path == "" { + return Plugin{}, errors.New("plugin candidate path cannot be empty") + } + + // The candidate listing process should have skipped anything + // which would fail here, so there are all real errors. + fullname := filepath.Base(path) + if fullname == "." { + return Plugin{}, errors.Errorf("unable to determine basename of plugin candidate %q", path) + } + if runtime.GOOS == "windows" { + exe := ".exe" + if !strings.HasSuffix(fullname, exe) { + return Plugin{}, errors.Errorf("plugin candidate %q lacks required %q suffix", path, exe) + } + fullname = strings.TrimSuffix(fullname, exe) + } + if !strings.HasPrefix(fullname, NamePrefix) { + return Plugin{}, errors.Errorf("plugin candidate %q does not have %q prefix", path, NamePrefix) + } + + p := Plugin{ + Name: strings.TrimPrefix(fullname, NamePrefix), + Path: path, + } + + // Now apply the candidate tests, so these update p.Err. + if !pluginNameRe.MatchString(p.Name) { + p.Err = errors.Errorf("plugin candidate %q did not match %q", p.Name, pluginNameRe.String()) + return p, nil + } + + if rootcmd != nil { + for _, cmd := range rootcmd.Commands() { + if cmd.Name() == p.Name { + p.Err = errors.Errorf("plugin %q duplicates builtin command", p.Name) + return p, nil + } + if cmd.HasAlias(p.Name) { + p.Err = errors.Errorf("plugin %q duplicates an alias of builtin command %q", p.Name, cmd.Name()) + return p, nil + } + } + } + + // We are supposed to check for relevant execute permissions here. Instead we rely on an attempt to execute. + meta, err := c.Metadata() + if err != nil { + p.Err = errors.Wrap(err, "failed to fetch metadata") + return p, nil + } + + if err := json.Unmarshal(meta, &p.Metadata); err != nil { + p.Err = errors.Wrap(err, "invalid metadata") + return p, nil + } + + if p.Metadata.SchemaVersion != "0.1.0" { + p.Err = errors.Errorf("plugin SchemaVersion %q is not valid, must be 0.1.0", p.Metadata.SchemaVersion) + return p, nil + } + if p.Metadata.Vendor == "" { + p.Err = errors.Errorf("plugin metadata does not define a vendor") + return p, nil + } + return p, nil +} diff --git a/cli/config/configfile/file.go b/cli/config/configfile/file.go index d815570362a8..99ffd47a5fcb 100644 --- a/cli/config/configfile/file.go +++ b/cli/config/configfile/file.go @@ -49,6 +49,7 @@ type ConfigFile struct { StackOrchestrator string `json:"stackOrchestrator,omitempty"` Kubernetes *KubernetesConfig `json:"kubernetes,omitempty"` CurrentContext string `json:"currentContext,omitempty"` + CLIPluginsExtraDirs []string `json:"cliPluginsExtraDirs,omitempty"` } // ProxyConfig contains proxy configuration settings diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index f8890304ecfb..062cbfdc9b73 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/docker/cli/cli" + pluginmanager "github.com/docker/cli/cli-plugins/manager" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/commands" cliflags "github.com/docker/cli/cli/flags" @@ -30,9 +31,20 @@ func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command { SilenceUsage: true, SilenceErrors: true, TraverseChildren: true, - Args: noArgs, RunE: func(cmd *cobra.Command, args []string) error { - return command.ShowHelp(dockerCli.Err())(cmd, args) + if len(args) == 0 { + return command.ShowHelp(dockerCli.Err())(cmd, args) + } + plugincmd, err := pluginmanager.PluginRunCommand(dockerCli, args[0], cmd) + if pluginmanager.IsNotFound(err) { + return fmt.Errorf( + "docker: '%s' is not a docker command.\nSee 'docker --help'", args[0]) + } + if err != nil { + return err + } + + return plugincmd.Run() }, PersistentPreRunE: func(cmd *cobra.Command, args []string) error { // flags must be the top-level command flags, not cmd.Flags() @@ -136,14 +148,6 @@ func initializeDockerCli(dockerCli *command.DockerCli, flags *pflag.FlagSet, opt return dockerCli.Initialize(opts) } -func noArgs(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return nil - } - return fmt.Errorf( - "docker: '%s' is not a docker command.\nSee 'docker --help'", args[0]) -} - func main() { dockerCli, err := command.NewDockerCli() if err != nil { From 5db336798c2fc17eb20f049508bb291db8a69d71 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Tue, 11 Dec 2018 14:15:04 +0000 Subject: [PATCH 32/60] Add some simple e2e tests for executing CLI plugins To help with this add a bad plugin which produces invalid metadata and arrange for it to be built in the e2e container. Signed-off-by: Ian Campbell --- dockerfiles/Dockerfile.e2e | 2 +- e2e/cli-plugins/plugins/badmeta/main.go | 19 ++++++ e2e/cli-plugins/run_test.go | 60 +++++++++++++++++++ .../testdata/docker-badmeta-err.golden | 2 + .../testdata/docker-nonexistent-err.golden | 2 + e2e/cli-plugins/util_test.go | 24 ++++++++ scripts/build/plugins | 2 +- scripts/test/e2e/run | 1 + 8 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 e2e/cli-plugins/plugins/badmeta/main.go create mode 100644 e2e/cli-plugins/run_test.go create mode 100644 e2e/cli-plugins/testdata/docker-badmeta-err.golden create mode 100644 e2e/cli-plugins/testdata/docker-nonexistent-err.golden create mode 100644 e2e/cli-plugins/util_test.go diff --git a/dockerfiles/Dockerfile.e2e b/dockerfiles/Dockerfile.e2e index b2f57e0a1069..eedda3b7f59d 100644 --- a/dockerfiles/Dockerfile.e2e +++ b/dockerfiles/Dockerfile.e2e @@ -38,6 +38,6 @@ ARG VERSION ARG GITCOMMIT ENV VERSION=${VERSION} GITCOMMIT=${GITCOMMIT} RUN ./scripts/build/binary -RUN ./scripts/build/plugins +RUN ./scripts/build/plugins e2e/cli-plugins/plugins/* CMD ./scripts/test/e2e/entry diff --git a/e2e/cli-plugins/plugins/badmeta/main.go b/e2e/cli-plugins/plugins/badmeta/main.go new file mode 100644 index 000000000000..10bd0eff7263 --- /dev/null +++ b/e2e/cli-plugins/plugins/badmeta/main.go @@ -0,0 +1,19 @@ +package main + +// This is not a real plugin, but just returns malformated metadata +// from the subcommand and otherwise exits with failure. + +import ( + "fmt" + "os" + + "github.com/docker/cli/cli-plugins/manager" +) + +func main() { + if len(os.Args) == 2 && os.Args[1] == manager.MetadataSubcommandName { + fmt.Println(`{invalid-json}`) + os.Exit(0) + } + os.Exit(1) +} diff --git a/e2e/cli-plugins/run_test.go b/e2e/cli-plugins/run_test.go new file mode 100644 index 000000000000..e4cd94d59ad6 --- /dev/null +++ b/e2e/cli-plugins/run_test.go @@ -0,0 +1,60 @@ +package cliplugins + +import ( + "testing" + + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/golden" + "gotest.tools/icmd" +) + +// TestRunNonexisting ensures correct behaviour when running a nonexistent plugin. +func TestRunNonexisting(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("nonexistent")) + res.Assert(t, icmd.Expected{ + ExitCode: 1, + }) + assert.Assert(t, is.Equal(res.Stdout(), "")) + golden.Assert(t, res.Stderr(), "docker-nonexistent-err.golden") +} + +// TestRunBad ensures correct behaviour when running an existent but invalid plugin +func TestRunBad(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("badmeta")) + res.Assert(t, icmd.Expected{ + ExitCode: 1, + }) + assert.Assert(t, is.Equal(res.Stdout(), "")) + golden.Assert(t, res.Stderr(), "docker-badmeta-err.golden") +} + +// TestRunGood ensures correct behaviour when running a valid plugin +func TestRunGood(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("helloworld")) + res.Assert(t, icmd.Expected{ + ExitCode: 0, + Out: "Hello World!", + }) +} + +// TestRunGoodSubcommand ensures correct behaviour when running a valid plugin with a subcommand +func TestRunGoodSubcommand(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("helloworld", "goodbye")) + res.Assert(t, icmd.Expected{ + ExitCode: 0, + Out: "Goodbye World!", + }) +} diff --git a/e2e/cli-plugins/testdata/docker-badmeta-err.golden b/e2e/cli-plugins/testdata/docker-badmeta-err.golden new file mode 100644 index 000000000000..df2344638af7 --- /dev/null +++ b/e2e/cli-plugins/testdata/docker-badmeta-err.golden @@ -0,0 +1,2 @@ +docker: 'badmeta' is not a docker command. +See 'docker --help' diff --git a/e2e/cli-plugins/testdata/docker-nonexistent-err.golden b/e2e/cli-plugins/testdata/docker-nonexistent-err.golden new file mode 100644 index 000000000000..f2265f6b9ecd --- /dev/null +++ b/e2e/cli-plugins/testdata/docker-nonexistent-err.golden @@ -0,0 +1,2 @@ +docker: 'nonexistent' is not a docker command. +See 'docker --help' diff --git a/e2e/cli-plugins/util_test.go b/e2e/cli-plugins/util_test.go new file mode 100644 index 000000000000..0ab1aa956687 --- /dev/null +++ b/e2e/cli-plugins/util_test.go @@ -0,0 +1,24 @@ +package cliplugins + +import ( + "fmt" + "os" + "testing" + + "gotest.tools/fs" + "gotest.tools/icmd" +) + +func prepare(t *testing.T) (func(args ...string) icmd.Cmd, func()) { + cfg := fs.NewDir(t, "plugin-test", + fs.WithFile("config.json", fmt.Sprintf(`{"cliPluginsExtraDirs": [%q]}`, os.Getenv("DOCKER_CLI_E2E_PLUGINS_EXTRA_DIRS"))), + ) + run := func(args ...string) icmd.Cmd { + return icmd.Command("docker", append([]string{"--config", cfg.Path()}, args...)...) + } + cleanup := func() { + cfg.Remove() + } + return run, cleanup + +} diff --git a/scripts/build/plugins b/scripts/build/plugins index bc14748b921f..fce2689cdc25 100755 --- a/scripts/build/plugins +++ b/scripts/build/plugins @@ -8,7 +8,7 @@ set -eu -o pipefail source ./scripts/build/.variables mkdir -p "build/plugins-${GOOS}-${GOARCH}" -for p in cli-plugins/examples/* ; do +for p in cli-plugins/examples/* "$@" ; do [ -d "$p" ] || continue n=$(basename "$p") diff --git a/scripts/test/e2e/run b/scripts/test/e2e/run index 7a401db9ce1c..d494019419fb 100755 --- a/scripts/test/e2e/run +++ b/scripts/test/e2e/run @@ -69,6 +69,7 @@ function runtests { TEST_SKIP_PLUGIN_TESTS="${SKIP_PLUGIN_TESTS-}" \ GOPATH="$GOPATH" \ PATH="$PWD/build/:/usr/bin" \ + DOCKER_CLI_E2E_PLUGINS_EXTRA_DIRS="$PWD/build/plugins-linux-amd64" \ "$(which go)" test -v ./e2e/... ${TESTFLAGS-} } From f912b55bd13bd414e4619c4804424d3c3ddb5b15 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Tue, 11 Dec 2018 14:50:04 +0000 Subject: [PATCH 33/60] Integrate CLI plugins into `docker help` output. To do this we add a stub `cobra.Command` for each installed plugin (only when invoking `help`, not for normal running). This requires a function to list all available plugins so that is added here. Signed-off-by: Ian Campbell --- cli-plugins/manager/cobra.go | 57 +++++++++++++++++++++ cli-plugins/manager/manager.go | 77 +++++++++++++++++++++++++++++ cli-plugins/manager/manager_test.go | 71 ++++++++++++++++++++++++++ cli-plugins/manager/plugin.go | 6 +++ cli/cobra.go | 34 ++++++++++--- cli/cobra_test.go | 27 ++++++++++ cmd/docker/docker.go | 36 ++++++++++++-- e2e/cli-plugins/help_test.go | 54 ++++++++++++++++++++ 8 files changed, 351 insertions(+), 11 deletions(-) create mode 100644 cli-plugins/manager/cobra.go create mode 100644 e2e/cli-plugins/help_test.go diff --git a/cli-plugins/manager/cobra.go b/cli-plugins/manager/cobra.go new file mode 100644 index 000000000000..302d338a1c99 --- /dev/null +++ b/cli-plugins/manager/cobra.go @@ -0,0 +1,57 @@ +package manager + +import ( + "github.com/docker/cli/cli/command" + "github.com/spf13/cobra" +) + +const ( + // CommandAnnotationPlugin is added to every stub command added by + // AddPluginCommandStubs with the value "true" and so can be + // used to distinguish plugin stubs from regular commands. + CommandAnnotationPlugin = "com.docker.cli.plugin" + + // CommandAnnotationPluginVendor is added to every stub command + // added by AddPluginCommandStubs and contains the vendor of + // that plugin. + CommandAnnotationPluginVendor = "com.docker.cli.plugin.vendor" + + // CommandAnnotationPluginInvalid is added to any stub command + // added by AddPluginCommandStubs for an invalid command (that + // is, one which failed it's candidate test) and contains the + // reason for the failure. + CommandAnnotationPluginInvalid = "com.docker.cli.plugin-invalid" +) + +// AddPluginCommandStubs adds a stub cobra.Commands for each plugin +// (optionally including invalid ones). The command stubs will have +// several annotations added, see `CommandAnnotationPlugin*`. +func AddPluginCommandStubs(dockerCli command.Cli, cmd *cobra.Command, includeInvalid bool) error { + plugins, err := ListPlugins(dockerCli, cmd) + if err != nil { + return err + } + for _, p := range plugins { + if !includeInvalid && p.Err != nil { + continue + } + vendor := p.Vendor + if vendor == "" { + vendor = "unknown" + } + annotations := map[string]string{ + CommandAnnotationPlugin: "true", + CommandAnnotationPluginVendor: vendor, + } + if p.Err != nil { + annotations[CommandAnnotationPluginInvalid] = p.Err.Error() + } + cmd.AddCommand(&cobra.Command{ + Use: p.Name, + Short: p.ShortDescription, + Run: func(_ *cobra.Command, _ []string) {}, + Annotations: annotations, + }) + } + return nil +} diff --git a/cli-plugins/manager/manager.go b/cli-plugins/manager/manager.go index 882d2ee2ea98..b6ab943595e7 100644 --- a/cli-plugins/manager/manager.go +++ b/cli-plugins/manager/manager.go @@ -1,10 +1,12 @@ package manager import ( + "io/ioutil" "os" "os/exec" "path/filepath" "runtime" + "strings" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/config" @@ -41,6 +43,81 @@ func getPluginDirs(dockerCli command.Cli) []string { return pluginDirs } +func addPluginCandidatesFromDir(res map[string][]string, d string) error { + dentries, err := ioutil.ReadDir(d) + if err != nil { + return err + } + for _, dentry := range dentries { + switch dentry.Mode() & os.ModeType { + case 0, os.ModeSymlink: + // Regular file or symlink, keep going + default: + // Something else, ignore. + continue + } + name := dentry.Name() + if !strings.HasPrefix(name, NamePrefix) { + continue + } + name = strings.TrimPrefix(name, NamePrefix) + if runtime.GOOS == "windows" { + exe := ".exe" + if !strings.HasSuffix(name, exe) { + continue + } + name = strings.TrimSuffix(name, exe) + } + res[name] = append(res[name], filepath.Join(d, dentry.Name())) + } + return nil +} + +// listPluginCandidates returns a map from plugin name to the list of (unvalidated) Candidates. The list is in descending order of priority. +func listPluginCandidates(dirs []string) (map[string][]string, error) { + result := make(map[string][]string) + for _, d := range dirs { + // Silently ignore any directories which we cannot + // Stat (e.g. due to permissions or anything else) or + // which is not a directory. + if fi, err := os.Stat(d); err != nil || !fi.IsDir() { + continue + } + if err := addPluginCandidatesFromDir(result, d); err != nil { + // Silently ignore paths which don't exist. + if os.IsNotExist(err) { + continue + } + return nil, err // Or return partial result? + } + } + return result, nil +} + +// ListPlugins produces a list of the plugins available on the system +func ListPlugins(dockerCli command.Cli, rootcmd *cobra.Command) ([]Plugin, error) { + candidates, err := listPluginCandidates(getPluginDirs(dockerCli)) + if err != nil { + return nil, err + } + + var plugins []Plugin + for _, paths := range candidates { + if len(paths) == 0 { + continue + } + c := &candidate{paths[0]} + p, err := newPlugin(c, rootcmd) + if err != nil { + return nil, err + } + p.ShadowedPaths = paths[1:] + plugins = append(plugins, p) + } + + return plugins, nil +} + // PluginRunCommand returns an "os/exec".Cmd which when .Run() will execute the named plugin. // The rootcmd argument is referenced to determine the set of builtin commands in order to detect conficts. // The error returned satisfies the IsNotFound() predicate if no plugin was found or if the first candidate plugin was invalid somehow. diff --git a/cli-plugins/manager/manager_test.go b/cli-plugins/manager/manager_test.go index 3a62b911fab1..450ae6120071 100644 --- a/cli-plugins/manager/manager_test.go +++ b/cli-plugins/manager/manager_test.go @@ -7,8 +7,79 @@ import ( "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/internal/test" "gotest.tools/assert" + "gotest.tools/fs" ) +func TestListPluginCandidates(t *testing.T) { + // Populate a selection of directories with various shadowed and bogus/obscure plugin candidates. + // For the purposes of this test no contents is required and permissions are irrelevant. + dir := fs.NewDir(t, t.Name(), + fs.WithDir( + "plugins1", + fs.WithFile("docker-plugin1", ""), // This appears in each directory + fs.WithFile("not-a-plugin", ""), // Should be ignored + fs.WithFile("docker-symlinked1", ""), // This and ... + fs.WithSymlink("docker-symlinked2", "docker-symlinked1"), // ... this should both appear + fs.WithDir("ignored1"), // A directory should be ignored + ), + fs.WithDir( + "plugins2", + fs.WithFile("docker-plugin1", ""), + fs.WithFile("also-not-a-plugin", ""), + fs.WithFile("docker-hardlink1", ""), // This and ... + fs.WithHardlink("docker-hardlink2", "docker-hardlink1"), // ... this should both appear + fs.WithDir("ignored2"), + ), + fs.WithDir( + "plugins3-target", // Will be referenced as a symlink from below + fs.WithFile("docker-plugin1", ""), + fs.WithDir("ignored3"), + fs.WithSymlink("docker-brokensymlink", "broken"), // A broken symlink is still a candidate (but would fail tests later) + fs.WithFile("non-plugin-symlinked", ""), // This shouldn't appear, but ... + fs.WithSymlink("docker-symlinked", "non-plugin-symlinked"), // ... this link to it should. + ), + fs.WithSymlink("plugins3", "plugins3-target"), + fs.WithFile("/plugins4", ""), + fs.WithSymlink("plugins5", "plugins5-nonexistent-target"), + ) + defer dir.Remove() + + var dirs []string + for _, d := range []string{"plugins1", "nonexistent", "plugins2", "plugins3", "plugins4", "plugins5"} { + dirs = append(dirs, dir.Join(d)) + } + + candidates, err := listPluginCandidates(dirs) + assert.NilError(t, err) + exp := map[string][]string{ + "plugin1": { + dir.Join("plugins1", "docker-plugin1"), + dir.Join("plugins2", "docker-plugin1"), + dir.Join("plugins3", "docker-plugin1"), + }, + "symlinked1": { + dir.Join("plugins1", "docker-symlinked1"), + }, + "symlinked2": { + dir.Join("plugins1", "docker-symlinked2"), + }, + "hardlink1": { + dir.Join("plugins2", "docker-hardlink1"), + }, + "hardlink2": { + dir.Join("plugins2", "docker-hardlink2"), + }, + "brokensymlink": { + dir.Join("plugins3", "docker-brokensymlink"), + }, + "symlinked": { + dir.Join("plugins3", "docker-symlinked"), + }, + } + + assert.DeepEqual(t, candidates, exp) +} + func TestErrPluginNotFound(t *testing.T) { var err error = errPluginNotFound("test") err.(errPluginNotFound).NotFound() diff --git a/cli-plugins/manager/plugin.go b/cli-plugins/manager/plugin.go index 8a07c5853726..cc7e312db88d 100644 --- a/cli-plugins/manager/plugin.go +++ b/cli-plugins/manager/plugin.go @@ -69,6 +69,12 @@ func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) { if rootcmd != nil { for _, cmd := range rootcmd.Commands() { + // Ignore conflicts with commands which are + // just plugin stubs (i.e. from a previous + // call to AddPluginCommandStubs). + if p := cmd.Annotations[CommandAnnotationPlugin]; p == "true" { + continue + } if cmd.Name() == p.Name { p.Err = errors.Errorf("plugin %q duplicates builtin command", p.Name) return p, nil diff --git a/cli/cobra.go b/cli/cobra.go index 8acce9478392..6a7511011746 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + pluginmanager "github.com/docker/cli/cli-plugins/manager" cliconfig "github.com/docker/cli/cli/config" cliflags "github.com/docker/cli/cli/flags" "github.com/docker/docker/pkg/term" @@ -14,7 +15,7 @@ import ( // setupCommonRootCommand contains the setup common to // SetupRootCommand and SetupPluginRootCommand. -func setupCommonRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *pflag.FlagSet) { +func setupCommonRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *pflag.FlagSet, *cobra.Command) { opts := cliflags.NewClientOptions() flags := rootCmd.Flags() @@ -26,19 +27,21 @@ func setupCommonRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *p cobra.AddTemplateFunc("operationSubCommands", operationSubCommands) cobra.AddTemplateFunc("managementSubCommands", managementSubCommands) cobra.AddTemplateFunc("wrappedFlagUsages", wrappedFlagUsages) + cobra.AddTemplateFunc("commandVendor", commandVendor) + cobra.AddTemplateFunc("isFirstLevelCommand", isFirstLevelCommand) // is it an immediate sub-command of the root rootCmd.SetUsageTemplate(usageTemplate) rootCmd.SetHelpTemplate(helpTemplate) rootCmd.SetFlagErrorFunc(FlagErrorFunc) rootCmd.SetHelpCommand(helpCommand) - return opts, flags + return opts, flags, helpCommand } // SetupRootCommand sets default usage, help, and error handling for the // root command. -func SetupRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *pflag.FlagSet) { - opts, flags := setupCommonRootCommand(rootCmd) +func SetupRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *pflag.FlagSet, *cobra.Command) { + opts, flags, helpCmd := setupCommonRootCommand(rootCmd) rootCmd.SetVersionTemplate("Docker version {{.Version}}\n") @@ -46,12 +49,12 @@ func SetupRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *pflag.F rootCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help") rootCmd.PersistentFlags().Lookup("help").Hidden = true - return opts, flags + return opts, flags, helpCmd } // SetupPluginRootCommand sets default usage, help and error handling for a plugin root command. func SetupPluginRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *pflag.FlagSet) { - opts, flags := setupCommonRootCommand(rootCmd) + opts, flags, _ := setupCommonRootCommand(rootCmd) rootCmd.PersistentFlags().BoolP("help", "", false, "Print usage") rootCmd.PersistentFlags().Lookup("help").Hidden = true @@ -138,6 +141,21 @@ func wrappedFlagUsages(cmd *cobra.Command) string { return cmd.Flags().FlagUsagesWrapped(width - 1) } +func isFirstLevelCommand(cmd *cobra.Command) bool { + return cmd.Parent() == cmd.Root() +} + +func commandVendor(cmd *cobra.Command) string { + width := 13 + if v, ok := cmd.Annotations[pluginmanager.CommandAnnotationPluginVendor]; ok { + if len(v) > width-2 { + v = v[:width-3] + "…" + } + return fmt.Sprintf("%-*s", width, "("+v+")") + } + return strings.Repeat(" ", width) +} + func managementSubCommands(cmd *cobra.Command) []*cobra.Command { cmds := []*cobra.Command{} for _, sub := range cmd.Commands() { @@ -178,7 +196,7 @@ Options: Management Commands: {{- range managementSubCommands . }} - {{rpad .Name .NamePadding }} {{.Short}} + {{rpad .Name .NamePadding }} {{ if isFirstLevelCommand .}}{{commandVendor .}} {{ end}}{{.Short}} {{- end}} {{- end}} @@ -187,7 +205,7 @@ Management Commands: Commands: {{- range operationSubCommands . }} - {{rpad .Name .NamePadding }} {{.Short}} + {{rpad .Name .NamePadding }} {{ if isFirstLevelCommand .}}{{commandVendor .}} {{ end}}{{.Short}} {{- end}} {{- end}} diff --git a/cli/cobra_test.go b/cli/cobra_test.go index 8c9cf1b19fc6..99744e0670e8 100644 --- a/cli/cobra_test.go +++ b/cli/cobra_test.go @@ -3,6 +3,7 @@ package cli import ( "testing" + pluginmanager "github.com/docker/cli/cli-plugins/manager" "github.com/spf13/cobra" "gotest.tools/assert" ) @@ -28,3 +29,29 @@ func TestVisitAll(t *testing.T) { expected := []string{"sub1sub1", "sub1sub2", "sub1", "sub2", "root"} assert.DeepEqual(t, expected, visited) } + +func TestCommandVendor(t *testing.T) { + // Non plugin. + assert.Equal(t, commandVendor(&cobra.Command{Use: "test"}), " ") + + // Plugins with various lengths of vendor. + for _, tc := range []struct { + vendor string + expected string + }{ + {vendor: "vendor", expected: "(vendor) "}, + {vendor: "vendor12345", expected: "(vendor12345)"}, + {vendor: "vendor123456", expected: "(vendor1234…)"}, + {vendor: "vendor1234567", expected: "(vendor1234…)"}, + } { + t.Run(tc.vendor, func(t *testing.T) { + cmd := &cobra.Command{ + Use: "test", + Annotations: map[string]string{ + pluginmanager.CommandAnnotationPluginVendor: tc.vendor, + }, + } + assert.Equal(t, commandVendor(cmd), tc.expected) + }) + } +} diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index 062cbfdc9b73..b9734453ffbd 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -21,8 +21,9 @@ import ( func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command { var ( - opts *cliflags.ClientOptions - flags *pflag.FlagSet + opts *cliflags.ClientOptions + flags *pflag.FlagSet + helpCmd *cobra.Command ) cmd := &cobra.Command{ @@ -57,11 +58,12 @@ func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command { Version: fmt.Sprintf("%s, build %s", version.Version, version.GitCommit), DisableFlagsInUseLine: true, } - opts, flags = cli.SetupRootCommand(cmd) + opts, flags, helpCmd = cli.SetupRootCommand(cmd) flags.BoolP("version", "v", false, "Print version information and quit") setFlagErrorFunc(dockerCli, cmd, flags, opts) + setupHelpCommand(dockerCli, cmd, helpCmd, flags, opts) setHelpFunc(dockerCli, cmd, flags, opts) cmd.SetOutput(dockerCli.Out()) @@ -90,6 +92,34 @@ func setFlagErrorFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *p }) } +func setupHelpCommand(dockerCli *command.DockerCli, rootCmd, helpCmd *cobra.Command, flags *pflag.FlagSet, opts *cliflags.ClientOptions) { + origRun := helpCmd.Run + origRunE := helpCmd.RunE + + helpCmd.Run = nil + helpCmd.RunE = func(c *cobra.Command, args []string) error { + // No Persistent* hooks are called for help, so we must initialize here. + if err := initializeDockerCli(dockerCli, flags, opts); err != nil { + return err + } + + // Add a stub entry for every plugin so they are + // included in the help output. If we have no args + // then this is being used for `docker help` and we + // want to include broken plugins, otherwise this is + // `help «foo»` and we do not. + if err := pluginmanager.AddPluginCommandStubs(dockerCli, rootCmd, len(args) == 0); err != nil { + return err + } + + if origRunE != nil { + return origRunE(c, args) + } + origRun(c, args) + return nil + } +} + func setHelpFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag.FlagSet, opts *cliflags.ClientOptions) { defaultHelpFunc := cmd.HelpFunc() cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) { diff --git a/e2e/cli-plugins/help_test.go b/e2e/cli-plugins/help_test.go new file mode 100644 index 000000000000..c66dfacc0f4c --- /dev/null +++ b/e2e/cli-plugins/help_test.go @@ -0,0 +1,54 @@ +package cliplugins + +import ( + "bufio" + "regexp" + "strings" + "testing" + + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/icmd" +) + +// TestGlobalHelp ensures correct behaviour when running `docker help` +func TestGlobalHelp(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("help")) + res.Assert(t, icmd.Expected{ + ExitCode: 0, + }) + assert.Assert(t, is.Equal(res.Stderr(), "")) + scanner := bufio.NewScanner(strings.NewReader(res.Stdout())) + + // Instead of baking in the full current output of `docker + // help`, which can be expected to change regularly, bake in + // some checkpoints. Key things we are looking for: + // + // - The top-level description + // - Each of the main headings + // - Some builtin commands under the main headings + // - The `helloworld` plugin in the appropriate place + // + // Regexps are needed because the width depends on `unix.TIOCGWINSZ` or similar. + for _, expected := range []*regexp.Regexp{ + regexp.MustCompile(`^A self-sufficient runtime for containers$`), + regexp.MustCompile(`^Management Commands:$`), + regexp.MustCompile(`^ container\s+Manage containers$`), + regexp.MustCompile(`^Commands:$`), + regexp.MustCompile(`^ create\s+Create a new container$`), + regexp.MustCompile(`^ helloworld\s+\(Docker Inc\.\)\s+A basic Hello World plugin for tests$`), + regexp.MustCompile(`^ ps\s+List containers$`), + } { + var found bool + for scanner.Scan() { + if expected.MatchString(scanner.Text()) { + found = true + break + } + } + assert.Assert(t, found, "Did not find match for %q in `docker help` output", expected) + } +} From c43da091888d6b2a427bab7b6c917107894ecbcd Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Tue, 22 Jan 2019 13:44:39 +0000 Subject: [PATCH 34/60] Add stubs when calling help due to no arguments e.g. the `docker` case which should act as `docker help`. Signed-off-by: Ian Campbell --- cmd/docker/docker.go | 20 +++++++++++--------- e2e/cli-plugins/help_test.go | 8 ++++++++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index b9734453ffbd..d781b688ca42 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -103,15 +103,6 @@ func setupHelpCommand(dockerCli *command.DockerCli, rootCmd, helpCmd *cobra.Comm return err } - // Add a stub entry for every plugin so they are - // included in the help output. If we have no args - // then this is being used for `docker help` and we - // want to include broken plugins, otherwise this is - // `help «foo»` and we do not. - if err := pluginmanager.AddPluginCommandStubs(dockerCli, rootCmd, len(args) == 0); err != nil { - return err - } - if origRunE != nil { return origRunE(c, args) } @@ -135,6 +126,17 @@ func setHelpFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag. ccmd.Println(err) return } + + // Add a stub entry for every plugin so they are + // included in the help output. If we have no args + // then this is being used for `docker help` and we + // want to include broken plugins, otherwise this is + // `help «foo»` and we do not. + if err := pluginmanager.AddPluginCommandStubs(dockerCli, ccmd.Root(), len(args) == 0); err != nil { + ccmd.Println(err) + return + } + defaultHelpFunc(ccmd, args) }) } diff --git a/e2e/cli-plugins/help_test.go b/e2e/cli-plugins/help_test.go index c66dfacc0f4c..f9690fe50324 100644 --- a/e2e/cli-plugins/help_test.go +++ b/e2e/cli-plugins/help_test.go @@ -51,4 +51,12 @@ func TestGlobalHelp(t *testing.T) { } assert.Assert(t, found, "Did not find match for %q in `docker help` output", expected) } + + // Running just `docker` (without help) should produce the same thing, except on Stderr + res2 := icmd.RunCmd(run()) + res2.Assert(t, icmd.Expected{ + ExitCode: 0, + }) + assert.Assert(t, is.Equal(res2.Stdout(), "")) + assert.Assert(t, is.Equal(res2.Stderr(), res.Stdout())) } From 20a284721cc59563627e5d927344735a1fa0a2b1 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Tue, 11 Dec 2018 14:52:59 +0000 Subject: [PATCH 35/60] =?UTF-8?q?Integrate=20CLI=20plugins=20with=20`docke?= =?UTF-8?q?r=20help=20=C2=ABfoo=C2=BB`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ian Campbell --- cmd/docker/docker.go | 12 +++++ e2e/cli-plugins/run_test.go | 52 +++++++++++++++++++ .../testdata/docker-help-badmeta-err.golden | 1 + .../docker-help-helloworld-goodbye.golden | 4 ++ .../testdata/docker-help-helloworld.golden | 9 ++++ .../docker-help-nonexistent-err.golden | 1 + 6 files changed, 79 insertions(+) create mode 100644 e2e/cli-plugins/testdata/docker-help-badmeta-err.golden create mode 100644 e2e/cli-plugins/testdata/docker-help-helloworld-goodbye.golden create mode 100644 e2e/cli-plugins/testdata/docker-help-helloworld.golden create mode 100644 e2e/cli-plugins/testdata/docker-help-nonexistent-err.golden diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index d781b688ca42..f4b94652ecbb 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -103,6 +103,18 @@ func setupHelpCommand(dockerCli *command.DockerCli, rootCmd, helpCmd *cobra.Comm return err } + if len(args) > 0 { + helpcmd, err := pluginmanager.PluginRunCommand(dockerCli, args[0], rootCmd) + if err == nil { + err = helpcmd.Run() + if err != nil { + return err + } + } + if !pluginmanager.IsNotFound(err) { + return err + } + } if origRunE != nil { return origRunE(c, args) } diff --git a/e2e/cli-plugins/run_test.go b/e2e/cli-plugins/run_test.go index e4cd94d59ad6..34bd84c9a863 100644 --- a/e2e/cli-plugins/run_test.go +++ b/e2e/cli-plugins/run_test.go @@ -22,6 +22,19 @@ func TestRunNonexisting(t *testing.T) { golden.Assert(t, res.Stderr(), "docker-nonexistent-err.golden") } +// TestHelpNonexisting ensures correct behaviour when invoking help on a nonexistent plugin. +func TestHelpNonexisting(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("help", "nonexistent")) + res.Assert(t, icmd.Expected{ + ExitCode: 1, + }) + assert.Assert(t, is.Equal(res.Stdout(), "")) + golden.Assert(t, res.Stderr(), "docker-help-nonexistent-err.golden") +} + // TestRunBad ensures correct behaviour when running an existent but invalid plugin func TestRunBad(t *testing.T) { run, cleanup := prepare(t) @@ -35,6 +48,19 @@ func TestRunBad(t *testing.T) { golden.Assert(t, res.Stderr(), "docker-badmeta-err.golden") } +// TestHelpBad ensures correct behaviour when invoking help on a existent but invalid plugin. +func TestHelpBad(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("help", "badmeta")) + res.Assert(t, icmd.Expected{ + ExitCode: 1, + }) + assert.Assert(t, is.Equal(res.Stdout(), "")) + golden.Assert(t, res.Stderr(), "docker-help-badmeta-err.golden") +} + // TestRunGood ensures correct behaviour when running a valid plugin func TestRunGood(t *testing.T) { run, cleanup := prepare(t) @@ -47,6 +73,19 @@ func TestRunGood(t *testing.T) { }) } +// TestHelpGood ensures correct behaviour when invoking help on a +// valid plugin. A global argument is included to ensure it does not +// interfere. +func TestHelpGood(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("-D", "help", "helloworld")) + res.Assert(t, icmd.Success) + golden.Assert(t, res.Stdout(), "docker-help-helloworld.golden") + assert.Assert(t, is.Equal(res.Stderr(), "")) +} + // TestRunGoodSubcommand ensures correct behaviour when running a valid plugin with a subcommand func TestRunGoodSubcommand(t *testing.T) { run, cleanup := prepare(t) @@ -58,3 +97,16 @@ func TestRunGoodSubcommand(t *testing.T) { Out: "Goodbye World!", }) } + +// TestHelpGoodSubcommand ensures correct behaviour when invoking help on a +// valid plugin subcommand. A global argument is included to ensure it does not +// interfere. +func TestHelpGoodSubcommand(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("-D", "help", "helloworld", "goodbye")) + res.Assert(t, icmd.Success) + golden.Assert(t, res.Stdout(), "docker-help-helloworld-goodbye.golden") + assert.Assert(t, is.Equal(res.Stderr(), "")) +} diff --git a/e2e/cli-plugins/testdata/docker-help-badmeta-err.golden b/e2e/cli-plugins/testdata/docker-help-badmeta-err.golden new file mode 100644 index 000000000000..13a827f4064e --- /dev/null +++ b/e2e/cli-plugins/testdata/docker-help-badmeta-err.golden @@ -0,0 +1 @@ +unknown help topic: badmeta diff --git a/e2e/cli-plugins/testdata/docker-help-helloworld-goodbye.golden b/e2e/cli-plugins/testdata/docker-help-helloworld-goodbye.golden new file mode 100644 index 000000000000..d789f7997f78 --- /dev/null +++ b/e2e/cli-plugins/testdata/docker-help-helloworld-goodbye.golden @@ -0,0 +1,4 @@ + +Usage: docker helloworld goodbye + +Say Goodbye instead of Hello diff --git a/e2e/cli-plugins/testdata/docker-help-helloworld.golden b/e2e/cli-plugins/testdata/docker-help-helloworld.golden new file mode 100644 index 000000000000..67390e6e442d --- /dev/null +++ b/e2e/cli-plugins/testdata/docker-help-helloworld.golden @@ -0,0 +1,9 @@ + +Usage: docker helloworld COMMAND + +A basic Hello World plugin for tests + +Commands: + goodbye Say Goodbye instead of Hello + +Run 'docker helloworld COMMAND --help' for more information on a command. diff --git a/e2e/cli-plugins/testdata/docker-help-nonexistent-err.golden b/e2e/cli-plugins/testdata/docker-help-nonexistent-err.golden new file mode 100644 index 000000000000..7a151caa3eb5 --- /dev/null +++ b/e2e/cli-plugins/testdata/docker-help-nonexistent-err.golden @@ -0,0 +1 @@ +unknown help topic: nonexistent From 53f018120ae8d93531245ba56d4acdfa4bd837ba Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Mon, 17 Dec 2018 16:23:55 +0000 Subject: [PATCH 36/60] =?UTF-8?q?Integrate=20CLI=20plugins=20with=20`docke?= =?UTF-8?q?r=20=C2=ABplugin=C2=BB=20--help`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To achieve this we hook in at the beginning of our custom `HelpFunc` and detect the plugin case by adding stub commands. Signed-off-by: Ian Campbell --- cmd/docker/docker.go | 26 ++++++++++++++++ e2e/cli-plugins/run_test.go | 60 +++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index f4b94652ecbb..1ebf30a89d25 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -123,6 +123,21 @@ func setupHelpCommand(dockerCli *command.DockerCli, rootCmd, helpCmd *cobra.Comm } } +func tryRunPluginHelp(dockerCli command.Cli, ccmd *cobra.Command, cargs []string) error { + root := ccmd.Root() + pluginmanager.AddPluginCommandStubs(dockerCli, root, false) + + cmd, _, err := root.Traverse(cargs) + if err != nil { + return err + } + helpcmd, err := pluginmanager.PluginRunCommand(dockerCli, cmd.Name(), root) + if err != nil { + return err + } + return helpcmd.Run() +} + func setHelpFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag.FlagSet, opts *cliflags.ClientOptions) { defaultHelpFunc := cmd.HelpFunc() cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) { @@ -130,6 +145,17 @@ func setHelpFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag. ccmd.Println(err) return } + if len(args) >= 1 { + err := tryRunPluginHelp(dockerCli, ccmd, args) + if err == nil { // Successfully ran the plugin + return + } + if !pluginmanager.IsNotFound(err) { + ccmd.Println(err) + return + } + } + if err := isSupported(ccmd, dockerCli); err != nil { ccmd.Println(err) return diff --git a/e2e/cli-plugins/run_test.go b/e2e/cli-plugins/run_test.go index 34bd84c9a863..30a13cf79e2e 100644 --- a/e2e/cli-plugins/run_test.go +++ b/e2e/cli-plugins/run_test.go @@ -35,6 +35,22 @@ func TestHelpNonexisting(t *testing.T) { golden.Assert(t, res.Stderr(), "docker-help-nonexistent-err.golden") } +// TestNonexistingHelp ensures correct behaviour when invoking a +// nonexistent plugin with `--help`. +func TestNonexistingHelp(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("nonexistent", "--help")) + res.Assert(t, icmd.Expected{ + ExitCode: 0, + // This should actually be the whole docker help + // output, so spot check instead having of a golden + // with everything in, which will change too frequently. + Out: "Usage: docker [OPTIONS] COMMAND\n\nA self-sufficient runtime for containers", + }) +} + // TestRunBad ensures correct behaviour when running an existent but invalid plugin func TestRunBad(t *testing.T) { run, cleanup := prepare(t) @@ -61,6 +77,22 @@ func TestHelpBad(t *testing.T) { golden.Assert(t, res.Stderr(), "docker-help-badmeta-err.golden") } +// TestBadHelp ensures correct behaviour when invoking an +// existent but invalid plugin with `--help`. +func TestBadHelp(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("badmeta", "--help")) + res.Assert(t, icmd.Expected{ + ExitCode: 0, + // This should be literally the whole docker help + // output, so spot check instead of a golden with + // everything in which will change all the time. + Out: "Usage: docker [OPTIONS] COMMAND\n\nA self-sufficient runtime for containers", + }) +} + // TestRunGood ensures correct behaviour when running a valid plugin func TestRunGood(t *testing.T) { run, cleanup := prepare(t) @@ -86,6 +118,20 @@ func TestHelpGood(t *testing.T) { assert.Assert(t, is.Equal(res.Stderr(), "")) } +// TestGoodHelp ensures correct behaviour when calling a valid plugin +// with `--help`. A global argument is used to ensure it does not +// interfere. +func TestGoodHelp(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("-D", "helloworld", "--help")) + res.Assert(t, icmd.Success) + // This is the same golden file as `TestHelpGood`, above. + golden.Assert(t, res.Stdout(), "docker-help-helloworld.golden") + assert.Assert(t, is.Equal(res.Stderr(), "")) +} + // TestRunGoodSubcommand ensures correct behaviour when running a valid plugin with a subcommand func TestRunGoodSubcommand(t *testing.T) { run, cleanup := prepare(t) @@ -110,3 +156,17 @@ func TestHelpGoodSubcommand(t *testing.T) { golden.Assert(t, res.Stdout(), "docker-help-helloworld-goodbye.golden") assert.Assert(t, is.Equal(res.Stderr(), "")) } + +// TestGoodSubcommandHelp ensures correct behaviour when calling a valid plugin +// with a subcommand and `--help`. A global argument is used to ensure it does not +// interfere. +func TestGoodSubcommandHelp(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("-D", "helloworld", "goodbye", "--help")) + res.Assert(t, icmd.Success) + // This is the same golden file as `TestHelpGoodSubcommand`, above. + golden.Assert(t, res.Stdout(), "docker-help-helloworld-goodbye.golden") + assert.Assert(t, is.Equal(res.Stderr(), "")) +} From e5e578abc7d8cb507c08cdca56386d1a37f041ae Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Mon, 17 Dec 2018 15:55:38 +0000 Subject: [PATCH 37/60] Allow plugins to make use of the cobra `PersistentPreRun` hooks. Previously a plugin which used these hooks would overwrite the top-level plugin command's use of this hook, resulting in the dockerCli object not being fully initialised. Provide a function which plugins can use to chain to the required behaviour. This required some fairly ugly arrangements to preserve state (which was previously in-scope in `newPluginCOmmand`) to be used by the new function. Signed-off-by: Ian Campbell --- cli-plugins/examples/helloworld/main.go | 20 +++++- cli-plugins/plugin/plugin.go | 62 ++++++++++++++----- e2e/cli-plugins/run_test.go | 12 ++++ .../testdata/docker-help-helloworld.golden | 1 + 4 files changed, 78 insertions(+), 17 deletions(-) diff --git a/cli-plugins/examples/helloworld/main.go b/cli-plugins/examples/helloworld/main.go index e79e32ce5436..9a511591ec96 100644 --- a/cli-plugins/examples/helloworld/main.go +++ b/cli-plugins/examples/helloworld/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "github.com/docker/cli/cli-plugins/manager" @@ -18,16 +19,33 @@ func main() { fmt.Fprintln(dockerCli.Out(), "Goodbye World!") }, } + apiversion := &cobra.Command{ + Use: "apiversion", + Short: "Print the API version of the server", + RunE: func(_ *cobra.Command, _ []string) error { + cli := dockerCli.Client() + ping, err := cli.Ping(context.Background()) + if err != nil { + return err + } + fmt.Println(ping.APIVersion) + return nil + }, + } cmd := &cobra.Command{ Use: "helloworld", Short: "A basic Hello World plugin for tests", + // This is redundant but included to exercise + // the path where a plugin overrides this + // hook. + PersistentPreRunE: plugin.PersistentPreRunE, Run: func(cmd *cobra.Command, args []string) { fmt.Fprintln(dockerCli.Out(), "Hello World!") }, } - cmd.AddCommand(goodbye) + cmd.AddCommand(goodbye, apiversion) return cmd }, manager.Metadata{ diff --git a/cli-plugins/plugin/plugin.go b/cli-plugins/plugin/plugin.go index ce2bd0bd6f50..c72a68bb7eb0 100644 --- a/cli-plugins/plugin/plugin.go +++ b/cli-plugins/plugin/plugin.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "os" + "sync" "github.com/docker/cli/cli" "github.com/docker/cli/cli-plugins/manager" @@ -42,29 +43,53 @@ func Run(makeCmd func(command.Cli) *cobra.Command, meta manager.Metadata) { } } -func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager.Metadata) *cobra.Command { - var ( - opts *cliflags.ClientOptions - flags *pflag.FlagSet - ) +// options encapsulates the ClientOptions and FlagSet constructed by +// `newPluginCommand` such that they can be finalized by our +// `PersistentPreRunE`. This is necessary because otherwise a plugin's +// own use of that hook will shadow anything we add to the top-level +// command meaning the CLI is never Initialized. +var options struct { + init, prerun sync.Once + opts *cliflags.ClientOptions + flags *pflag.FlagSet + dockerCli *command.DockerCli +} + +// PersistentPreRunE must be called by any plugin command (or +// subcommand) which uses the cobra `PersistentPreRun*` hook. Plugins +// which do not make use of `PersistentPreRun*` do not need to call +// this (although it remains safe to do so). Plugins are recommended +// to use `PersistenPreRunE` to enable the error to be +// returned. Should not be called outside of a commands +// PersistentPreRunE hook and must not be run unless Run has been +// called. +func PersistentPreRunE(cmd *cobra.Command, args []string) error { + var err error + options.prerun.Do(func() { + if options.opts == nil || options.flags == nil || options.dockerCli == nil { + panic("PersistentPreRunE called without Run successfully called first") + } + // flags must be the original top-level command flags, not cmd.Flags() + options.opts.Common.SetDefaultOptions(options.flags) + err = options.dockerCli.Initialize(options.opts) + }) + return err +} +func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager.Metadata) *cobra.Command { name := plugin.Use fullname := manager.NamePrefix + name cmd := &cobra.Command{ - Use: fmt.Sprintf("docker [OPTIONS] %s [ARG...]", name), - Short: fullname + " is a Docker CLI plugin", - SilenceUsage: true, - SilenceErrors: true, - TraverseChildren: true, - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - // flags must be the top-level command flags, not cmd.Flags() - opts.Common.SetDefaultOptions(flags) - return dockerCli.Initialize(opts) - }, + Use: fmt.Sprintf("docker [OPTIONS] %s [ARG...]", name), + Short: fullname + " is a Docker CLI plugin", + SilenceUsage: true, + SilenceErrors: true, + TraverseChildren: true, + PersistentPreRunE: PersistentPreRunE, DisableFlagsInUseLine: true, } - opts, flags = cli.SetupPluginRootCommand(cmd) + opts, flags := cli.SetupPluginRootCommand(cmd) cmd.SetOutput(dockerCli.Out()) @@ -75,6 +100,11 @@ func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta cli.DisableFlagsInUseLine(cmd) + options.init.Do(func() { + options.opts = opts + options.flags = flags + options.dockerCli = dockerCli + }) return cmd } diff --git a/e2e/cli-plugins/run_test.go b/e2e/cli-plugins/run_test.go index 30a13cf79e2e..d16bf3bb8be9 100644 --- a/e2e/cli-plugins/run_test.go +++ b/e2e/cli-plugins/run_test.go @@ -170,3 +170,15 @@ func TestGoodSubcommandHelp(t *testing.T) { golden.Assert(t, res.Stdout(), "docker-help-helloworld-goodbye.golden") assert.Assert(t, is.Equal(res.Stderr(), "")) } + +// TestCliInitialized tests the code paths which ensure that the Cli +// object is initialized even if the plugin uses PersistentRunE +func TestCliInitialized(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("helloworld", "apiversion")) + res.Assert(t, icmd.Success) + assert.Assert(t, res.Stdout() != "") + assert.Assert(t, is.Equal(res.Stderr(), "")) +} diff --git a/e2e/cli-plugins/testdata/docker-help-helloworld.golden b/e2e/cli-plugins/testdata/docker-help-helloworld.golden index 67390e6e442d..e7252bf2d016 100644 --- a/e2e/cli-plugins/testdata/docker-help-helloworld.golden +++ b/e2e/cli-plugins/testdata/docker-help-helloworld.golden @@ -4,6 +4,7 @@ Usage: docker helloworld COMMAND A basic Hello World plugin for tests Commands: + apiversion Print the API version of the server goodbye Say Goodbye instead of Hello Run 'docker helloworld COMMAND --help' for more information on a command. From 0ab8ec0e4c1226cc176cdbcef9a8083d6995dc89 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Wed, 19 Dec 2018 11:29:01 +0000 Subject: [PATCH 38/60] Output broken CLI plugins in `help` output. Signed-off-by: Ian Campbell --- cli/cobra.go | 44 ++++++++++++++++++++++++++++++++++++ cli/cobra_test.go | 21 +++++++++++++++++ e2e/cli-plugins/help_test.go | 3 +++ 3 files changed, 68 insertions(+) diff --git a/cli/cobra.go b/cli/cobra.go index 6a7511011746..cea1a9be4e85 100644 --- a/cli/cobra.go +++ b/cli/cobra.go @@ -24,11 +24,14 @@ func setupCommonRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *p cobra.AddTemplateFunc("hasSubCommands", hasSubCommands) cobra.AddTemplateFunc("hasManagementSubCommands", hasManagementSubCommands) + cobra.AddTemplateFunc("hasInvalidPlugins", hasInvalidPlugins) cobra.AddTemplateFunc("operationSubCommands", operationSubCommands) cobra.AddTemplateFunc("managementSubCommands", managementSubCommands) + cobra.AddTemplateFunc("invalidPlugins", invalidPlugins) cobra.AddTemplateFunc("wrappedFlagUsages", wrappedFlagUsages) cobra.AddTemplateFunc("commandVendor", commandVendor) cobra.AddTemplateFunc("isFirstLevelCommand", isFirstLevelCommand) // is it an immediate sub-command of the root + cobra.AddTemplateFunc("invalidPluginReason", invalidPluginReason) rootCmd.SetUsageTemplate(usageTemplate) rootCmd.SetHelpTemplate(helpTemplate) @@ -115,6 +118,10 @@ var helpCommand = &cobra.Command{ }, } +func isPlugin(cmd *cobra.Command) bool { + return cmd.Annotations[pluginmanager.CommandAnnotationPlugin] == "true" +} + func hasSubCommands(cmd *cobra.Command) bool { return len(operationSubCommands(cmd)) > 0 } @@ -123,9 +130,16 @@ func hasManagementSubCommands(cmd *cobra.Command) bool { return len(managementSubCommands(cmd)) > 0 } +func hasInvalidPlugins(cmd *cobra.Command) bool { + return len(invalidPlugins(cmd)) > 0 +} + func operationSubCommands(cmd *cobra.Command) []*cobra.Command { cmds := []*cobra.Command{} for _, sub := range cmd.Commands() { + if isPlugin(sub) && invalidPluginReason(sub) != "" { + continue + } if sub.IsAvailableCommand() && !sub.HasSubCommands() { cmds = append(cmds, sub) } @@ -159,6 +173,9 @@ func commandVendor(cmd *cobra.Command) string { func managementSubCommands(cmd *cobra.Command) []*cobra.Command { cmds := []*cobra.Command{} for _, sub := range cmd.Commands() { + if isPlugin(sub) && invalidPluginReason(sub) != "" { + continue + } if sub.IsAvailableCommand() && sub.HasSubCommands() { cmds = append(cmds, sub) } @@ -166,6 +183,23 @@ func managementSubCommands(cmd *cobra.Command) []*cobra.Command { return cmds } +func invalidPlugins(cmd *cobra.Command) []*cobra.Command { + cmds := []*cobra.Command{} + for _, sub := range cmd.Commands() { + if !isPlugin(sub) { + continue + } + if invalidPluginReason(sub) != "" { + cmds = append(cmds, sub) + } + } + return cmds +} + +func invalidPluginReason(cmd *cobra.Command) string { + return cmd.Annotations[pluginmanager.CommandAnnotationPluginInvalid] +} + var usageTemplate = `Usage: {{- if not .HasSubCommands}} {{.UseLine}}{{end}} @@ -209,6 +243,16 @@ Commands: {{- end}} {{- end}} +{{- if hasInvalidPlugins . }} + +Invalid Plugins: + +{{- range invalidPlugins . }} + {{rpad .Name .NamePadding }} {{invalidPluginReason .}} +{{- end}} + +{{- end}} + {{- if .HasSubCommands }} Run '{{.CommandPath}} COMMAND --help' for more information on a command. diff --git a/cli/cobra_test.go b/cli/cobra_test.go index 99744e0670e8..a9d943e678bf 100644 --- a/cli/cobra_test.go +++ b/cli/cobra_test.go @@ -4,8 +4,10 @@ import ( "testing" pluginmanager "github.com/docker/cli/cli-plugins/manager" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/spf13/cobra" "gotest.tools/assert" + is "gotest.tools/assert/cmp" ) func TestVisitAll(t *testing.T) { @@ -55,3 +57,22 @@ func TestCommandVendor(t *testing.T) { }) } } + +func TestInvalidPlugin(t *testing.T) { + root := &cobra.Command{Use: "root"} + sub1 := &cobra.Command{Use: "sub1"} + sub1sub1 := &cobra.Command{Use: "sub1sub1"} + sub1sub2 := &cobra.Command{Use: "sub1sub2"} + sub2 := &cobra.Command{Use: "sub2"} + + assert.Assert(t, is.Len(invalidPlugins(root), 0)) + + sub1.Annotations = map[string]string{ + pluginmanager.CommandAnnotationPlugin: "true", + pluginmanager.CommandAnnotationPluginInvalid: "foo", + } + root.AddCommand(sub1, sub2) + sub1.AddCommand(sub1sub1, sub1sub2) + + assert.DeepEqual(t, invalidPlugins(root), []*cobra.Command{sub1}, cmpopts.IgnoreUnexported(cobra.Command{})) +} diff --git a/e2e/cli-plugins/help_test.go b/e2e/cli-plugins/help_test.go index f9690fe50324..f82959dbab05 100644 --- a/e2e/cli-plugins/help_test.go +++ b/e2e/cli-plugins/help_test.go @@ -31,6 +31,7 @@ func TestGlobalHelp(t *testing.T) { // - Each of the main headings // - Some builtin commands under the main headings // - The `helloworld` plugin in the appropriate place + // - The `badmeta` plugin under the "Invalid Plugins" heading. // // Regexps are needed because the width depends on `unix.TIOCGWINSZ` or similar. for _, expected := range []*regexp.Regexp{ @@ -41,6 +42,8 @@ func TestGlobalHelp(t *testing.T) { regexp.MustCompile(`^ create\s+Create a new container$`), regexp.MustCompile(`^ helloworld\s+\(Docker Inc\.\)\s+A basic Hello World plugin for tests$`), regexp.MustCompile(`^ ps\s+List containers$`), + regexp.MustCompile(`^Invalid Plugins:$`), + regexp.MustCompile(`^ badmeta\s+invalid metadata: invalid character 'i' looking for beginning of object key string$`), } { var found bool for scanner.Scan() { From 1c576e90435283451051056bb6987b28212122f3 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Wed, 19 Dec 2018 14:49:20 +0000 Subject: [PATCH 39/60] Integrate CLI plugins into `docker info` Fairly straight forward. It became necessary to wrap `Plugin.Err` with a type which implements `encoding.MarshalText` in order to have that field rendered properly in the `docker info -f '{{json}}'` output. Since I changed the type somewhat I also added a unit test for `formatInfo`. Signed-off-by: Ian Campbell --- cli-plugins/manager/candidate_test.go | 3 + cli-plugins/manager/error.go | 43 ++++++++++++++ cli-plugins/manager/error_test.go | 24 ++++++++ cli-plugins/manager/metadata.go | 10 ++-- cli-plugins/manager/plugin.go | 23 ++++---- cli/command/system/info.go | 27 ++++++++- cli/command/system/info_test.go | 37 +++++++++++- .../testdata/docker-info-badsec.json.golden | 2 +- .../docker-info-daemon-warnings.json.golden | 2 +- .../docker-info-legacy-warnings.json.golden | 2 +- .../testdata/docker-info-no-swarm.json.golden | 2 +- .../docker-info-plugins-warnings.golden | 1 + .../testdata/docker-info-plugins.golden | 56 +++++++++++++++++++ .../testdata/docker-info-plugins.json.golden | 1 + .../docker-info-with-swarm.json.golden | 2 +- 15 files changed, 210 insertions(+), 25 deletions(-) create mode 100644 cli-plugins/manager/error.go create mode 100644 cli-plugins/manager/error_test.go create mode 100644 cli/command/system/testdata/docker-info-plugins-warnings.golden create mode 100644 cli/command/system/testdata/docker-info-plugins.golden create mode 100644 cli/command/system/testdata/docker-info-plugins.json.golden diff --git a/cli-plugins/manager/candidate_test.go b/cli-plugins/manager/candidate_test.go index 11384226a5d4..b5ed06453f76 100644 --- a/cli-plugins/manager/candidate_test.go +++ b/cli-plugins/manager/candidate_test.go @@ -2,11 +2,13 @@ package manager import ( "fmt" + "reflect" "strings" "testing" "github.com/spf13/cobra" "gotest.tools/assert" + "gotest.tools/assert/cmp" ) type fakeCandidate struct { @@ -73,6 +75,7 @@ func TestValidateCandidate(t *testing.T) { assert.ErrorContains(t, err, tc.err) } else if tc.invalid != "" { assert.NilError(t, err) + assert.Assert(t, cmp.ErrorType(p.Err, reflect.TypeOf(&pluginError{}))) assert.ErrorContains(t, p.Err, tc.invalid) } else { assert.NilError(t, err) diff --git a/cli-plugins/manager/error.go b/cli-plugins/manager/error.go new file mode 100644 index 000000000000..1ad28678695a --- /dev/null +++ b/cli-plugins/manager/error.go @@ -0,0 +1,43 @@ +package manager + +import ( + "github.com/pkg/errors" +) + +// pluginError is set as Plugin.Err by NewPlugin if the plugin +// candidate fails one of the candidate tests. This exists primarily +// to implement encoding.TextMarshaller such that rendering a plugin as JSON +// (e.g. for `docker info -f '{{json .CLIPlugins}}'`) renders the Err +// field as a useful string and not just `{}`. See +// https://github.com/golang/go/issues/10748 for some discussion +// around why the builtin error type doesn't implement this. +type pluginError struct { + cause error +} + +// Error satisfies the core error interface for pluginError. +func (e *pluginError) Error() string { + return e.cause.Error() +} + +// Cause satisfies the errors.causer interface for pluginError. +func (e *pluginError) Cause() error { + return e.cause +} + +// MarshalText marshalls the pluginError into a textual form. +func (e *pluginError) MarshalText() (text []byte, err error) { + return []byte(e.cause.Error()), nil +} + +// wrapAsPluginError wraps an error in a pluginError with an +// additional message, analogous to errors.Wrapf. +func wrapAsPluginError(err error, msg string) error { + return &pluginError{cause: errors.Wrap(err, msg)} +} + +// NewPluginError creates a new pluginError, analogous to +// errors.Errorf. +func NewPluginError(msg string, args ...interface{}) error { + return &pluginError{cause: errors.Errorf(msg, args...)} +} diff --git a/cli-plugins/manager/error_test.go b/cli-plugins/manager/error_test.go new file mode 100644 index 000000000000..04614e24ff47 --- /dev/null +++ b/cli-plugins/manager/error_test.go @@ -0,0 +1,24 @@ +package manager + +import ( + "fmt" + "testing" + + "github.com/pkg/errors" + "gopkg.in/yaml.v2" + "gotest.tools/assert" +) + +func TestPluginError(t *testing.T) { + err := NewPluginError("new error") + assert.Error(t, err, "new error") + + inner := fmt.Errorf("testing") + err = wrapAsPluginError(inner, "wrapping") + assert.Error(t, err, "wrapping: testing") + assert.Equal(t, inner, errors.Cause(err)) + + actual, err := yaml.Marshal(err) + assert.NilError(t, err) + assert.Equal(t, "'wrapping: testing'\n", string(actual)) +} diff --git a/cli-plugins/manager/metadata.go b/cli-plugins/manager/metadata.go index 2d5734e8682c..1df3c8e08fde 100644 --- a/cli-plugins/manager/metadata.go +++ b/cli-plugins/manager/metadata.go @@ -13,13 +13,13 @@ const ( // Metadata provided by the plugin type Metadata struct { // SchemaVersion describes the version of this struct. Mandatory, must be "0.1.0" - SchemaVersion string + SchemaVersion string `json:",omitempty"` // Vendor is the name of the plugin vendor. Mandatory - Vendor string + Vendor string `json:",omitempty"` // Version is the optional version of this plugin. - Version string + Version string `json:",omitempty"` // ShortDescription should be suitable for a single line help message. - ShortDescription string + ShortDescription string `json:",omitempty"` // URL is a pointer to the plugin's homepage. - URL string + URL string `json:",omitempty"` } diff --git a/cli-plugins/manager/plugin.go b/cli-plugins/manager/plugin.go index cc7e312db88d..06a0ba8345ed 100644 --- a/cli-plugins/manager/plugin.go +++ b/cli-plugins/manager/plugin.go @@ -19,8 +19,8 @@ var ( type Plugin struct { Metadata - Name string - Path string + Name string `json:",omitempty"` + Path string `json:",omitempty"` // Err is non-nil if the plugin failed one of the candidate tests. Err error `json:",omitempty"` @@ -31,8 +31,9 @@ type Plugin struct { // newPlugin determines if the given candidate is valid and returns a // Plugin. If the candidate fails one of the tests then `Plugin.Err` -// is set, but the `Plugin` is still returned with no error. An error -// is only returned due to a non-recoverable error. +// is set, and is always a `pluginError`, but the `Plugin` is still +// returned with no error. An error is only returned due to a +// non-recoverable error. func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) { path := c.Path() if path == "" { @@ -63,7 +64,7 @@ func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) { // Now apply the candidate tests, so these update p.Err. if !pluginNameRe.MatchString(p.Name) { - p.Err = errors.Errorf("plugin candidate %q did not match %q", p.Name, pluginNameRe.String()) + p.Err = NewPluginError("plugin candidate %q did not match %q", p.Name, pluginNameRe.String()) return p, nil } @@ -76,11 +77,11 @@ func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) { continue } if cmd.Name() == p.Name { - p.Err = errors.Errorf("plugin %q duplicates builtin command", p.Name) + p.Err = NewPluginError("plugin %q duplicates builtin command", p.Name) return p, nil } if cmd.HasAlias(p.Name) { - p.Err = errors.Errorf("plugin %q duplicates an alias of builtin command %q", p.Name, cmd.Name()) + p.Err = NewPluginError("plugin %q duplicates an alias of builtin command %q", p.Name, cmd.Name()) return p, nil } } @@ -89,21 +90,21 @@ func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) { // We are supposed to check for relevant execute permissions here. Instead we rely on an attempt to execute. meta, err := c.Metadata() if err != nil { - p.Err = errors.Wrap(err, "failed to fetch metadata") + p.Err = wrapAsPluginError(err, "failed to fetch metadata") return p, nil } if err := json.Unmarshal(meta, &p.Metadata); err != nil { - p.Err = errors.Wrap(err, "invalid metadata") + p.Err = wrapAsPluginError(err, "invalid metadata") return p, nil } if p.Metadata.SchemaVersion != "0.1.0" { - p.Err = errors.Errorf("plugin SchemaVersion %q is not valid, must be 0.1.0", p.Metadata.SchemaVersion) + p.Err = NewPluginError("plugin SchemaVersion %q is not valid, must be 0.1.0", p.Metadata.SchemaVersion) return p, nil } if p.Metadata.Vendor == "" { - p.Err = errors.Errorf("plugin metadata does not define a vendor") + p.Err = NewPluginError("plugin metadata does not define a vendor") return p, nil } return p, nil diff --git a/cli/command/system/info.go b/cli/command/system/info.go index 29380ea1a3e5..c86c04993cd3 100644 --- a/cli/command/system/info.go +++ b/cli/command/system/info.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/docker/cli/cli" + pluginmanager "github.com/docker/cli/cli-plugins/manager" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/debug" "github.com/docker/cli/templates" @@ -23,6 +24,7 @@ type infoOptions struct { type clientInfo struct { Debug bool + Plugins []pluginmanager.Plugin Warnings []string } @@ -47,7 +49,7 @@ func NewInfoCommand(dockerCli command.Cli) *cobra.Command { Short: "Display system-wide information", Args: cli.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - return runInfo(dockerCli, &opts) + return runInfo(cmd, dockerCli, &opts) }, } @@ -58,7 +60,7 @@ func NewInfoCommand(dockerCli command.Cli) *cobra.Command { return cmd } -func runInfo(dockerCli command.Cli, opts *infoOptions) error { +func runInfo(cmd *cobra.Command, dockerCli command.Cli, opts *infoOptions) error { var info info ctx := context.Background() @@ -71,6 +73,11 @@ func runInfo(dockerCli command.Cli, opts *infoOptions) error { info.ClientInfo = &clientInfo{ Debug: debug.IsEnabled(), } + if plugins, err := pluginmanager.ListPlugins(dockerCli, cmd.Root()); err == nil { + info.ClientInfo.Plugins = plugins + } else { + info.ClientErrors = append(info.ClientErrors, err.Error()) + } if opts.format == "" { return prettyPrintInfo(dockerCli, info) @@ -109,6 +116,17 @@ func prettyPrintInfo(dockerCli command.Cli, info info) error { func prettyPrintClientInfo(dockerCli command.Cli, info clientInfo) error { fmt.Fprintln(dockerCli.Out(), " Debug Mode:", info.Debug) + if len(info.Plugins) > 0 { + fmt.Fprintln(dockerCli.Out(), " Plugins:") + for _, p := range info.Plugins { + if p.Err == nil { + fmt.Fprintf(dockerCli.Out(), " %s: (%s, %s) %s\n", p.Name, p.Version, p.Vendor, p.ShortDescription) + } else { + info.Warnings = append(info.Warnings, fmt.Sprintf("WARNING: Plugin %q is not valid: %s", p.Path, p.Err)) + } + } + } + if len(info.Warnings) > 0 { fmt.Fprintln(dockerCli.Err(), strings.Join(info.Warnings, "\n")) } @@ -447,6 +465,11 @@ func getBackingFs(info types.Info) string { } func formatInfo(dockerCli command.Cli, info info, format string) error { + // Ensure slice/array fields render as `[]` not `null` + if info.ClientInfo != nil && info.ClientInfo.Plugins == nil { + info.ClientInfo.Plugins = make([]pluginmanager.Plugin, 0) + } + tmpl, err := templates.Parse(format) if err != nil { return cli.StatusError{StatusCode: 64, diff --git a/cli/command/system/info_test.go b/cli/command/system/info_test.go index d3bcd7e02af6..cb46715e5340 100644 --- a/cli/command/system/info_test.go +++ b/cli/command/system/info_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + pluginmanager "github.com/docker/cli/cli-plugins/manager" "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/registry" @@ -192,6 +193,24 @@ PQQDAgNIADBFAiEAo9fTQNM5DP9bHVcTJYfl2Cay1bFu1E+lnpmN+EYJfeACIGKH }, } +var samplePluginsInfo = []pluginmanager.Plugin{ + { + Name: "goodplugin", + Path: "/path/to/docker-goodplugin", + Metadata: pluginmanager.Metadata{ + SchemaVersion: "0.1.0", + ShortDescription: "unit test is good", + Vendor: "ACME Corp", + Version: "0.1.0", + }, + }, + { + Name: "badplugin", + Path: "/path/to/docker-badplugin", + Err: pluginmanager.NewPluginError("something wrong"), + }, +} + func TestPrettyPrintInfo(t *testing.T) { infoWithSwarm := sampleInfoNoSwarm infoWithSwarm.Swarm = sampleSwarmInfo @@ -228,8 +247,9 @@ func TestPrettyPrintInfo(t *testing.T) { sampleInfoBadSecurity.SecurityOptions = []string{"foo="} for _, tc := range []struct { - doc string - dockerInfo info + doc string + dockerInfo info + prettyGolden string warningsGolden string jsonGolden string @@ -245,6 +265,19 @@ func TestPrettyPrintInfo(t *testing.T) { jsonGolden: "docker-info-no-swarm", }, { + doc: "info with plugins", + dockerInfo: info{ + Info: &sampleInfoNoSwarm, + ClientInfo: &clientInfo{ + Plugins: samplePluginsInfo, + }, + }, + prettyGolden: "docker-info-plugins", + jsonGolden: "docker-info-plugins", + warningsGolden: "docker-info-plugins-warnings", + }, + { + doc: "info with swarm", dockerInfo: info{ Info: &infoWithSwarm, diff --git a/cli/command/system/testdata/docker-info-badsec.json.golden b/cli/command/system/testdata/docker-info-badsec.json.golden index 4b15ac022239..3f3eea239385 100644 --- a/cli/command/system/testdata/docker-info-badsec.json.golden +++ b/cli/command/system/testdata/docker-info-badsec.json.golden @@ -1 +1 @@ -{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["foo="],"Warnings":null,"ServerErrors":["an error happened"],"ClientInfo":{"Debug":false,"Warnings":null}} +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["foo="],"Warnings":null,"ServerErrors":["an error happened"],"ClientInfo":{"Debug":false,"Plugins":[],"Warnings":null}} diff --git a/cli/command/system/testdata/docker-info-daemon-warnings.json.golden b/cli/command/system/testdata/docker-info-daemon-warnings.json.golden index 504939799d6e..0a387bb51072 100644 --- a/cli/command/system/testdata/docker-info-daemon-warnings.json.golden +++ b/cli/command/system/testdata/docker-info-daemon-warnings.json.golden @@ -1 +1 @@ -{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":["WARNING: No memory limit support","WARNING: No swap limit support","WARNING: No kernel memory limit support","WARNING: No oom kill disable support","WARNING: No cpu cfs quota support","WARNING: No cpu cfs period support","WARNING: No cpu shares support","WARNING: No cpuset support","WARNING: IPv4 forwarding is disabled","WARNING: bridge-nf-call-iptables is disabled","WARNING: bridge-nf-call-ip6tables is disabled"],"ClientInfo":{"Debug":true,"Warnings":null}} +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":["WARNING: No memory limit support","WARNING: No swap limit support","WARNING: No kernel memory limit support","WARNING: No oom kill disable support","WARNING: No cpu cfs quota support","WARNING: No cpu cfs period support","WARNING: No cpu shares support","WARNING: No cpuset support","WARNING: IPv4 forwarding is disabled","WARNING: bridge-nf-call-iptables is disabled","WARNING: bridge-nf-call-ip6tables is disabled"],"ClientInfo":{"Debug":true,"Plugins":[],"Warnings":null}} diff --git a/cli/command/system/testdata/docker-info-legacy-warnings.json.golden b/cli/command/system/testdata/docker-info-legacy-warnings.json.golden index 489f700eb3e6..1996dd6d359a 100644 --- a/cli/command/system/testdata/docker-info-legacy-warnings.json.golden +++ b/cli/command/system/testdata/docker-info-legacy-warnings.json.golden @@ -1 +1 @@ -{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":false,"SwapLimit":false,"KernelMemory":false,"KernelMemoryTCP":false,"CpuCfsPeriod":false,"CpuCfsQuota":false,"CPUShares":false,"CPUSet":false,"IPv4Forwarding":false,"BridgeNfIptables":false,"BridgeNfIp6tables":false,"Debug":true,"NFd":33,"OomKillDisable":false,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":true,"Warnings":null}} +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":false,"SwapLimit":false,"KernelMemory":false,"KernelMemoryTCP":false,"CpuCfsPeriod":false,"CpuCfsQuota":false,"CPUShares":false,"CPUSet":false,"IPv4Forwarding":false,"BridgeNfIptables":false,"BridgeNfIp6tables":false,"Debug":true,"NFd":33,"OomKillDisable":false,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":true,"Plugins":[],"Warnings":null}} diff --git a/cli/command/system/testdata/docker-info-no-swarm.json.golden b/cli/command/system/testdata/docker-info-no-swarm.json.golden index b99808753108..7dd6c0eb2a57 100644 --- a/cli/command/system/testdata/docker-info-no-swarm.json.golden +++ b/cli/command/system/testdata/docker-info-no-swarm.json.golden @@ -1 +1 @@ -{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":true,"Warnings":null}} +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":true,"Plugins":[],"Warnings":null}} diff --git a/cli/command/system/testdata/docker-info-plugins-warnings.golden b/cli/command/system/testdata/docker-info-plugins-warnings.golden new file mode 100644 index 000000000000..be6c83426af8 --- /dev/null +++ b/cli/command/system/testdata/docker-info-plugins-warnings.golden @@ -0,0 +1 @@ +WARNING: Plugin "/path/to/docker-badplugin" is not valid: something wrong diff --git a/cli/command/system/testdata/docker-info-plugins.golden b/cli/command/system/testdata/docker-info-plugins.golden new file mode 100644 index 000000000000..8a419965a263 --- /dev/null +++ b/cli/command/system/testdata/docker-info-plugins.golden @@ -0,0 +1,56 @@ +Client: + Debug Mode: false + Plugins: + goodplugin: (0.1.0, ACME Corp) unit test is good + +Server: + Containers: 0 + Running: 0 + Paused: 0 + Stopped: 0 + Images: 0 + Server Version: 17.06.1-ce + Storage Driver: aufs + Root Dir: /var/lib/docker/aufs + Backing Filesystem: extfs + Dirs: 0 + Dirperm1 Supported: true + Logging Driver: json-file + Cgroup Driver: cgroupfs + Plugins: + Volume: local + Network: bridge host macvlan null overlay + Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog + Swarm: inactive + Runtimes: runc + Default Runtime: runc + Init Binary: docker-init + containerd version: 6e23458c129b551d5c9871e5174f6b1b7f6d1170 + runc version: 810190ceaa507aa2727d7ae6f4790c76ec150bd2 + init version: 949e6fa + Security Options: + apparmor + seccomp + Profile: default + Kernel Version: 4.4.0-87-generic + Operating System: Ubuntu 16.04.3 LTS + OSType: linux + Architecture: x86_64 + CPUs: 2 + Total Memory: 1.953GiB + Name: system-sample + ID: EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX + Docker Root Dir: /var/lib/docker + Debug Mode: true + File Descriptors: 33 + Goroutines: 135 + System Time: 2017-08-24T17:44:34.077811894Z + EventsListeners: 0 + Registry: https://index.docker.io/v1/ + Labels: + provider=digitalocean + Experimental: false + Insecure Registries: + 127.0.0.0/8 + Live Restore Enabled: false + diff --git a/cli/command/system/testdata/docker-info-plugins.json.golden b/cli/command/system/testdata/docker-info-plugins.json.golden new file mode 100644 index 000000000000..f7c8435b3b10 --- /dev/null +++ b/cli/command/system/testdata/docker-info-plugins.json.golden @@ -0,0 +1 @@ +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":false,"Plugins":[{"SchemaVersion":"0.1.0","Vendor":"ACME Corp","Version":"0.1.0","ShortDescription":"unit test is good","Name":"goodplugin","Path":"/path/to/docker-goodplugin"},{"Name":"badplugin","Path":"/path/to/docker-badplugin","Err":"something wrong"}],"Warnings":null}} diff --git a/cli/command/system/testdata/docker-info-with-swarm.json.golden b/cli/command/system/testdata/docker-info-with-swarm.json.golden index 58ff6ea2b2fc..da49769c8d64 100644 --- a/cli/command/system/testdata/docker-info-with-swarm.json.golden +++ b/cli/command/system/testdata/docker-info-with-swarm.json.golden @@ -1 +1 @@ -{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"qo2dfdig9mmxqkawulggepdih","NodeAddr":"165.227.107.89","LocalNodeState":"active","ControlAvailable":true,"Error":"","RemoteManagers":[{"NodeID":"qo2dfdig9mmxqkawulggepdih","Addr":"165.227.107.89:2377"}],"Nodes":1,"Managers":1,"Cluster":{"ID":"9vs5ygs0gguyyec4iqf2314c0","Version":{"Index":11},"CreatedAt":"2017-08-24T17:34:19.278062352Z","UpdatedAt":"2017-08-24T17:34:42.398815481Z","Spec":{"Name":"default","Labels":null,"Orchestration":{"TaskHistoryRetentionLimit":5},"Raft":{"SnapshotInterval":10000,"KeepOldSnapshots":0,"LogEntriesForSlowFollowers":500,"ElectionTick":3,"HeartbeatTick":1},"Dispatcher":{"HeartbeatPeriod":5000000000},"CAConfig":{"NodeCertExpiry":7776000000000000},"TaskDefaults":{},"EncryptionConfig":{"AutoLockManagers":true}},"TLSInfo":{"TrustRoot":"\n-----BEGIN CERTIFICATE-----\nMIIBajCCARCgAwIBAgIUaFCW5xsq8eyiJ+Pmcv3MCflMLnMwCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwODI0MTcyOTAwWhcNMzcwODE5MTcy\nOTAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABDy7NebyUJyUjWJDBUdnZoV6GBxEGKO4TZPNDwnxDxJcUdLVaB7WGa4/DLrW\nUfsVgh1JGik2VTiLuTMA1tLlNPOjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBQl16XFtaaXiUAwEuJptJlDjfKskDAKBggqhkjO\nPQQDAgNIADBFAiEAo9fTQNM5DP9bHVcTJYfl2Cay1bFu1E+lnpmN+EYJfeACIGKH\n1pCUkZ+D0IB6CiEZGWSHyLuXPM1rlP+I5KuS7sB8\n-----END CERTIFICATE-----\n","CertIssuerSubject":"MBMxETAPBgNVBAMTCHN3YXJtLWNh","CertIssuerPublicKey":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPLs15vJQnJSNYkMFR2dmhXoYHEQYo7hNk80PCfEPElxR0tVoHtYZrj8MutZR+xWCHUkaKTZVOIu5MwDW0uU08w=="},"RootRotationInProgress":false,"DefaultAddrPool":null,"SubnetSize":0,"DataPathPort":0}},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":false,"Warnings":null}} +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"qo2dfdig9mmxqkawulggepdih","NodeAddr":"165.227.107.89","LocalNodeState":"active","ControlAvailable":true,"Error":"","RemoteManagers":[{"NodeID":"qo2dfdig9mmxqkawulggepdih","Addr":"165.227.107.89:2377"}],"Nodes":1,"Managers":1,"Cluster":{"ID":"9vs5ygs0gguyyec4iqf2314c0","Version":{"Index":11},"CreatedAt":"2017-08-24T17:34:19.278062352Z","UpdatedAt":"2017-08-24T17:34:42.398815481Z","Spec":{"Name":"default","Labels":null,"Orchestration":{"TaskHistoryRetentionLimit":5},"Raft":{"SnapshotInterval":10000,"KeepOldSnapshots":0,"LogEntriesForSlowFollowers":500,"ElectionTick":3,"HeartbeatTick":1},"Dispatcher":{"HeartbeatPeriod":5000000000},"CAConfig":{"NodeCertExpiry":7776000000000000},"TaskDefaults":{},"EncryptionConfig":{"AutoLockManagers":true}},"TLSInfo":{"TrustRoot":"\n-----BEGIN CERTIFICATE-----\nMIIBajCCARCgAwIBAgIUaFCW5xsq8eyiJ+Pmcv3MCflMLnMwCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwODI0MTcyOTAwWhcNMzcwODE5MTcy\nOTAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABDy7NebyUJyUjWJDBUdnZoV6GBxEGKO4TZPNDwnxDxJcUdLVaB7WGa4/DLrW\nUfsVgh1JGik2VTiLuTMA1tLlNPOjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBQl16XFtaaXiUAwEuJptJlDjfKskDAKBggqhkjO\nPQQDAgNIADBFAiEAo9fTQNM5DP9bHVcTJYfl2Cay1bFu1E+lnpmN+EYJfeACIGKH\n1pCUkZ+D0IB6CiEZGWSHyLuXPM1rlP+I5KuS7sB8\n-----END CERTIFICATE-----\n","CertIssuerSubject":"MBMxETAPBgNVBAMTCHN3YXJtLWNh","CertIssuerPublicKey":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPLs15vJQnJSNYkMFR2dmhXoYHEQYo7hNk80PCfEPElxR0tVoHtYZrj8MutZR+xWCHUkaKTZVOIu5MwDW0uU08w=="},"RootRotationInProgress":false,"DefaultAddrPool":null,"SubnetSize":0,"DataPathPort":0}},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":false,"Plugins":[],"Warnings":null}} From 609dcb91527d13948606916539f1d51184304ea8 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Wed, 9 Jan 2019 11:18:25 +0000 Subject: [PATCH 40/60] Documentation on writing a plugin Signed-off-by: Ian Campbell --- cli-plugins/manager/metadata.go | 2 +- docs/extend/cli_plugins.md | 98 +++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 docs/extend/cli_plugins.md diff --git a/cli-plugins/manager/metadata.go b/cli-plugins/manager/metadata.go index 1df3c8e08fde..d3de778141f6 100644 --- a/cli-plugins/manager/metadata.go +++ b/cli-plugins/manager/metadata.go @@ -10,7 +10,7 @@ const ( MetadataSubcommandName = "docker-cli-plugin-metadata" ) -// Metadata provided by the plugin +// Metadata provided by the plugin. See docs/extend/cli_plugins.md for canonical information. type Metadata struct { // SchemaVersion describes the version of this struct. Mandatory, must be "0.1.0" SchemaVersion string `json:",omitempty"` diff --git a/docs/extend/cli_plugins.md b/docs/extend/cli_plugins.md new file mode 100644 index 000000000000..a99204ca583d --- /dev/null +++ b/docs/extend/cli_plugins.md @@ -0,0 +1,98 @@ +--- +description: "Writing Docker CLI Plugins" +keywords: "docker, cli plugin" +--- + + + +# Docker CLI Plugin Spec + +The `docker` CLI supports adding additional top-level subcommands as +additional out-of-process commands which can be installed +independently. These plugins run on the client side and should not be +confused with "plugins" which run on the server. + +This document contains information for authors of such plugins. + +## Requirements for CLI Plugins + +### Naming + +A valid CLI plugin name consists only of lower case letters `a-z` +and the digits `0-9`. The leading character must be a letter. A valid +name therefore would match the regex `^[a-z][a-z0-9]*$`. + +The binary implementing a plugin must be named `docker-$name` where +`$name` is the name of the plugin. On Windows a `.exe` suffix is +mandatory. + +## Required sub-commands + +A CLI plugin must support being invoked in at least these two ways: + +* `docker-$name docker-cli-plugin-metadata` -- outputs metadata about + the plugin. +* `docker-$name [GLOBAL OPTIONS] $name [OPTIONS AND FURTHER SUB + COMMANDS]` -- the primary entry point to the plugin's functionality. + +A plugin may implement other subcommands but these will never be +invoked by the current Docker CLI. However doing so is strongly +discouraged: new subcommands may be added in the future without +consideration for additional non-specified subcommands which may be +used by plugins in the field. + +### The `docker-cli-plugin-metadata` subcommand + +When invoked in this manner the plugin must produce a JSON object +(and nothing else) on its standard output and exit success (0). + +The JSON object has the following defined keys: +* `SchemaVersion` (_string_) mandatory: must contain precisely "0.1.0". +* `Vendor` (_string_) mandatory: contains the name of the plugin vendor/author. May be truncated to 11 characters in some display contexts. +* `ShortDescription` (_string_) optional: a short description of the plugin, suitable for a single line help message. +* `Version` (_string_) optional: the version of the plugin, this is considered to be an opaque string by the core and therefore has no restrictions on its syntax. +* `URL` (_string_) optional: a pointer to the plugin's web page. + +A binary which does not correctly output the metadata +(e.g. syntactically invalid, missing mandatory keys etc) is not +considered a valid CLI plugin and will not be run. + +### The primary entry point subcommand + +This is the entry point for actually running the plugin. It maybe have +options or further subcommands. + +#### Required global options + +A plugin is required to support all of the global options of the +top-level CLI, i.e. those listed by `man docker 1` with the exception +of `-v`. + +## Installation + +Plugins distributed in packages for system wide installation on +Unix(-like) systems should be installed in either +`/usr/lib/docker/cli-plugins` or `/usr/libexec/docker/cli-plugins` +depending on which of `/usr/lib` and `/usr/libexec` is usual on that +system. System Administrators may also choose to manually install into +the `/usr/local/lib` or `/usr/local/libexec` equivalents but packages +should not do so. + +Plugins distributed on Windows for system wide installation should be +installed in `%PROGRAMDATA%\Docker\cli-plugins`. + +User's may on all systems install plugins into `~/.docker/cli-plugins`. + +## Implementing a plugin in Go + +When writing a plugin in Go the easiest way to meet the above +requirements is to simply call the +`github.com/docker/cli/cli-plugins/plugin.Run` method from your `main` +function to instantiate the plugin. From 63f3ad181bd2c6020953ac84256512a9b135c04a Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Mon, 14 Jan 2019 17:53:19 +0000 Subject: [PATCH 41/60] Refactor code which deals with Windows' `.exe` suffix Signed-off-by: Ian Campbell --- cli-plugins/manager/manager.go | 15 ++++----------- cli-plugins/manager/plugin.go | 12 ++++-------- cli-plugins/manager/suffix_unix.go | 10 ++++++++++ cli-plugins/manager/suffix_windows.go | 19 +++++++++++++++++++ 4 files changed, 37 insertions(+), 19 deletions(-) create mode 100644 cli-plugins/manager/suffix_unix.go create mode 100644 cli-plugins/manager/suffix_windows.go diff --git a/cli-plugins/manager/manager.go b/cli-plugins/manager/manager.go index b6ab943595e7..02c8748630d0 100644 --- a/cli-plugins/manager/manager.go +++ b/cli-plugins/manager/manager.go @@ -5,7 +5,6 @@ import ( "os" "os/exec" "path/filepath" - "runtime" "strings" "github.com/docker/cli/cli/command" @@ -61,12 +60,9 @@ func addPluginCandidatesFromDir(res map[string][]string, d string) error { continue } name = strings.TrimPrefix(name, NamePrefix) - if runtime.GOOS == "windows" { - exe := ".exe" - if !strings.HasSuffix(name, exe) { - continue - } - name = strings.TrimSuffix(name, exe) + var err error + if name, err = trimExeSuffix(name); err != nil { + continue } res[name] = append(res[name], filepath.Join(d, dentry.Name())) } @@ -131,10 +127,7 @@ func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command // fallback to their "invalid" command path. return nil, errPluginNotFound(name) } - exename := NamePrefix + name - if runtime.GOOS == "windows" { - exename = exename + ".exe" - } + exename := addExeSuffix(NamePrefix + name) for _, d := range getPluginDirs(dockerCli) { path := filepath.Join(d, exename) diff --git a/cli-plugins/manager/plugin.go b/cli-plugins/manager/plugin.go index 06a0ba8345ed..a8ac4fa3a399 100644 --- a/cli-plugins/manager/plugin.go +++ b/cli-plugins/manager/plugin.go @@ -4,7 +4,6 @@ import ( "encoding/json" "path/filepath" "regexp" - "runtime" "strings" "github.com/pkg/errors" @@ -46,15 +45,12 @@ func newPlugin(c Candidate, rootcmd *cobra.Command) (Plugin, error) { if fullname == "." { return Plugin{}, errors.Errorf("unable to determine basename of plugin candidate %q", path) } - if runtime.GOOS == "windows" { - exe := ".exe" - if !strings.HasSuffix(fullname, exe) { - return Plugin{}, errors.Errorf("plugin candidate %q lacks required %q suffix", path, exe) - } - fullname = strings.TrimSuffix(fullname, exe) + var err error + if fullname, err = trimExeSuffix(fullname); err != nil { + return Plugin{}, errors.Wrapf(err, "plugin candidate %q", path) } if !strings.HasPrefix(fullname, NamePrefix) { - return Plugin{}, errors.Errorf("plugin candidate %q does not have %q prefix", path, NamePrefix) + return Plugin{}, errors.Errorf("plugin candidate %q: does not have %q prefix", path, NamePrefix) } p := Plugin{ diff --git a/cli-plugins/manager/suffix_unix.go b/cli-plugins/manager/suffix_unix.go new file mode 100644 index 000000000000..14f0903f40b7 --- /dev/null +++ b/cli-plugins/manager/suffix_unix.go @@ -0,0 +1,10 @@ +// +build !windows + +package manager + +func trimExeSuffix(s string) (string, error) { + return s, nil +} +func addExeSuffix(s string) string { + return s +} diff --git a/cli-plugins/manager/suffix_windows.go b/cli-plugins/manager/suffix_windows.go new file mode 100644 index 000000000000..d39a6e410d8d --- /dev/null +++ b/cli-plugins/manager/suffix_windows.go @@ -0,0 +1,19 @@ +package manager + +import ( + "strings" + + "github.com/pkg/errors" +) + +func trimExeSuffix(s string) (string, error) { + exe := ".exe" + if !strings.HasSuffix(s, exe) { + return "", errors.Errorf("lacks required %q suffix", exe) + } + return strings.TrimSuffix(s, exe), nil +} + +func addExeSuffix(s string) string { + return s + ".exe" +} From 1337895751efda0a928dcf40a58db6577513c513 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Mon, 28 Jan 2019 17:50:05 +0000 Subject: [PATCH 42/60] Check for `.exe` case insensitively On Windows `foo.exe`, `foo.eXe` and `foo.EXE` are equally executable. Signed-off-by: Ian Campbell --- cli-plugins/manager/suffix_windows.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/cli-plugins/manager/suffix_windows.go b/cli-plugins/manager/suffix_windows.go index d39a6e410d8d..53b507c87dc9 100644 --- a/cli-plugins/manager/suffix_windows.go +++ b/cli-plugins/manager/suffix_windows.go @@ -1,17 +1,24 @@ package manager import ( + "path/filepath" "strings" "github.com/pkg/errors" ) +// This is made slightly more complex due to needing to be case insensitive. func trimExeSuffix(s string) (string, error) { + ext := filepath.Ext(s) + if ext == "" { + return "", errors.Errorf("path %q lacks required file extension", s) + } + exe := ".exe" - if !strings.HasSuffix(s, exe) { - return "", errors.Errorf("lacks required %q suffix", exe) + if !strings.EqualFold(ext, exe) { + return "", errors.Errorf("path %q lacks required %q suffix", s, exe) } - return strings.TrimSuffix(s, exe), nil + return strings.TrimSuffix(s, ext), nil } func addExeSuffix(s string) string { From 935d47bbe967808f69f5b7c72fc848a08f447c2d Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Thu, 17 Jan 2019 13:38:38 +0000 Subject: [PATCH 43/60] Ignore unknown arguments on the top-level command. This allows passing argument to plugins, otherwise they are caught by the parse loop, since cobra does not know about each plugin at this stage (to avoid having to always scan for all plugins) this means that e.g. `docker plugin --foo` would accumulate `plugin` as an arg to the `docker` command, then choke on the unknown `--foo`. This allows unknown global args only, unknown arguments on subcommands (e.g. `docker ps --foo`) are still correctly caught. Add an e2e test covering this case. Signed-off-by: Ian Campbell --- cli-plugins/examples/helloworld/main.go | 5 ++++- cmd/docker/docker.go | 10 ++++++++++ e2e/cli-plugins/run_test.go | 12 ++++++++++++ .../testdata/docker-help-helloworld.golden | 5 ++++- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/cli-plugins/examples/helloworld/main.go b/cli-plugins/examples/helloworld/main.go index 9a511591ec96..cbe015937f96 100644 --- a/cli-plugins/examples/helloworld/main.go +++ b/cli-plugins/examples/helloworld/main.go @@ -33,6 +33,7 @@ func main() { }, } + var who string cmd := &cobra.Command{ Use: "helloworld", Short: "A basic Hello World plugin for tests", @@ -41,9 +42,11 @@ func main() { // hook. PersistentPreRunE: plugin.PersistentPreRunE, Run: func(cmd *cobra.Command, args []string) { - fmt.Fprintln(dockerCli.Out(), "Hello World!") + fmt.Fprintf(dockerCli.Out(), "Hello %s!\n", who) }, } + flags := cmd.Flags() + flags.StringVar(&who, "who", "World", "Who are we addressing?") cmd.AddCommand(goodbye, apiversion) return cmd diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index 1ebf30a89d25..74fab46dcc34 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -32,6 +32,16 @@ func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command { SilenceUsage: true, SilenceErrors: true, TraverseChildren: true, + FParseErrWhitelist: cobra.FParseErrWhitelist{ + // UnknownFlags ignores any unknown + // --arguments on the top-level docker command + // only. This is necessary to allow passing + // --arguments to plugins otherwise + // e.g. `docker plugin --foo` is caught here + // in the monolithic CLI and `foo` is reported + // as an unknown argument. + UnknownFlags: true, + }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { return command.ShowHelp(dockerCli.Err())(cmd, args) diff --git a/e2e/cli-plugins/run_test.go b/e2e/cli-plugins/run_test.go index d16bf3bb8be9..e12c51025d72 100644 --- a/e2e/cli-plugins/run_test.go +++ b/e2e/cli-plugins/run_test.go @@ -144,6 +144,18 @@ func TestRunGoodSubcommand(t *testing.T) { }) } +// TestRunGoodArgument ensures correct behaviour when running a valid plugin with an `--argument`. +func TestRunGoodArgument(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("helloworld", "--who", "Cleveland")) + res.Assert(t, icmd.Expected{ + ExitCode: 0, + Out: "Hello Cleveland!", + }) +} + // TestHelpGoodSubcommand ensures correct behaviour when invoking help on a // valid plugin subcommand. A global argument is included to ensure it does not // interfere. diff --git a/e2e/cli-plugins/testdata/docker-help-helloworld.golden b/e2e/cli-plugins/testdata/docker-help-helloworld.golden index e7252bf2d016..6ff36bc64eae 100644 --- a/e2e/cli-plugins/testdata/docker-help-helloworld.golden +++ b/e2e/cli-plugins/testdata/docker-help-helloworld.golden @@ -1,8 +1,11 @@ -Usage: docker helloworld COMMAND +Usage: docker helloworld [OPTIONS] COMMAND A basic Hello World plugin for tests +Options: + --who string Who are we addressing? (default "World") + Commands: apiversion Print the API version of the server goodbye Say Goodbye instead of Hello From 0a89eb554b662446cbdaf26c8c7916b0ce304460 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Tue, 29 Jan 2019 09:28:43 +0000 Subject: [PATCH 44/60] Ensure plugins default search path obeys `--config` A static global initialiser happens before the arguments are parsed, so we need to calculate the path later. Signed-off-by: Ian Campbell --- cli-plugins/manager/manager.go | 4 +--- cli-plugins/manager/manager_test.go | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/cli-plugins/manager/manager.go b/cli-plugins/manager/manager.go index 02c8748630d0..6fdc582c97d0 100644 --- a/cli-plugins/manager/manager.go +++ b/cli-plugins/manager/manager.go @@ -29,15 +29,13 @@ func IsNotFound(err error) bool { return ok } -var defaultUserPluginDir = config.Path("cli-plugins") - func getPluginDirs(dockerCli command.Cli) []string { var pluginDirs []string if cfg := dockerCli.ConfigFile(); cfg != nil { pluginDirs = append(pluginDirs, cfg.CLIPluginsExtraDirs...) } - pluginDirs = append(pluginDirs, defaultUserPluginDir) + pluginDirs = append(pluginDirs, config.Path("cli-plugins")) pluginDirs = append(pluginDirs, defaultSystemPluginDirs...) return pluginDirs } diff --git a/cli-plugins/manager/manager_test.go b/cli-plugins/manager/manager_test.go index 450ae6120071..14176e57ab1c 100644 --- a/cli-plugins/manager/manager_test.go +++ b/cli-plugins/manager/manager_test.go @@ -4,6 +4,7 @@ import ( "strings" "testing" + "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/internal/test" "gotest.tools/assert" @@ -91,7 +92,7 @@ func TestErrPluginNotFound(t *testing.T) { func TestGetPluginDirs(t *testing.T) { cli := test.NewFakeCli(nil) - expected := []string{defaultUserPluginDir} + expected := []string{config.Path("cli-plugins")} expected = append(expected, defaultSystemPluginDirs...) assert.Equal(t, strings.Join(expected, ":"), strings.Join(getPluginDirs(cli), ":")) From baabf6e8ad4c2a89211160ce6379546bbfb5fdd6 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Wed, 30 Jan 2019 10:46:31 +0000 Subject: [PATCH 45/60] Ensure that plugins are only listed once in help outputs. They were listed twice in `docker --help` (but not `docker help`), since the stubs were added in both `tryRunPluginHelp` and the `setHelpFunc` closure. Calling `AddPluginStubCommands` earlier in `setHelpFunc` before the call to `tryRunPluginHelp` is sufficient. Also it is no longer necessary to add just valid plugins (`tryRunPluginHelp` handles invalid plugins correctly) so remove that logic (which was in any case broken for e.g. `docker --help`). Update the e2e test to check for duplicate entries and also to test `docker --help` which was previously missed. Signed-off-by: Ian Campbell --- cli-plugins/manager/cobra.go | 11 ++++------- cmd/docker/docker.go | 22 ++++++++++----------- e2e/cli-plugins/help_test.go | 38 ++++++++++++++++++++++++++++++------ 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/cli-plugins/manager/cobra.go b/cli-plugins/manager/cobra.go index 302d338a1c99..692de7fdb108 100644 --- a/cli-plugins/manager/cobra.go +++ b/cli-plugins/manager/cobra.go @@ -23,18 +23,15 @@ const ( CommandAnnotationPluginInvalid = "com.docker.cli.plugin-invalid" ) -// AddPluginCommandStubs adds a stub cobra.Commands for each plugin -// (optionally including invalid ones). The command stubs will have -// several annotations added, see `CommandAnnotationPlugin*`. -func AddPluginCommandStubs(dockerCli command.Cli, cmd *cobra.Command, includeInvalid bool) error { +// AddPluginCommandStubs adds a stub cobra.Commands for each valid and invalid +// plugin. The command stubs will have several annotations added, see +// `CommandAnnotationPlugin*`. +func AddPluginCommandStubs(dockerCli command.Cli, cmd *cobra.Command) error { plugins, err := ListPlugins(dockerCli, cmd) if err != nil { return err } for _, p := range plugins { - if !includeInvalid && p.Err != nil { - continue - } vendor := p.Vendor if vendor == "" { vendor = "unknown" diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index 74fab46dcc34..5cccfe2df11f 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -135,7 +135,6 @@ func setupHelpCommand(dockerCli *command.DockerCli, rootCmd, helpCmd *cobra.Comm func tryRunPluginHelp(dockerCli command.Cli, ccmd *cobra.Command, cargs []string) error { root := ccmd.Root() - pluginmanager.AddPluginCommandStubs(dockerCli, root, false) cmd, _, err := root.Traverse(cargs) if err != nil { @@ -155,6 +154,17 @@ func setHelpFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag. ccmd.Println(err) return } + + // Add a stub entry for every plugin so they are + // included in the help output and so that + // `tryRunPluginHelp` can find them or if we fall + // through they will be included in the default help + // output. + if err := pluginmanager.AddPluginCommandStubs(dockerCli, ccmd.Root()); err != nil { + ccmd.Println(err) + return + } + if len(args) >= 1 { err := tryRunPluginHelp(dockerCli, ccmd, args) if err == nil { // Successfully ran the plugin @@ -175,16 +185,6 @@ func setHelpFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag. return } - // Add a stub entry for every plugin so they are - // included in the help output. If we have no args - // then this is being used for `docker help` and we - // want to include broken plugins, otherwise this is - // `help «foo»` and we do not. - if err := pluginmanager.AddPluginCommandStubs(dockerCli, ccmd.Root(), len(args) == 0); err != nil { - ccmd.Println(err) - return - } - defaultHelpFunc(ccmd, args) }) } diff --git a/e2e/cli-plugins/help_test.go b/e2e/cli-plugins/help_test.go index f82959dbab05..6ecdd4c7f087 100644 --- a/e2e/cli-plugins/help_test.go +++ b/e2e/cli-plugins/help_test.go @@ -34,29 +34,55 @@ func TestGlobalHelp(t *testing.T) { // - The `badmeta` plugin under the "Invalid Plugins" heading. // // Regexps are needed because the width depends on `unix.TIOCGWINSZ` or similar. + helloworldre := regexp.MustCompile(`^ helloworld\s+\(Docker Inc\.\)\s+A basic Hello World plugin for tests$`) + badmetare := regexp.MustCompile(`^ badmeta\s+invalid metadata: invalid character 'i' looking for beginning of object key string$`) + var helloworldcount, badmetacount int for _, expected := range []*regexp.Regexp{ regexp.MustCompile(`^A self-sufficient runtime for containers$`), regexp.MustCompile(`^Management Commands:$`), regexp.MustCompile(`^ container\s+Manage containers$`), regexp.MustCompile(`^Commands:$`), regexp.MustCompile(`^ create\s+Create a new container$`), - regexp.MustCompile(`^ helloworld\s+\(Docker Inc\.\)\s+A basic Hello World plugin for tests$`), + helloworldre, regexp.MustCompile(`^ ps\s+List containers$`), regexp.MustCompile(`^Invalid Plugins:$`), - regexp.MustCompile(`^ badmeta\s+invalid metadata: invalid character 'i' looking for beginning of object key string$`), + badmetare, + nil, // scan to end of input rather than stopping at badmetare } { var found bool for scanner.Scan() { - if expected.MatchString(scanner.Text()) { + text := scanner.Text() + if helloworldre.MatchString(text) { + helloworldcount++ + } + if badmetare.MatchString(text) { + badmetacount++ + } + + if expected != nil && expected.MatchString(text) { found = true break } } - assert.Assert(t, found, "Did not find match for %q in `docker help` output", expected) + assert.Assert(t, expected == nil || found, "Did not find match for %q in `docker help` output", expected) } + // We successfully scanned all the input + assert.Assert(t, !scanner.Scan()) + assert.NilError(t, scanner.Err()) + // Plugins should only be listed once. + assert.Assert(t, is.Equal(helloworldcount, 1)) + assert.Assert(t, is.Equal(badmetacount, 1)) + + // Running with `--help` should produce the same. + res2 := icmd.RunCmd(run("--help")) + res2.Assert(t, icmd.Expected{ + ExitCode: 0, + }) + assert.Assert(t, is.Equal(res2.Stdout(), res.Stdout())) + assert.Assert(t, is.Equal(res2.Stderr(), "")) - // Running just `docker` (without help) should produce the same thing, except on Stderr - res2 := icmd.RunCmd(run()) + // Running just `docker` (without `help` nor `--help`) should produce the same thing, except on Stderr. + res2 = icmd.RunCmd(run()) res2.Assert(t, icmd.Expected{ ExitCode: 0, }) From 7a9fc782c52be1e03a0e5d3bb05ec55325d14caa Mon Sep 17 00:00:00 2001 From: Ryan Wilson-Perkin Date: Tue, 29 Jan 2019 17:04:46 -0500 Subject: [PATCH 46/60] Fix small typo Noticed a typo in this markdown file: "instead" instead of "in stead" Signed-off-by: Ryan Wilson-Perkin --- docs/reference/commandline/stats.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/commandline/stats.md b/docs/reference/commandline/stats.md index 702e99a92c1a..44334254c34e 100644 --- a/docs/reference/commandline/stats.md +++ b/docs/reference/commandline/stats.md @@ -171,5 +171,5 @@ On Windows: "table {{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}" -> **Note**: On Docker 17.09 and older, the `{{.Container}}` column was used, in -> stead of `{{.ID}}\t{{.Name}}`. +> **Note**: On Docker 17.09 and older, the `{{.Container}}` column was used, +> instead of `{{.ID}}\t{{.Name}}`. From 27b2797f7deb3ca5b7f80371d825113deb1faca1 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Sun, 15 Oct 2017 21:39:56 +0200 Subject: [PATCH 47/60] Remove docker api dependency from cli/config Signed-off-by: Tonis Tiigi Signed-off-by: Tibor Vass --- cli/command/container/create.go | 2 +- cli/command/container/run.go | 2 +- cli/command/image/build.go | 8 +++++-- cli/command/registry.go | 10 +++++---- cli/command/registry/login.go | 3 ++- cli/command/registry/login_test.go | 11 +++++----- cli/command/registry_test.go | 3 ++- cli/config/config.go | 2 +- cli/config/configfile/file.go | 10 +++++---- cli/config/configfile/file_test.go | 18 +++++++++------- cli/config/credentials/credentials.go | 2 +- cli/config/credentials/file_store.go | 23 ++++++++++++++++++--- cli/config/credentials/file_store_test.go | 2 +- cli/config/credentials/native_store.go | 2 +- cli/config/credentials/native_store_test.go | 2 +- cli/config/types/authconfig.go | 22 ++++++++++++++++++++ internal/test/store.go | 2 +- 17 files changed, 89 insertions(+), 35 deletions(-) create mode 100644 cli/config/types/authconfig.go diff --git a/cli/command/container/create.go b/cli/command/container/create.go index 8f302056c456..49a3d4356cf8 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -62,7 +62,7 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command { } func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, options *createOptions, copts *containerOptions) error { - proxyConfig := dockerCli.ConfigFile().ParseProxyConfig(dockerCli.Client().DaemonHost(), copts.env.GetAll()) + proxyConfig := dockerCli.ConfigFile().ParseProxyConfig(dockerCli.Client().DaemonHost(), opts.ConvertKVStringsToMapWithNil(copts.env.GetAll())) newEnv := []string{} for k, v := range proxyConfig { if v == nil { diff --git a/cli/command/container/run.go b/cli/command/container/run.go index e3f36e0af7d0..e42e8115e177 100644 --- a/cli/command/container/run.go +++ b/cli/command/container/run.go @@ -68,7 +68,7 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command { } func runRun(dockerCli command.Cli, flags *pflag.FlagSet, ropts *runOptions, copts *containerOptions) error { - proxyConfig := dockerCli.ConfigFile().ParseProxyConfig(dockerCli.Client().DaemonHost(), copts.env.GetAll()) + proxyConfig := dockerCli.ConfigFile().ParseProxyConfig(dockerCli.Client().DaemonHost(), opts.ConvertKVStringsToMapWithNil(copts.env.GetAll())) newEnv := []string{} for k, v := range proxyConfig { if v == nil { diff --git a/cli/command/image/build.go b/cli/command/image/build.go index 5dd7aee6c5db..a28e2b3c1939 100644 --- a/cli/command/image/build.go +++ b/cli/command/image/build.go @@ -383,7 +383,11 @@ func runBuild(dockerCli command.Cli, options buildOptions) error { } configFile := dockerCli.ConfigFile() - authConfigs, _ := configFile.GetAllCredentials() + creds, _ := configFile.GetAllCredentials() + authConfigs := make(map[string]types.AuthConfig, len(creds)) + for k, auth := range creds { + authConfigs[k] = types.AuthConfig(auth) + } buildOptions := imageBuildOptions(dockerCli, options) buildOptions.Version = types.BuilderV1 buildOptions.Dockerfile = relDockerfile @@ -619,7 +623,7 @@ func imageBuildOptions(dockerCli command.Cli, options buildOptions) types.ImageB CgroupParent: options.cgroupParent, ShmSize: options.shmSize.Value(), Ulimits: options.ulimits.GetList(), - BuildArgs: configFile.ParseProxyConfig(dockerCli.Client().DaemonHost(), options.buildArgs.GetAll()), + BuildArgs: configFile.ParseProxyConfig(dockerCli.Client().DaemonHost(), opts.ConvertKVStringsToMapWithNil(options.buildArgs.GetAll())), Labels: opts.ConvertKVStringsToMap(options.labels.GetAll()), CacheFrom: options.cacheFrom, SecurityOpt: options.securityOpt, diff --git a/cli/command/registry.go b/cli/command/registry.go index f0276680bd8f..c86edcc79f2b 100644 --- a/cli/command/registry.go +++ b/cli/command/registry.go @@ -11,6 +11,7 @@ import ( "runtime" "strings" + configtypes "github.com/docker/cli/cli/config/types" "github.com/docker/cli/cli/debug" "github.com/docker/cli/cli/streams" "github.com/docker/distribution/reference" @@ -77,7 +78,7 @@ func ResolveAuthConfig(ctx context.Context, cli Cli, index *registrytypes.IndexI } a, _ := cli.ConfigFile().GetAuthConfig(configKey) - return a + return types.AuthConfig(a) } // GetDefaultAuthConfig gets the default auth config given a serverAddress @@ -86,16 +87,17 @@ func GetDefaultAuthConfig(cli Cli, checkCredStore bool, serverAddress string, is if !isDefaultRegistry { serverAddress = registry.ConvertToHostname(serverAddress) } - var authconfig types.AuthConfig + var authconfig configtypes.AuthConfig var err error if checkCredStore { authconfig, err = cli.ConfigFile().GetAuthConfig(serverAddress) } else { - authconfig = types.AuthConfig{} + authconfig = configtypes.AuthConfig{} } authconfig.ServerAddress = serverAddress authconfig.IdentityToken = "" - return &authconfig, err + res := types.AuthConfig(authconfig) + return &res, err } // ConfigureAuth handles prompting of user's username and password if needed diff --git a/cli/command/registry/login.go b/cli/command/registry/login.go index f4f57398bf7e..c35f5f3d6ff0 100644 --- a/cli/command/registry/login.go +++ b/cli/command/registry/login.go @@ -8,6 +8,7 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" + configtypes "github.com/docker/cli/cli/config/types" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/client" @@ -149,7 +150,7 @@ func runLogin(dockerCli command.Cli, opts loginOptions) error { //nolint: gocycl } } - if err := creds.Store(*authConfig); err != nil { + if err := creds.Store(configtypes.AuthConfig(*authConfig)); err != nil { return errors.Errorf("Error saving credentials: %v", err) } diff --git a/cli/command/registry/login_test.go b/cli/command/registry/login_test.go index e260be2b3efb..6cbf3570c614 100644 --- a/cli/command/registry/login_test.go +++ b/cli/command/registry/login_test.go @@ -6,6 +6,7 @@ import ( "fmt" "testing" + configtypes "github.com/docker/cli/cli/config/types" "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" @@ -79,21 +80,21 @@ func TestRunLogin(t *testing.T) { const validPassword = "p1" const validPassword2 = "p2" - validAuthConfig := types.AuthConfig{ + validAuthConfig := configtypes.AuthConfig{ ServerAddress: storedServerAddress, Username: validUsername, Password: validPassword, } - expiredAuthConfig := types.AuthConfig{ + expiredAuthConfig := configtypes.AuthConfig{ ServerAddress: storedServerAddress, Username: validUsername, Password: expiredPassword, } testCases := []struct { inputLoginOption loginOptions - inputStoredCred *types.AuthConfig + inputStoredCred *configtypes.AuthConfig expectedErr string - expectedSavedCred types.AuthConfig + expectedSavedCred configtypes.AuthConfig }{ { inputLoginOption: loginOptions{ @@ -118,7 +119,7 @@ func TestRunLogin(t *testing.T) { }, inputStoredCred: &validAuthConfig, expectedErr: "", - expectedSavedCred: types.AuthConfig{ + expectedSavedCred: configtypes.AuthConfig{ ServerAddress: storedServerAddress, Username: validUsername, Password: validPassword2, diff --git a/cli/command/registry_test.go b/cli/command/registry_test.go index 966db86b91b9..a40c10e0072f 100644 --- a/cli/command/registry_test.go +++ b/cli/command/registry_test.go @@ -13,6 +13,7 @@ import ( // Prevents a circular import with "github.com/docker/cli/internal/test" . "github.com/docker/cli/cli/command" + configtypes "github.com/docker/cli/cli/config/types" "github.com/docker/cli/cli/debug" "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types" @@ -134,7 +135,7 @@ func TestGetDefaultAuthConfig(t *testing.T) { errBuf := new(bytes.Buffer) cli.SetErr(errBuf) for _, authconfig := range testAuthConfigs { - cli.ConfigFile().GetCredentialsStore(authconfig.ServerAddress).Store(authconfig) + cli.ConfigFile().GetCredentialsStore(authconfig.ServerAddress).Store(configtypes.AuthConfig(authconfig)) } for _, tc := range testCases { serverAddress := tc.inputServerAddress diff --git a/cli/config/config.go b/cli/config/config.go index f773abee4091..2a6c5f0ea46f 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -8,7 +8,7 @@ import ( "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/config/credentials" - "github.com/docker/docker/api/types" + "github.com/docker/cli/cli/config/types" "github.com/docker/docker/pkg/homedir" "github.com/pkg/errors" ) diff --git a/cli/config/configfile/file.go b/cli/config/configfile/file.go index 99ffd47a5fcb..656184993faa 100644 --- a/cli/config/configfile/file.go +++ b/cli/config/configfile/file.go @@ -11,8 +11,7 @@ import ( "strings" "github.com/docker/cli/cli/config/credentials" - "github.com/docker/cli/opts" - "github.com/docker/docker/api/types" + "github.com/docker/cli/cli/config/types" "github.com/pkg/errors" ) @@ -196,7 +195,7 @@ func (configFile *ConfigFile) Save() error { // ParseProxyConfig computes proxy configuration by retrieving the config for the provided host and // then checking this against any environment variables provided to the container -func (configFile *ConfigFile) ParseProxyConfig(host string, runOpts []string) map[string]*string { +func (configFile *ConfigFile) ParseProxyConfig(host string, runOpts map[string]*string) map[string]*string { var cfgKey string if _, ok := configFile.Proxies[host]; !ok { @@ -212,7 +211,10 @@ func (configFile *ConfigFile) ParseProxyConfig(host string, runOpts []string) ma "NO_PROXY": &config.NoProxy, "FTP_PROXY": &config.FTPProxy, } - m := opts.ConvertKVStringsToMapWithNil(runOpts) + m := runOpts + if m == nil { + m = make(map[string]*string) + } for k := range permitted { if *permitted[k] == "" { continue diff --git a/cli/config/configfile/file_test.go b/cli/config/configfile/file_test.go index 765a9f9cd9c1..5c21e8c4261c 100644 --- a/cli/config/configfile/file_test.go +++ b/cli/config/configfile/file_test.go @@ -1,13 +1,12 @@ package configfile import ( - "fmt" "io/ioutil" "os" "testing" "github.com/docker/cli/cli/config/credentials" - "github.com/docker/docker/api/types" + "github.com/docker/cli/cli/config/types" "gotest.tools/assert" is "gotest.tools/assert/cmp" ) @@ -41,7 +40,7 @@ func TestProxyConfig(t *testing.T) { }, } - proxyConfig := cfg.ParseProxyConfig("/var/run/docker.sock", []string{}) + proxyConfig := cfg.ParseProxyConfig("/var/run/docker.sock", nil) expected := map[string]*string{ "HTTP_PROXY": &httpProxy, "http_proxy": &httpProxy, @@ -75,9 +74,14 @@ func TestProxyConfigOverride(t *testing.T) { }, } - ropts := []string{ - fmt.Sprintf("HTTP_PROXY=%s", overrideHTTPProxy), - "NO_PROXY=", + clone := func(s string) *string { + s2 := s + return &s2 + } + + ropts := map[string]*string{ + "HTTP_PROXY": clone(overrideHTTPProxy), + "NO_PROXY": clone(overrideNoProxy), } proxyConfig := cfg.ParseProxyConfig("/var/run/docker.sock", ropts) expected := map[string]*string{ @@ -124,7 +128,7 @@ func TestProxyConfigPerHost(t *testing.T) { }, } - proxyConfig := cfg.ParseProxyConfig("tcp://example.docker.com:2376", []string{}) + proxyConfig := cfg.ParseProxyConfig("tcp://example.docker.com:2376", nil) expected := map[string]*string{ "HTTP_PROXY": &extHTTPProxy, "http_proxy": &extHTTPProxy, diff --git a/cli/config/credentials/credentials.go b/cli/config/credentials/credentials.go index ca874cac5134..28d58ec48d7d 100644 --- a/cli/config/credentials/credentials.go +++ b/cli/config/credentials/credentials.go @@ -1,7 +1,7 @@ package credentials import ( - "github.com/docker/docker/api/types" + "github.com/docker/cli/cli/config/types" ) // Store is the interface that any credentials store must implement. diff --git a/cli/config/credentials/file_store.go b/cli/config/credentials/file_store.go index 6ae681754a5a..e509820b73f0 100644 --- a/cli/config/credentials/file_store.go +++ b/cli/config/credentials/file_store.go @@ -1,8 +1,9 @@ package credentials import ( - "github.com/docker/docker/api/types" - "github.com/docker/docker/registry" + "strings" + + "github.com/docker/cli/cli/config/types" ) type store interface { @@ -35,7 +36,7 @@ func (c *fileStore) Get(serverAddress string) (types.AuthConfig, error) { // Maybe they have a legacy config file, we will iterate the keys converting // them to the new format and testing for r, ac := range c.file.GetAuthConfigs() { - if serverAddress == registry.ConvertToHostname(r) { + if serverAddress == ConvertToHostname(r) { return ac, nil } } @@ -62,3 +63,19 @@ func (c *fileStore) GetFilename() string { func (c *fileStore) IsFileStore() bool { return true } + +// ConvertToHostname converts a registry url which has http|https prepended +// to just an hostname. +// Copied from github.com/docker/docker/registry.ConvertToHostname to reduce dependencies. +func ConvertToHostname(url string) string { + stripped := url + if strings.HasPrefix(url, "http://") { + stripped = strings.TrimPrefix(url, "http://") + } else if strings.HasPrefix(url, "https://") { + stripped = strings.TrimPrefix(url, "https://") + } + + nameParts := strings.SplitN(stripped, "/", 2) + + return nameParts[0] +} diff --git a/cli/config/credentials/file_store_test.go b/cli/config/credentials/file_store_test.go index eb27d3774f32..df7fc8b04c66 100644 --- a/cli/config/credentials/file_store_test.go +++ b/cli/config/credentials/file_store_test.go @@ -3,7 +3,7 @@ package credentials import ( "testing" - "github.com/docker/docker/api/types" + "github.com/docker/cli/cli/config/types" "gotest.tools/assert" is "gotest.tools/assert/cmp" ) diff --git a/cli/config/credentials/native_store.go b/cli/config/credentials/native_store.go index ef3aab4ad309..afe542cc3ce9 100644 --- a/cli/config/credentials/native_store.go +++ b/cli/config/credentials/native_store.go @@ -1,9 +1,9 @@ package credentials import ( + "github.com/docker/cli/cli/config/types" "github.com/docker/docker-credential-helpers/client" "github.com/docker/docker-credential-helpers/credentials" - "github.com/docker/docker/api/types" ) const ( diff --git a/cli/config/credentials/native_store_test.go b/cli/config/credentials/native_store_test.go index 88befab5eef9..4d6340bc11ea 100644 --- a/cli/config/credentials/native_store_test.go +++ b/cli/config/credentials/native_store_test.go @@ -8,9 +8,9 @@ import ( "strings" "testing" + "github.com/docker/cli/cli/config/types" "github.com/docker/docker-credential-helpers/client" "github.com/docker/docker-credential-helpers/credentials" - "github.com/docker/docker/api/types" "github.com/pkg/errors" "gotest.tools/assert" is "gotest.tools/assert/cmp" diff --git a/cli/config/types/authconfig.go b/cli/config/types/authconfig.go new file mode 100644 index 000000000000..056af6b84259 --- /dev/null +++ b/cli/config/types/authconfig.go @@ -0,0 +1,22 @@ +package types + +// AuthConfig contains authorization information for connecting to a Registry +type AuthConfig struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Auth string `json:"auth,omitempty"` + + // Email is an optional value associated with the username. + // This field is deprecated and will be removed in a later + // version of docker. + Email string `json:"email,omitempty"` + + ServerAddress string `json:"serveraddress,omitempty"` + + // IdentityToken is used to authenticate the user and get + // an access token for the registry. + IdentityToken string `json:"identitytoken,omitempty"` + + // RegistryToken is a bearer token to be sent to a registry + RegistryToken string `json:"registrytoken,omitempty"` +} diff --git a/internal/test/store.go b/internal/test/store.go index 35565dc68ebd..802ea6a026a6 100644 --- a/internal/test/store.go +++ b/internal/test/store.go @@ -2,7 +2,7 @@ package test import ( "github.com/docker/cli/cli/config/credentials" - "github.com/docker/docker/api/types" + "github.com/docker/cli/cli/config/types" ) // FakeStore implements a credentials.Store that only acts as an in memory map From 3bd3996f72ca281cec288dd6e7f4fdaa0e1eeb00 Mon Sep 17 00:00:00 2001 From: Silvin Lubecki Date: Thu, 24 Jan 2019 17:53:42 +0100 Subject: [PATCH 48/60] Use gotest.tools/gotestsum binary to run unit and e2e tests and simplify the output. Signed-off-by: Silvin Lubecki --- dockerfiles/Dockerfile.dev | 5 +++++ dockerfiles/Dockerfile.e2e | 6 ++++++ scripts/test/e2e/run | 4 ++-- scripts/test/unit | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/dockerfiles/Dockerfile.dev b/dockerfiles/Dockerfile.dev index af6da4b156cf..778cec876c72 100644 --- a/dockerfiles/Dockerfile.dev +++ b/dockerfiles/Dockerfile.dev @@ -16,6 +16,11 @@ RUN go get -d github.com/mjibson/esc && \ go build -v -o /usr/bin/esc . && \ rm -rf /go/src/* /go/pkg/* /go/bin/* +ARG GOTESTSUM_VERSION=0.3.2 +RUN curl -Ls https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_linux_amd64.tar.gz -o gotestsum.tar.gz && \ + tar -xf gotestsum.tar.gz gotestsum -C /usr/bin && \ + rm gotestsum.tar.gz + ENV CGO_ENABLED=0 \ PATH=$PATH:/go/src/github.com/docker/cli/build \ DISABLE_WARN_OUTSIDE_CONTAINER=1 diff --git a/dockerfiles/Dockerfile.e2e b/dockerfiles/Dockerfile.e2e index eedda3b7f59d..ccbcda2d6f04 100644 --- a/dockerfiles/Dockerfile.e2e +++ b/dockerfiles/Dockerfile.e2e @@ -24,6 +24,12 @@ ARG NOTARY_VERSION=v0.6.1 RUN curl -Ls https://github.com/theupdateframework/notary/releases/download/${NOTARY_VERSION}/notary-Linux-amd64 -o /usr/local/bin/notary \ && chmod +x /usr/local/bin/notary +ARG GOTESTSUM_VERSION=0.3.2 +RUN curl -Ls https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_linux_amd64.tar.gz -o gotestsum.tar.gz \ + && tar -xf gotestsum.tar.gz gotestsum \ + && mv gotestsum /usr/local/bin/gotestsum \ + && rm gotestsum.tar.gz + ENV CGO_ENABLED=0 \ DISABLE_WARN_OUTSIDE_CONTAINER=1 \ PATH=/go/src/github.com/docker/cli/build:$PATH diff --git a/scripts/test/e2e/run b/scripts/test/e2e/run index d494019419fb..bdff2ce3e34e 100755 --- a/scripts/test/e2e/run +++ b/scripts/test/e2e/run @@ -68,9 +68,9 @@ function runtests { TEST_REMOTE_DAEMON="${REMOTE_DAEMON-}" \ TEST_SKIP_PLUGIN_TESTS="${SKIP_PLUGIN_TESTS-}" \ GOPATH="$GOPATH" \ - PATH="$PWD/build/:/usr/bin" \ + PATH="$PWD/build/:/usr/bin:/usr/local/bin:/usr/local/go/bin" \ DOCKER_CLI_E2E_PLUGINS_EXTRA_DIRS="$PWD/build/plugins-linux-amd64" \ - "$(which go)" test -v ./e2e/... ${TESTFLAGS-} + "$(which gotestsum)" -- ./e2e/... ${TESTFLAGS-} } export unique_id="${E2E_UNIQUE_ID:-cliendtoendsuite}" diff --git a/scripts/test/unit b/scripts/test/unit index 7eb82d0ff037..6e0b6d5cbad0 100755 --- a/scripts/test/unit +++ b/scripts/test/unit @@ -1,4 +1,4 @@ #!/usr/bin/env bash set -eu -o pipefail -go test -v "$@" +gotestsum -- "$@" From 277f61415ec99d5fbae75c15013f2fdfb0017af4 Mon Sep 17 00:00:00 2001 From: Silvin Lubecki Date: Mon, 28 Jan 2019 10:27:35 +0100 Subject: [PATCH 49/60] Better coverage output, removing unnecessary unit scripts. Signed-off-by: Silvin Lubecki --- Makefile | 6 +++--- scripts/test/unit | 4 ---- scripts/test/unit-with-coverage | 20 -------------------- 3 files changed, 3 insertions(+), 27 deletions(-) delete mode 100755 scripts/test/unit delete mode 100755 scripts/test/unit-with-coverage diff --git a/Makefile b/Makefile index aaf5317fe60e..10f07793fb19 100644 --- a/Makefile +++ b/Makefile @@ -11,15 +11,15 @@ clean: ## remove build artifacts rm -rf ./build/* cli/winresources/rsrc_* ./man/man[1-9] docs/yaml/gen .PHONY: test-unit -test-unit: ## run unit test - ./scripts/test/unit $(shell go list ./... | grep -vE '/vendor/|/e2e/') +test-unit: ## run unit tests, to change the output format use: GOTESTSUM_FORMAT=(dots|short|standard-quiet|short-verbose|standard-verbose) make test-unit + gotestsum -- $(shell go list ./... | grep -vE '/vendor/|/e2e/') .PHONY: test test: test-unit ## run tests .PHONY: test-coverage test-coverage: ## run test coverage - ./scripts/test/unit-with-coverage $(shell go list ./... | grep -vE '/vendor/|/e2e/') + gotestsum -- -coverprofile=coverage.txt $(shell go list ./... | grep -vE '/vendor/|/e2e/') .PHONY: fmt fmt: diff --git a/scripts/test/unit b/scripts/test/unit deleted file mode 100755 index 6e0b6d5cbad0..000000000000 --- a/scripts/test/unit +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail - -gotestsum -- "$@" diff --git a/scripts/test/unit-with-coverage b/scripts/test/unit-with-coverage deleted file mode 100755 index db2efe785304..000000000000 --- a/scripts/test/unit-with-coverage +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail - -# install test dependencies once before running tests for each package. This -# reduces the runtime from 200s down to 23s -go test -i "$@" - -echo "mode: atomic" > coverage.txt -for pkg in "$@"; do - ./scripts/test/unit \ - -cover \ - -coverprofile=profile.out \ - -covermode=atomic \ - "${pkg}" - - if test -f profile.out; then - grep -v "^mode:" < profile.out >> coverage.txt || true - rm profile.out - fi -done From ff5a83c3aa71fe5f006824edd22792b529a7ce8d Mon Sep 17 00:00:00 2001 From: Silvin Lubecki Date: Thu, 31 Jan 2019 17:31:40 +0100 Subject: [PATCH 50/60] Store the junit.xml file produced while running the unit tests. Signed-off-by: Silvin Lubecki --- circle.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index 3b4d9259e9d1..9895c317990d 100644 --- a/circle.yml +++ b/circle.yml @@ -56,11 +56,16 @@ jobs: - run: name: "Unit Test with Coverage" command: | + mkdir test-results docker build -f dockerfiles/Dockerfile.dev --tag cli-builder:$CIRCLE_BUILD_NUM . - docker run --name \ + docker run \ + -e GOTESTSUM_JUNITFILE=/tmp/junit.xml \ + --name \ test-$CIRCLE_BUILD_NUM cli-builder:$CIRCLE_BUILD_NUM \ make test-coverage - + docker cp \ + test-$CIRCLE_BUILD_NUM:/tmp/junit.xml \ + /work/test-results/unit-tests - run: name: "Upload to Codecov" command: | @@ -70,6 +75,8 @@ jobs: apk add -U bash curl curl -s https://codecov.io/bash | bash || \ echo 'Codecov failed to upload' + - store_test_results: + path: test-results validate: working_directory: /work From 26c598801bf721a0766b050f08d222051eb62f44 Mon Sep 17 00:00:00 2001 From: Silvin Lubecki Date: Fri, 1 Feb 2019 11:56:27 +0100 Subject: [PATCH 51/60] Fix storing test results on circle ci. Signed-off-by: Silvin Lubecki --- circle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index 9895c317990d..c5dc2b8ade6e 100644 --- a/circle.yml +++ b/circle.yml @@ -56,7 +56,7 @@ jobs: - run: name: "Unit Test with Coverage" command: | - mkdir test-results + mkdir -p test-results/unit-tests docker build -f dockerfiles/Dockerfile.dev --tag cli-builder:$CIRCLE_BUILD_NUM . docker run \ -e GOTESTSUM_JUNITFILE=/tmp/junit.xml \ @@ -65,7 +65,7 @@ jobs: make test-coverage docker cp \ test-$CIRCLE_BUILD_NUM:/tmp/junit.xml \ - /work/test-results/unit-tests + ./test-results/unit-tests/junit.xml - run: name: "Upload to Codecov" command: | From f353eeb544b969590e05b404dd0da87179611510 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 10 Dec 2018 23:21:07 +0100 Subject: [PATCH 52/60] Update containerd to 1.2.1 Signed-off-by: Sebastiaan van Stijn --- internal/containerizedengine/client_test.go | 8 + vendor.conf | 13 +- vendor/github.com/Microsoft/go-winio/ea.go | 274 +++++++------- .../Microsoft/hcsshim/hnsendpoint.go | 3 + .../Microsoft/hcsshim/hnspolicylist.go | 4 +- .../github.com/Microsoft/hcsshim/interface.go | 5 + .../hcsshim/internal/guestrequest/types.go | 85 +++++ .../Microsoft/hcsshim/internal/hcs/hcs.go | 1 + .../Microsoft/hcsshim/internal/hcs/process.go | 34 ++ .../hcsshim/internal/hcs/zsyscall_windows.go | 33 +- .../hcsshim/internal/hns/hnspolicylist.go | 4 +- .../hcsshim/internal/schema1/schema1.go | 23 +- .../hcsshim/internal/schema2/attachment.go | 31 ++ .../hcsshim/internal/schema2/battery.go | 13 + .../schema2/cache_query_stats_response.go | 19 + .../hcsshim/internal/schema2/chipset.go | 25 ++ .../hcsshim/internal/schema2/close_handle.go | 15 + .../hcsshim/internal/schema2/com_port.go | 18 + .../internal/schema2/compute_system.go | 27 ++ .../hcsshim/internal/schema2/configuration.go | 72 ++++ .../hcsshim/internal/schema2/console_size.go | 17 + .../hcsshim/internal/schema2/container.go | 35 ++ .../container_credential_guard_state.go | 25 ++ .../schema2/container_memory_information.go | 26 ++ .../hcsshim/internal/schema2/device.go | 16 + .../hcsshim/internal/schema2/devices.go | 43 +++ .../internal/schema2/enhanced_mode_video.go | 15 + .../internal/schema2/flexible_io_device.go | 19 + .../internal/schema2/guest_connection.go | 19 + .../internal/schema2/guest_connection_info.go | 21 ++ .../internal/schema2/guest_crash_reporting.go | 15 + .../hcsshim/internal/schema2/guest_os.go | 15 + .../hcsshim/internal/schema2/guest_state.go | 22 ++ .../hcsshim/internal/schema2/hosted_system.go | 17 + .../hcsshim/internal/schema2/hv_socket.go | 17 + .../hcsshim/internal/schema2/hv_socket_2.go | 16 + .../schema2/hv_socket_service_config.go | 22 ++ .../schema2/hv_socket_system_config.go | 22 ++ .../hcsshim/internal/schema2/keyboard.go | 13 + .../hcsshim/internal/schema2/layer.go | 22 ++ .../internal/schema2/mapped_directory.go | 21 ++ .../hcsshim/internal/schema2/mapped_pipe.go | 19 + .../hcsshim/internal/schema2/memory.go | 15 + .../hcsshim/internal/schema2/memory_2.go | 25 ++ .../schema2/memory_information_for_vm.go | 19 + .../hcsshim/internal/schema2/memory_stats.go | 20 ++ .../schema2/modify_setting_request.go | 20 ++ .../hcsshim/internal/schema2/mouse.go | 13 + .../internal/schema2/network_adapter.go | 17 + .../hcsshim/internal/schema2/networking.go | 24 ++ .../internal/schema2/pause_notification.go | 16 + .../hcsshim/internal/schema2/pause_options.go | 18 + .../hcsshim/internal/schema2/plan9.go | 15 + .../hcsshim/internal/schema2/plan9_share.go | 26 ++ .../internal/schema2/process_details.go | 34 ++ .../schema2/process_modify_request.go | 20 ++ .../internal/schema2/process_parameters.go | 47 +++ .../internal/schema2/process_status.go | 22 ++ .../hcsshim/internal/schema2/processor.go | 19 + .../hcsshim/internal/schema2/processor_2.go | 21 ++ .../internal/schema2/processor_stats.go | 20 ++ .../hcsshim/internal/schema2/properties.go | 47 +++ .../internal/schema2/property_query.go | 16 + .../schema2/rdp_connection_options.go | 17 + .../internal/schema2/registry_changes.go | 17 + .../hcsshim/internal/schema2/registry_key.go | 19 + .../internal/schema2/registry_value.go | 31 ++ .../hcsshim/internal/schema2/restore_state.go | 19 + .../hcsshim/internal/schema2/save_options.go | 19 + .../hcsshim/internal/schema2/scsi.go | 16 + .../schema2/shared_memory_configuration.go | 15 + .../internal/schema2/shared_memory_region.go | 23 ++ .../schema2/shared_memory_region_info.go | 17 + .../internal/schema2/silo_properties.go | 18 + .../hcsshim/internal/schema2/statistics.go | 30 ++ .../hcsshim/internal/schema2/storage.go | 21 ++ .../hcsshim/internal/schema2/storage_qo_s.go | 17 + .../hcsshim/internal/schema2/storage_stats.go | 22 ++ .../hcsshim/internal/schema2/topology.go | 17 + .../hcsshim/internal/schema2/uefi.go | 21 ++ .../internal/schema2/uefi_boot_entry.go | 23 ++ .../hcsshim/internal/schema2/version.go | 17 + .../hcsshim/internal/schema2/video_monitor.go | 19 + .../internal/schema2/virtual_machine.go | 29 ++ .../internal/schema2/virtual_node_info.go | 21 ++ .../schema2/virtual_p_mem_controller.go | 20 ++ .../internal/schema2/virtual_p_mem_device.go | 19 + .../hcsshim/internal/schema2/virtual_smb.go | 17 + .../internal/schema2/virtual_smb_share.go | 21 ++ .../schema2/virtual_smb_share_options.go | 63 ++++ .../hcsshim/internal/schema2/vm_memory.go | 27 ++ .../schema2/windows_crash_reporting.go | 17 + .../hcsshim/internal/wclayer/createlayer.go | 6 +- vendor/github.com/Microsoft/hcsshim/layer.go | 2 + .../github.com/Microsoft/hcsshim/version.go | 6 - .../containerd/containerd/README.md | 15 +- .../api/services/content/v1/content.pb.go | 4 +- .../api/services/content/v1/content.proto | 2 +- .../archive/compression/compression.go | 137 ++++++- .../containerd/archive/time_unix.go | 2 +- .../containerd/containerd/container_opts.go | 17 + .../containerd/containers/containers.go | 4 +- .../containerd/containerd/content/content.go | 7 +- .../containerd/containerd/content/helpers.go | 6 +- .../content/proxy/content_writer.go | 6 +- .../containerd/containerd/events.go | 3 + .../containerd/containerd/export.go | 3 +- .../github.com/containerd/containerd/image.go | 6 + .../containerd/images/archive/importer.go | 262 ++++++++++++++ .../containerd/images/archive/reference.go | 86 +++++ .../containerd/containerd/images/image.go | 9 +- .../containerd/images/importexport.go | 2 +- .../containerd/containerd/import.go | 133 +++++-- .../containerd/containerd/mount/mount_unix.go | 2 +- ...{mountinfo_freebsd.go => mountinfo_bsd.go} | 2 + .../containerd/mount/mountinfo_unsupported.go | 2 +- .../containerd/containerd/oci/spec.go | 3 +- .../containerd/containerd/oci/spec_opts.go | 23 ++ .../containerd/platforms/cpuinfo.go | 16 + .../containerd/remotes/docker/authorizer.go | 313 ++++++++++++++++ .../containerd/remotes/docker/resolver.go | 338 ++++-------------- .../remotes/docker/schema1/converter.go | 22 +- .../containerd/containerd/signal_map_linux.go | 60 ++++ .../containerd/containerd/signal_map_unix.go | 58 +++ .../containerd/signal_map_windows.go | 39 ++ .../containerd/containerd/signals.go | 105 ++++++ .../containerd/containerd/vendor.conf | 26 +- vendor/github.com/containerd/cri/LICENSE | 201 +++++++++++ vendor/github.com/containerd/cri/README.md | 176 +++++++++ .../containerd/cri/pkg/util/deep_copy.go | 42 +++ .../github.com/containerd/cri/pkg/util/id.go | 29 ++ .../containerd/cri/pkg/util/image.go | 50 +++ .../containerd/cri/pkg/util/strings.go | 59 +++ vendor/github.com/containerd/cri/vendor.conf | 78 ++++ vendor/github.com/containerd/typeurl/types.go | 16 + .../github.com/opencontainers/runc/README.md | 17 + .../runc/libcontainer/README.md | 8 +- .../runc/libcontainer/nsenter/README.md | 6 +- .../runc/libcontainer/nsenter/nsexec.c | 77 ++-- .../runc/libcontainer/user/lookup_unix.go | 22 +- .../opencontainers/runc/vendor.conf | 2 +- 141 files changed, 4224 insertions(+), 556 deletions(-) create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/guestrequest/types.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/attachment.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/battery.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/cache_query_stats_response.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/chipset.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/close_handle.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/com_port.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/compute_system.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/configuration.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/console_size.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/container.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/container_credential_guard_state.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/container_memory_information.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/device.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/devices.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/enhanced_mode_video.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/flexible_io_device.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_connection.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_connection_info.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_crash_reporting.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_os.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_state.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/hosted_system.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_2.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_service_config.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_system_config.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/keyboard.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/layer.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/mapped_directory.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/mapped_pipe.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/memory.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_2.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_information_for_vm.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_stats.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/modify_setting_request.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/mouse.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/network_adapter.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/networking.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/pause_notification.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/pause_options.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/plan9.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/plan9_share.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/process_details.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/process_modify_request.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/process_parameters.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/process_status.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/processor.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/processor_2.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/processor_stats.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/properties.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/property_query.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/rdp_connection_options.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_changes.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_key.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_value.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/restore_state.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/save_options.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/scsi.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_configuration.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_region.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_region_info.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/silo_properties.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/statistics.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/storage.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/storage_qo_s.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/storage_stats.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/topology.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/uefi.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/uefi_boot_entry.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/version.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/video_monitor.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_machine.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_node_info.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_controller.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_device.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb_share.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb_share_options.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/vm_memory.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/windows_crash_reporting.go delete mode 100644 vendor/github.com/Microsoft/hcsshim/version.go create mode 100644 vendor/github.com/containerd/containerd/images/archive/importer.go create mode 100644 vendor/github.com/containerd/containerd/images/archive/reference.go rename vendor/github.com/containerd/containerd/mount/{mountinfo_freebsd.go => mountinfo_bsd.go} (98%) create mode 100644 vendor/github.com/containerd/containerd/remotes/docker/authorizer.go create mode 100644 vendor/github.com/containerd/containerd/signal_map_linux.go create mode 100644 vendor/github.com/containerd/containerd/signal_map_unix.go create mode 100644 vendor/github.com/containerd/containerd/signal_map_windows.go create mode 100644 vendor/github.com/containerd/containerd/signals.go create mode 100644 vendor/github.com/containerd/cri/LICENSE create mode 100644 vendor/github.com/containerd/cri/README.md create mode 100644 vendor/github.com/containerd/cri/pkg/util/deep_copy.go create mode 100644 vendor/github.com/containerd/cri/pkg/util/id.go create mode 100644 vendor/github.com/containerd/cri/pkg/util/image.go create mode 100644 vendor/github.com/containerd/cri/pkg/util/strings.go create mode 100644 vendor/github.com/containerd/cri/vendor.conf diff --git a/internal/containerizedengine/client_test.go b/internal/containerizedengine/client_test.go index bb3fae6a5f87..af5f5878fd60 100644 --- a/internal/containerizedengine/client_test.go +++ b/internal/containerizedengine/client_test.go @@ -40,6 +40,7 @@ type ( fakeImage struct { nameFunc func() string targetFunc func() ocispec.Descriptor + labelFunc func() map[string]string unpackFunc func(context.Context, string) error rootFSFunc func(ctx context.Context) ([]digest.Digest, error) sizeFunc func(ctx context.Context) (int64, error) @@ -180,6 +181,13 @@ func (i *fakeImage) Target() ocispec.Descriptor { } return ocispec.Descriptor{} } +func (i *fakeImage) Labels() map[string]string { + if i.labelFunc != nil { + return i.labelFunc() + } + return nil +} + func (i *fakeImage) Unpack(ctx context.Context, name string) error { if i.unpackFunc != nil { return i.unpackFunc(ctx, name) diff --git a/vendor.conf b/vendor.conf index 88d21f77836d..049b6e16eb14 100755 --- a/vendor.conf +++ b/vendor.conf @@ -4,10 +4,11 @@ github.com/asaskevich/govalidator f9ffefc3facfbe0caee3fea233cbb6e8208f4541 github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109 github.com/beorn7/perks 3a771d992973f24aa725d07868b467d1ddfceafb github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 -github.com/containerd/containerd d97a907f7f781c0ab8340877d8e6b53cc7f1c2f6 +github.com/containerd/containerd 9b32062dc1f5a7c2564315c269b5059754f12b9d # v1.2.1 github.com/containerd/continuity f44b615e492bdfb371aae2f76ec694d9da1db537 -github.com/containerd/fifo 3d5202a -github.com/containerd/typeurl f694355 +github.com/containerd/cri 0ca1e3c2b73b5c38e72f29bb76338d0078b23d6c # release/1.2 branch +github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c +github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 github.com/coreos/etcd v3.3.9 github.com/cpuguy83/go-md2man v1.0.8 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 # v1.1.0 @@ -49,8 +50,8 @@ github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 # github.com/json-iterator/go 1624edc4454b8682399def8740d46db5e4362ba4 # 1.1.5 github.com/mattn/go-shellwords v1.0.3 github.com/matttproud/golang_protobuf_extensions v1.0.1 -github.com/Microsoft/hcsshim v0.7.3 -github.com/Microsoft/go-winio v0.4.10 +github.com/Microsoft/hcsshim v0.8.1 +github.com/Microsoft/go-winio v0.4.11 github.com/miekg/pkcs11 287d9350987cc9334667882061e202e96cdfb4d0 github.com/mitchellh/mapstructure f15292f7a699fcc1a38a80977f80a046874ba8ac github.com/moby/buildkit 520201006c9dc676da9cf9655337ac711f7f127d @@ -60,7 +61,7 @@ github.com/morikuni/aec 39771216ff4c63d11f5e604076f9c45e8be1067b github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github.com/ijc25/Gotty github.com/opencontainers/go-digest v1.0.0-rc1 github.com/opencontainers/image-spec v1.0.1 -github.com/opencontainers/runc 20aff4f0488c6d4b8df4d85b4f63f1f704c11abd +github.com/opencontainers/runc 96ec2177ae841256168fcf76954f7177af9446eb github.com/opencontainers/runtime-spec v1.0.1 github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7 github.com/peterbourgon/diskv 5f041e8faa004a95c88a202771f4cc3e991971e6 # v2.0.1 diff --git a/vendor/github.com/Microsoft/go-winio/ea.go b/vendor/github.com/Microsoft/go-winio/ea.go index b37e930d6af6..4051c1b33bfe 100644 --- a/vendor/github.com/Microsoft/go-winio/ea.go +++ b/vendor/github.com/Microsoft/go-winio/ea.go @@ -1,137 +1,137 @@ -package winio - -import ( - "bytes" - "encoding/binary" - "errors" -) - -type fileFullEaInformation struct { - NextEntryOffset uint32 - Flags uint8 - NameLength uint8 - ValueLength uint16 -} - -var ( - fileFullEaInformationSize = binary.Size(&fileFullEaInformation{}) - - errInvalidEaBuffer = errors.New("invalid extended attribute buffer") - errEaNameTooLarge = errors.New("extended attribute name too large") - errEaValueTooLarge = errors.New("extended attribute value too large") -) - -// ExtendedAttribute represents a single Windows EA. -type ExtendedAttribute struct { - Name string - Value []byte - Flags uint8 -} - -func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { - var info fileFullEaInformation - err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info) - if err != nil { - err = errInvalidEaBuffer - return - } - - nameOffset := fileFullEaInformationSize - nameLen := int(info.NameLength) - valueOffset := nameOffset + int(info.NameLength) + 1 - valueLen := int(info.ValueLength) - nextOffset := int(info.NextEntryOffset) - if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) { - err = errInvalidEaBuffer - return - } - - ea.Name = string(b[nameOffset : nameOffset+nameLen]) - ea.Value = b[valueOffset : valueOffset+valueLen] - ea.Flags = info.Flags - if info.NextEntryOffset != 0 { - nb = b[info.NextEntryOffset:] - } - return -} - -// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION -// buffer retrieved from BackupRead, ZwQueryEaFile, etc. -func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) { - for len(b) != 0 { - ea, nb, err := parseEa(b) - if err != nil { - return nil, err - } - - eas = append(eas, ea) - b = nb - } - return -} - -func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error { - if int(uint8(len(ea.Name))) != len(ea.Name) { - return errEaNameTooLarge - } - if int(uint16(len(ea.Value))) != len(ea.Value) { - return errEaValueTooLarge - } - entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value)) - withPadding := (entrySize + 3) &^ 3 - nextOffset := uint32(0) - if !last { - nextOffset = withPadding - } - info := fileFullEaInformation{ - NextEntryOffset: nextOffset, - Flags: ea.Flags, - NameLength: uint8(len(ea.Name)), - ValueLength: uint16(len(ea.Value)), - } - - err := binary.Write(buf, binary.LittleEndian, &info) - if err != nil { - return err - } - - _, err = buf.Write([]byte(ea.Name)) - if err != nil { - return err - } - - err = buf.WriteByte(0) - if err != nil { - return err - } - - _, err = buf.Write(ea.Value) - if err != nil { - return err - } - - _, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize]) - if err != nil { - return err - } - - return nil -} - -// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION -// buffer for use with BackupWrite, ZwSetEaFile, etc. -func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) { - var buf bytes.Buffer - for i := range eas { - last := false - if i == len(eas)-1 { - last = true - } - - err := writeEa(&buf, &eas[i], last) - if err != nil { - return nil, err - } - } - return buf.Bytes(), nil -} +package winio + +import ( + "bytes" + "encoding/binary" + "errors" +) + +type fileFullEaInformation struct { + NextEntryOffset uint32 + Flags uint8 + NameLength uint8 + ValueLength uint16 +} + +var ( + fileFullEaInformationSize = binary.Size(&fileFullEaInformation{}) + + errInvalidEaBuffer = errors.New("invalid extended attribute buffer") + errEaNameTooLarge = errors.New("extended attribute name too large") + errEaValueTooLarge = errors.New("extended attribute value too large") +) + +// ExtendedAttribute represents a single Windows EA. +type ExtendedAttribute struct { + Name string + Value []byte + Flags uint8 +} + +func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { + var info fileFullEaInformation + err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info) + if err != nil { + err = errInvalidEaBuffer + return + } + + nameOffset := fileFullEaInformationSize + nameLen := int(info.NameLength) + valueOffset := nameOffset + int(info.NameLength) + 1 + valueLen := int(info.ValueLength) + nextOffset := int(info.NextEntryOffset) + if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) { + err = errInvalidEaBuffer + return + } + + ea.Name = string(b[nameOffset : nameOffset+nameLen]) + ea.Value = b[valueOffset : valueOffset+valueLen] + ea.Flags = info.Flags + if info.NextEntryOffset != 0 { + nb = b[info.NextEntryOffset:] + } + return +} + +// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION +// buffer retrieved from BackupRead, ZwQueryEaFile, etc. +func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) { + for len(b) != 0 { + ea, nb, err := parseEa(b) + if err != nil { + return nil, err + } + + eas = append(eas, ea) + b = nb + } + return +} + +func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error { + if int(uint8(len(ea.Name))) != len(ea.Name) { + return errEaNameTooLarge + } + if int(uint16(len(ea.Value))) != len(ea.Value) { + return errEaValueTooLarge + } + entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value)) + withPadding := (entrySize + 3) &^ 3 + nextOffset := uint32(0) + if !last { + nextOffset = withPadding + } + info := fileFullEaInformation{ + NextEntryOffset: nextOffset, + Flags: ea.Flags, + NameLength: uint8(len(ea.Name)), + ValueLength: uint16(len(ea.Value)), + } + + err := binary.Write(buf, binary.LittleEndian, &info) + if err != nil { + return err + } + + _, err = buf.Write([]byte(ea.Name)) + if err != nil { + return err + } + + err = buf.WriteByte(0) + if err != nil { + return err + } + + _, err = buf.Write(ea.Value) + if err != nil { + return err + } + + _, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize]) + if err != nil { + return err + } + + return nil +} + +// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION +// buffer for use with BackupWrite, ZwSetEaFile, etc. +func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) { + var buf bytes.Buffer + for i := range eas { + last := false + if i == len(eas)-1 { + last = true + } + + err := writeEa(&buf, &eas[i], last) + if err != nil { + return nil, err + } + } + return buf.Bytes(), nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go b/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go index 5f0dcfe759cf..eb013d2c42ed 100644 --- a/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go +++ b/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go @@ -7,6 +7,9 @@ import ( // HNSEndpoint represents a network endpoint in HNS type HNSEndpoint = hns.HNSEndpoint +// Namespace represents a Compartment. +type Namespace = hns.Namespace + //SystemType represents the type of the system on which actions are done type SystemType string diff --git a/vendor/github.com/Microsoft/hcsshim/hnspolicylist.go b/vendor/github.com/Microsoft/hcsshim/hnspolicylist.go index 55aaa4a50efb..12c2b9702997 100644 --- a/vendor/github.com/Microsoft/hcsshim/hnspolicylist.go +++ b/vendor/github.com/Microsoft/hcsshim/hnspolicylist.go @@ -37,8 +37,8 @@ func GetPolicyListByID(policyListID string) (*PolicyList, error) { } // AddLoadBalancer policy list for the specified endpoints -func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) { - return hns.AddLoadBalancer(endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort) +func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, isDSR bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) { + return hns.AddLoadBalancer(endpoints, isILB, isDSR, sourceVIP, vip, protocol, internalPort, externalPort) } // AddRoute adds route policy list for the specified endpoints diff --git a/vendor/github.com/Microsoft/hcsshim/interface.go b/vendor/github.com/Microsoft/hcsshim/interface.go index 2724624fd5aa..5b91e0cc55cf 100644 --- a/vendor/github.com/Microsoft/hcsshim/interface.go +++ b/vendor/github.com/Microsoft/hcsshim/interface.go @@ -17,6 +17,11 @@ type MappedPipe = schema1.MappedPipe type HvRuntime = schema1.HvRuntime type MappedVirtualDisk = schema1.MappedVirtualDisk +// AssignedDevice represents a device that has been directly assigned to a container +// +// NOTE: Support added in RS5 +type AssignedDevice = schema1.AssignedDevice + // ContainerConfig is used as both the input of CreateContainer // and to convert the parameters to JSON for passing onto the HCS type ContainerConfig = schema1.ContainerConfig diff --git a/vendor/github.com/Microsoft/hcsshim/internal/guestrequest/types.go b/vendor/github.com/Microsoft/hcsshim/internal/guestrequest/types.go new file mode 100644 index 000000000000..9f926c6be7da --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/guestrequest/types.go @@ -0,0 +1,85 @@ +package guestrequest + +import "github.com/Microsoft/hcsshim/internal/schema2" + +// Arguably, many of these (at least CombinedLayers) should have been generated +// by swagger. +// +// This will also change package name due to an inbound breaking change. + +// This class is used by a modify request to add or remove a combined layers +// structure in the guest. For windows, the GCS applies a filter in ContainerRootPath +// using the specified layers as the parent content. Ignores property ScratchPath +// since the container path is already the scratch path. For linux, the GCS unions +// the specified layers and ScratchPath together, placing the resulting union +// filesystem at ContainerRootPath. +type CombinedLayers struct { + ContainerRootPath string `json:"ContainerRootPath,omitempty"` + Layers []hcsschema.Layer `json:"Layers,omitempty"` + ScratchPath string `json:"ScratchPath,omitempty"` +} + +// Defines the schema for hosted settings passed to GCS and/or OpenGCS + +// SCSI. Scratch space for remote file-system commands, or R/W layer for containers +type LCOWMappedVirtualDisk struct { + MountPath string `json:"MountPath,omitempty"` // /tmp/scratch for an LCOW utility VM being used as a service VM + Lun uint8 `json:"Lun,omitempty"` + Controller uint8 `json:"Controller,omitempty"` + ReadOnly bool `json:"ReadOnly,omitempty"` +} + +type WCOWMappedVirtualDisk struct { + ContainerPath string `json:"ContainerPath,omitempty"` + Lun int32 `json:"Lun,omitempty"` +} + +type LCOWMappedDirectory struct { + MountPath string `json:"MountPath,omitempty"` + Port int32 `json:"Port,omitempty"` + ShareName string `json:"ShareName,omitempty"` // If empty not using ANames (not currently supported) + ReadOnly bool `json:"ReadOnly,omitempty"` +} + +// Read-only layers over VPMem +type LCOWMappedVPMemDevice struct { + DeviceNumber uint32 `json:"DeviceNumber,omitempty"` + MountPath string `json:"MountPath,omitempty"` // /tmp/pN +} + +type ResourceType string + +const ( + // These are constants for v2 schema modify guest requests. + ResourceTypeMappedDirectory ResourceType = "MappedDirectory" + ResourceTypeMappedVirtualDisk ResourceType = "MappedVirtualDisk" + ResourceTypeNetwork ResourceType = "Network" + ResourceTypeNetworkNamespace ResourceType = "NetworkNamespace" + ResourceTypeCombinedLayers ResourceType = "CombinedLayers" + ResourceTypeVPMemDevice ResourceType = "VPMemDevice" +) + +// GuestRequest is for modify commands passed to the guest. +type GuestRequest struct { + RequestType string `json:"RequestType,omitempty"` + ResourceType ResourceType `json:"ResourceType,omitempty"` + Settings interface{} `json:"Settings,omitempty"` +} + +type NetworkModifyRequest struct { + AdapterId string `json:"AdapterId,omitempty"` + RequestType string `json:"RequestType,omitempty"` + Settings interface{} `json:"Settings,omitempty"` +} + +type RS4NetworkModifyRequest struct { + AdapterInstanceId string `json:"AdapterInstanceId,omitempty"` + RequestType string `json:"RequestType,omitempty"` + Settings interface{} `json:"Settings,omitempty"` +} + +// SignalProcessOptions is the options passed to either WCOW or LCOW +// to signal a given process. +type SignalProcessOptions struct { + Signal int `json:,omitempty` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go index b8e30eba174e..b0d49cbcf157 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go @@ -27,6 +27,7 @@ import ( //sys hcsOpenProcess(computeSystem hcsSystem, pid uint32, process *hcsProcess, result **uint16) (hr error) = vmcompute.HcsOpenProcess? //sys hcsCloseProcess(process hcsProcess) (hr error) = vmcompute.HcsCloseProcess? //sys hcsTerminateProcess(process hcsProcess, result **uint16) (hr error) = vmcompute.HcsTerminateProcess? +//sys hcsSignalProcess(process hcsProcess, options string, result **uint16) (hr error) = vmcompute.HcsTerminateProcess? //sys hcsGetProcessInfo(process hcsProcess, processInformation *hcsProcessInformation, result **uint16) (hr error) = vmcompute.HcsGetProcessInfo? //sys hcsGetProcessProperties(process hcsProcess, processProperties **uint16, result **uint16) (hr error) = vmcompute.HcsGetProcessProperties? //sys hcsModifyProcess(process hcsProcess, settings string, result **uint16) (hr error) = vmcompute.HcsModifyProcess? diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go index 8294d66d7b48..d356cdc4d6f4 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go @@ -8,6 +8,7 @@ import ( "syscall" "time" + "github.com/Microsoft/hcsshim/internal/guestrequest" "github.com/Microsoft/hcsshim/internal/interop" "github.com/sirupsen/logrus" ) @@ -71,6 +72,39 @@ func (process *Process) SystemID() string { return process.system.ID() } +// Signal signals the process with `options`. +func (process *Process) Signal(options guestrequest.SignalProcessOptions) error { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + operation := "Signal" + title := "hcsshim::Process::" + operation + logrus.Debugf(title+" processid=%d", process.processID) + + if process.handle == 0 { + return makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + optionsb, err := json.Marshal(options) + if err != nil { + return err + } + + optionsStr := string(optionsb) + + var resultp *uint16 + completed := false + go syscallWatcher(fmt.Sprintf("SignalProcess %s: %d", process.SystemID(), process.Pid()), &completed) + err = hcsSignalProcess(process.handle, optionsStr, &resultp) + completed = true + events := processHcsResult(resultp) + if err != nil { + return makeProcessError(process, operation, err, events) + } + + logrus.Debugf(title+" succeeded processid=%d", process.processID) + return nil +} + // Kill signals the process to terminate but does not wait for it to finish terminating. func (process *Process) Kill() error { process.handleLock.RLock() diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go index 48d5cd32b99f..925c65e28d20 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go @@ -57,12 +57,13 @@ var ( procHcsOpenProcess = modvmcompute.NewProc("HcsOpenProcess") procHcsCloseProcess = modvmcompute.NewProc("HcsCloseProcess") procHcsTerminateProcess = modvmcompute.NewProc("HcsTerminateProcess") - procHcsGetProcessInfo = modvmcompute.NewProc("HcsGetProcessInfo") - procHcsGetProcessProperties = modvmcompute.NewProc("HcsGetProcessProperties") - procHcsModifyProcess = modvmcompute.NewProc("HcsModifyProcess") - procHcsGetServiceProperties = modvmcompute.NewProc("HcsGetServiceProperties") - procHcsRegisterProcessCallback = modvmcompute.NewProc("HcsRegisterProcessCallback") - procHcsUnregisterProcessCallback = modvmcompute.NewProc("HcsUnregisterProcessCallback") + + procHcsGetProcessInfo = modvmcompute.NewProc("HcsGetProcessInfo") + procHcsGetProcessProperties = modvmcompute.NewProc("HcsGetProcessProperties") + procHcsModifyProcess = modvmcompute.NewProc("HcsModifyProcess") + procHcsGetServiceProperties = modvmcompute.NewProc("HcsGetServiceProperties") + procHcsRegisterProcessCallback = modvmcompute.NewProc("HcsRegisterProcessCallback") + procHcsUnregisterProcessCallback = modvmcompute.NewProc("HcsUnregisterProcessCallback") ) func hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) { @@ -356,6 +357,26 @@ func hcsTerminateProcess(process hcsProcess, result **uint16) (hr error) { return } +func hcsSignalProcess(process hcsProcess, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsSignalProcess(process, _p0, result) +} + +func _hcsSignalProcess(process hcsProcess, options *uint16, result **uint16) (hr error) { + if hr = procHcsTerminateProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsTerminateProcess.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + func hcsGetProcessInfo(process hcsProcess, processInformation *hcsProcessInformation, result **uint16) (hr error) { if hr = procHcsGetProcessInfo.Find(); hr != nil { return diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go index ff7369e6ff9d..cc98b49e0a99 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go @@ -20,6 +20,7 @@ type ELBPolicy struct { SourceVIP string `json:"SourceVIP,omitempty"` VIPs []string `json:"VIPs,omitempty"` ILB bool `json:"ILB,omitempty"` + DSR bool `json:"IsDSR,omitempty"` } // LBPolicy is a structure defining schema for LoadBalancing based Policy @@ -139,7 +140,7 @@ func (policylist *PolicyList) RemoveEndpoint(endpoint *HNSEndpoint) (*PolicyList } // AddLoadBalancer policy list for the specified endpoints -func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) { +func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, isDSR bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) { operation := "AddLoadBalancer" title := "hcsshim::PolicyList::" + operation logrus.Debugf(title+" endpointId=%v, isILB=%v, sourceVIP=%s, vip=%s, protocol=%v, internalPort=%v, externalPort=%v", endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort) @@ -149,6 +150,7 @@ func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, elbPolicy := &ELBPolicy{ SourceVIP: sourceVIP, ILB: isILB, + DSR: isDSR, } if len(vip) > 0 { diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go b/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go index 6fa3bbc73d62..995433ace6f5 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go @@ -3,6 +3,8 @@ package schema1 import ( "encoding/json" "time" + + "github.com/Microsoft/hcsshim/internal/schema2" ) // ProcessConfig is used as both the input of Container.CreateProcess @@ -115,9 +117,10 @@ type ComputeSystemQuery struct { type PropertyType string const ( - PropertyTypeStatistics PropertyType = "Statistics" - PropertyTypeProcessList = "ProcessList" - PropertyTypeMappedVirtualDisk = "MappedVirtualDisk" + PropertyTypeStatistics PropertyType = "Statistics" // V1 and V2 + PropertyTypeProcessList = "ProcessList" // V1 and V2 + PropertyTypeMappedVirtualDisk = "MappedVirtualDisk" // Not supported in V2 schema call + PropertyTypeGuestConnection = "GuestConnection" // V1 and V2. Nil return from HCS before RS5 ) type PropertyQuery struct { @@ -142,6 +145,7 @@ type ContainerProperties struct { Statistics Statistics `json:",omitempty"` ProcessList []ProcessListItem `json:",omitempty"` MappedVirtualDiskControllers map[int]MappedVirtualDiskController `json:",omitempty"` + GuestConnectionInfo GuestConnectionInfo `json:",omitempty"` } // MemoryStats holds the memory statistics for a container @@ -206,6 +210,19 @@ type MappedVirtualDiskController struct { MappedVirtualDisks map[int]MappedVirtualDisk `json:",omitempty"` } +// GuestDefinedCapabilities is part of the GuestConnectionInfo returned by a GuestConnection call on a utility VM +type GuestDefinedCapabilities struct { + NamespaceAddRequestSupported bool `json:",omitempty"` + SignalProcessSupported bool `json:",omitempty"` +} + +// GuestConnectionInfo is the structure of an iterm return by a GuestConnection call on a utility VM +type GuestConnectionInfo struct { + SupportedSchemaVersions []hcsschema.Version `json:",omitempty"` + ProtocolVersion uint32 `json:",omitempty"` + GuestDefinedCapabilities GuestDefinedCapabilities `json:",omitempty"` +} + // Type of Request Support in ModifySystem type RequestType string diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/attachment.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/attachment.go new file mode 100644 index 000000000000..09456cbc2197 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/attachment.go @@ -0,0 +1,31 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Attachment struct { + + Type_ string `json:"Type,omitempty"` + + Path string `json:"Path,omitempty"` + + IgnoreFlushes bool `json:"IgnoreFlushes,omitempty"` + + CachingMode string `json:"CachingMode,omitempty"` + + NoWriteHardening bool `json:"NoWriteHardening,omitempty"` + + DisableExpansionOptimization bool `json:"DisableExpansionOptimization,omitempty"` + + IgnoreRelativeLocator bool `json:"IgnoreRelativeLocator,omitempty"` + + CaptureIoAttributionContext bool `json:"CaptureIoAttributionContext,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/battery.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/battery.go new file mode 100644 index 000000000000..ecbbed4c233f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/battery.go @@ -0,0 +1,13 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Battery struct { +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/cache_query_stats_response.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/cache_query_stats_response.go new file mode 100644 index 000000000000..243779eab678 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/cache_query_stats_response.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CacheQueryStatsResponse struct { + + L3OccupancyBytes int32 `json:"L3OccupancyBytes,omitempty"` + + L3TotalBwBytes int32 `json:"L3TotalBwBytes,omitempty"` + + L3LocalBwBytes int32 `json:"L3LocalBwBytes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/chipset.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/chipset.go new file mode 100644 index 000000000000..3fb24e2505bd --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/chipset.go @@ -0,0 +1,25 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Chipset struct { + + Uefi *Uefi `json:"Uefi,omitempty"` + + IsNumLockDisabled bool `json:"IsNumLockDisabled,omitempty"` + + BaseBoardSerialNumber string `json:"BaseBoardSerialNumber,omitempty"` + + ChassisSerialNumber string `json:"ChassisSerialNumber,omitempty"` + + ChassisAssetTag string `json:"ChassisAssetTag,omitempty"` + + UseUtc bool `json:"UseUtc,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/close_handle.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/close_handle.go new file mode 100644 index 000000000000..88f01707a732 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/close_handle.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type CloseHandle struct { + + Handle string `json:"Handle,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/com_port.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/com_port.go new file mode 100644 index 000000000000..c665be3d5a38 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/com_port.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// ComPort specifies the named pipe that will be used for the port, with empty string indicating a disconnected port. +type ComPort struct { + + NamedPipe string `json:"NamedPipe,omitempty"` + + OptimizeForDebugger bool `json:"OptimizeForDebugger,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/compute_system.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/compute_system.go new file mode 100644 index 000000000000..85785d285853 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/compute_system.go @@ -0,0 +1,27 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ComputeSystem struct { + + Owner string `json:"Owner,omitempty"` + + SchemaVersion *Version `json:"SchemaVersion,omitempty"` + + HostingSystemId string `json:"HostingSystemId,omitempty"` + + HostedSystem *HostedSystem `json:"HostedSystem,omitempty"` + + Container *Container `json:"Container,omitempty"` + + VirtualMachine *VirtualMachine `json:"VirtualMachine,omitempty"` + + ShouldTerminateOnLastHandleClosed bool `json:"ShouldTerminateOnLastHandleClosed,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/configuration.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/configuration.go new file mode 100644 index 000000000000..1a47db7d955f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/configuration.go @@ -0,0 +1,72 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import ( + "net/http" +) + +// contextKeys are used to identify the type of value in the context. +// Since these are string, it is possible to get a short description of the +// context key for logging and debugging using key.String(). + +type contextKey string + +func (c contextKey) String() string { + return "auth " + string(c) +} + +var ( + // ContextOAuth2 takes a oauth2.TokenSource as authentication for the request. + ContextOAuth2 = contextKey("token") + + // ContextBasicAuth takes BasicAuth as authentication for the request. + ContextBasicAuth = contextKey("basic") + + // ContextAccessToken takes a string oauth2 access token as authentication for the request. + ContextAccessToken = contextKey("accesstoken") + + // ContextAPIKey takes an APIKey as authentication for the request + ContextAPIKey = contextKey("apikey") +) + +// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth +type BasicAuth struct { + UserName string `json:"userName,omitempty"` + Password string `json:"password,omitempty"` +} + +// APIKey provides API key based authentication to a request passed via context using ContextAPIKey +type APIKey struct { + Key string + Prefix string +} + +type Configuration struct { + BasePath string `json:"basePath,omitempty"` + Host string `json:"host,omitempty"` + Scheme string `json:"scheme,omitempty"` + DefaultHeader map[string]string `json:"defaultHeader,omitempty"` + UserAgent string `json:"userAgent,omitempty"` + HTTPClient *http.Client +} + +func NewConfiguration() *Configuration { + cfg := &Configuration{ + BasePath: "https://localhost", + DefaultHeader: make(map[string]string), + UserAgent: "Swagger-Codegen/2.1.0/go", + } + return cfg +} + +func (c *Configuration) AddDefaultHeader(key string, value string) { + c.DefaultHeader[key] = value +} \ No newline at end of file diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/console_size.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/console_size.go new file mode 100644 index 000000000000..adbe07fe5594 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/console_size.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ConsoleSize struct { + + Height int32 `json:"Height,omitempty"` + + Width int32 `json:"Width,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/container.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/container.go new file mode 100644 index 000000000000..17dce28bc721 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/container.go @@ -0,0 +1,35 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Container struct { + + GuestOs *GuestOs `json:"GuestOs,omitempty"` + + Storage *Storage `json:"Storage,omitempty"` + + MappedDirectories []MappedDirectory `json:"MappedDirectories,omitempty"` + + MappedPipes []MappedPipe `json:"MappedPipes,omitempty"` + + Memory *Memory `json:"Memory,omitempty"` + + Processor *Processor `json:"Processor,omitempty"` + + Networking *Networking `json:"Networking,omitempty"` + + HvSocket *HvSocket `json:"HvSocket,omitempty"` + + ContainerCredentialGuard *ContainerCredentialGuardState `json:"ContainerCredentialGuard,omitempty"` + + RegistryChanges *RegistryChanges `json:"RegistryChanges,omitempty"` + + AssignedDevices []Device `json:"AssignedDevices,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/container_credential_guard_state.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/container_credential_guard_state.go new file mode 100644 index 000000000000..0f8f644379ce --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/container_credential_guard_state.go @@ -0,0 +1,25 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ContainerCredentialGuardState struct { + + // Authentication cookie for calls to a Container Credential Guard instance. + Cookie string `json:"Cookie,omitempty"` + + // Name of the RPC endpoint of the Container Credential Guard instance. + RpcEndpoint string `json:"RpcEndpoint,omitempty"` + + // Transport used for the configured Container Credential Guard instance. + Transport string `json:"Transport,omitempty"` + + // Credential spec used for the configured Container Credential Guard instance. + CredentialSpec string `json:"CredentialSpec,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/container_memory_information.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/container_memory_information.go new file mode 100644 index 000000000000..754797e213f6 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/container_memory_information.go @@ -0,0 +1,26 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// memory usage as viewed from within the container +type ContainerMemoryInformation struct { + + TotalPhysicalBytes int32 `json:"TotalPhysicalBytes,omitempty"` + + TotalUsage int32 `json:"TotalUsage,omitempty"` + + CommittedBytes int32 `json:"CommittedBytes,omitempty"` + + SharedCommittedBytes int32 `json:"SharedCommittedBytes,omitempty"` + + CommitLimitBytes int32 `json:"CommitLimitBytes,omitempty"` + + PeakCommitmentBytes int32 `json:"PeakCommitmentBytes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/device.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/device.go new file mode 100644 index 000000000000..ca319bbbcea2 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/device.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Device struct { + + // The interface class guid of the device to assign to container. + InterfaceClassGuid string `json:"InterfaceClassGuid,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/devices.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/devices.go new file mode 100644 index 000000000000..b2191c571dba --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/devices.go @@ -0,0 +1,43 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Devices struct { + + ComPorts map[string]ComPort `json:"ComPorts,omitempty"` + + Scsi map[string]Scsi `json:"Scsi,omitempty"` + + VirtualPMem *VirtualPMemController `json:"VirtualPMem,omitempty"` + + NetworkAdapters map[string]NetworkAdapter `json:"NetworkAdapters,omitempty"` + + VideoMonitor *VideoMonitor `json:"VideoMonitor,omitempty"` + + Keyboard *Keyboard `json:"Keyboard,omitempty"` + + Mouse *Mouse `json:"Mouse,omitempty"` + + HvSocket *HvSocket2 `json:"HvSocket,omitempty"` + + EnhancedModeVideo *EnhancedModeVideo `json:"EnhancedModeVideo,omitempty"` + + GuestCrashReporting *GuestCrashReporting `json:"GuestCrashReporting,omitempty"` + + VirtualSmb *VirtualSmb `json:"VirtualSmb,omitempty"` + + Plan9 *Plan9 `json:"Plan9,omitempty"` + + Battery *Battery `json:"Battery,omitempty"` + + FlexibleIov map[string]FlexibleIoDevice `json:"FlexibleIov,omitempty"` + + SharedMemory *SharedMemoryConfiguration `json:"SharedMemory,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/enhanced_mode_video.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/enhanced_mode_video.go new file mode 100644 index 000000000000..4fe592f71176 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/enhanced_mode_video.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type EnhancedModeVideo struct { + + ConnectionOptions *RdpConnectionOptions `json:"ConnectionOptions,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/flexible_io_device.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/flexible_io_device.go new file mode 100644 index 000000000000..51011afe407a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/flexible_io_device.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type FlexibleIoDevice struct { + + EmulatorId string `json:"EmulatorId,omitempty"` + + HostingModel string `json:"HostingModel,omitempty"` + + Configuration []string `json:"Configuration,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_connection.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_connection.go new file mode 100644 index 000000000000..7db29495b3e6 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_connection.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestConnection struct { + + // Use Vsock rather than Hyper-V sockets to communicate with the guest service. + UseVsock bool `json:"UseVsock,omitempty"` + + // Don't disconnect the guest connection when pausing the virtual machine. + UseConnectedSuspend bool `json:"UseConnectedSuspend,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_connection_info.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_connection_info.go new file mode 100644 index 000000000000..8a369bab71c4 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_connection_info.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Information about the guest. +type GuestConnectionInfo struct { + + // Each schema version x.y stands for the range of versions a.b where a==x and b<=y. This list comes from the SupportedSchemaVersions field in GcsCapabilities. + SupportedSchemaVersions []Version `json:"SupportedSchemaVersions,omitempty"` + + ProtocolVersion int32 `json:"ProtocolVersion,omitempty"` + + GuestDefinedCapabilities *interface{} `json:"GuestDefinedCapabilities,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_crash_reporting.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_crash_reporting.go new file mode 100644 index 000000000000..c5fa767352e6 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_crash_reporting.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestCrashReporting struct { + + WindowsCrashSettings *WindowsCrashReporting `json:"WindowsCrashSettings,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_os.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_os.go new file mode 100644 index 000000000000..c708fc7c3f96 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_os.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestOs struct { + + HostName string `json:"HostName,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_state.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_state.go new file mode 100644 index 000000000000..ef1eec88656f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/guest_state.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type GuestState struct { + + // The path to an existing file uses for persistent guest state storage. An empty string indicates the system should initialize new transient, in-memory guest state. + GuestStateFilePath string `json:"GuestStateFilePath,omitempty"` + + // The path to an existing file for persistent runtime state storage. An empty string indicates the system should initialize new transient, in-memory runtime state. + RuntimeStateFilePath string `json:"RuntimeStateFilePath,omitempty"` + + // If true, the guest state and runtime state files will be used as templates to populate transient, in-memory state instead of using the files as persistent backing store. + ForceTransientState bool `json:"ForceTransientState,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/hosted_system.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/hosted_system.go new file mode 100644 index 000000000000..0797584c51d2 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/hosted_system.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type HostedSystem struct { + + SchemaVersion *Version `json:"SchemaVersion,omitempty"` + + Container *Container `json:"Container,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket.go new file mode 100644 index 000000000000..ef9ffb8dd93f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type HvSocket struct { + + Config *HvSocketSystemConfig `json:"Config,omitempty"` + + EnablePowerShellDirect bool `json:"EnablePowerShellDirect,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_2.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_2.go new file mode 100644 index 000000000000..a19ba15c15bf --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_2.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// HvSocket configuration for a VM +type HvSocket2 struct { + + HvSocketConfig *HvSocketSystemConfig `json:"HvSocketConfig,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_service_config.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_service_config.go new file mode 100644 index 000000000000..a848e91e69b6 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_service_config.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type HvSocketServiceConfig struct { + + // SDDL string that HvSocket will check before allowing a host process to bind to this specific service. If not specified, defaults to the system DefaultBindSecurityDescriptor, defined in HvSocketSystemWpConfig in V1. + BindSecurityDescriptor string `json:"BindSecurityDescriptor,omitempty"` + + // SDDL string that HvSocket will check before allowing a host process to connect to this specific service. If not specified, defaults to the system DefaultConnectSecurityDescriptor, defined in HvSocketSystemWpConfig in V1. + ConnectSecurityDescriptor string `json:"ConnectSecurityDescriptor,omitempty"` + + // If true, HvSocket will process wildcard binds for this service/system combination. Wildcard binds are secured in the registry at SOFTWARE/Microsoft/Windows NT/CurrentVersion/Virtualization/HvSocket/WildcardDescriptors + AllowWildcardBinds bool `json:"AllowWildcardBinds,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_system_config.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_system_config.go new file mode 100644 index 000000000000..69f4f9d39b9a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/hv_socket_system_config.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// This is the HCS Schema version of the HvSocket configuration. The VMWP version is located in Config.Devices.IC in V1. +type HvSocketSystemConfig struct { + + // SDDL string that HvSocket will check before allowing a host process to bind to an unlisted service for this specific container/VM (not wildcard binds). + DefaultBindSecurityDescriptor string `json:"DefaultBindSecurityDescriptor,omitempty"` + + // SDDL string that HvSocket will check before allowing a host process to connect to an unlisted service in the VM/container. + DefaultConnectSecurityDescriptor string `json:"DefaultConnectSecurityDescriptor,omitempty"` + + ServiceTable map[string]HvSocketServiceConfig `json:"ServiceTable,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/keyboard.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/keyboard.go new file mode 100644 index 000000000000..3d3fa3b1c732 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/keyboard.go @@ -0,0 +1,13 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Keyboard struct { +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/layer.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/layer.go new file mode 100644 index 000000000000..b63b8ef12c86 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/layer.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Layer struct { + + Id string `json:"Id,omitempty"` + + Path string `json:"Path,omitempty"` + + PathType string `json:"PathType,omitempty"` + + // Unspecified defaults to Enabled + Cache string `json:"Cache,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/mapped_directory.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/mapped_directory.go new file mode 100644 index 000000000000..a823a6d3b894 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/mapped_directory.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type MappedDirectory struct { + + HostPath string `json:"HostPath,omitempty"` + + HostPathType string `json:"HostPathType,omitempty"` + + ContainerPath string `json:"ContainerPath,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/mapped_pipe.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/mapped_pipe.go new file mode 100644 index 000000000000..2d1d2604a9b0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/mapped_pipe.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type MappedPipe struct { + + ContainerPipeName string `json:"ContainerPipeName,omitempty"` + + HostPath string `json:"HostPath,omitempty"` + + HostPathType string `json:"HostPathType,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory.go new file mode 100644 index 000000000000..e1d135a3a4b6 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Memory struct { + + SizeInMB int32 `json:"SizeInMB,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_2.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_2.go new file mode 100644 index 000000000000..27d0b8c48387 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_2.go @@ -0,0 +1,25 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Memory2 struct { + SizeInMB int32 `json:"SizeInMB,omitempty"` + + AllowOvercommit bool `json:"AllowOvercommit,omitempty"` + + EnableHotHint bool `json:"EnableHotHint,omitempty"` + + EnableColdHint bool `json:"EnableColdHint,omitempty"` + + EnableEpf bool `json:"EnableEpf,omitempty"` + + // EnableDeferredCommit is private in the schema. If regenerated need to add back. + EnableDeferredCommit bool `json:"EnableDeferredCommit,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_information_for_vm.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_information_for_vm.go new file mode 100644 index 000000000000..bdd87dffd85a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_information_for_vm.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type MemoryInformationForVm struct { + + VirtualNodeCount int32 `json:"VirtualNodeCount,omitempty"` + + VirtualMachineMemory *VmMemory `json:"VirtualMachineMemory,omitempty"` + + VirtualNodes []VirtualNodeInfo `json:"VirtualNodes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_stats.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_stats.go new file mode 100644 index 000000000000..6214970f6972 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_stats.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Memory runtime statistics +type MemoryStats struct { + + MemoryUsageCommitBytes int32 `json:"MemoryUsageCommitBytes,omitempty"` + + MemoryUsageCommitPeakBytes int32 `json:"MemoryUsageCommitPeakBytes,omitempty"` + + MemoryUsagePrivateWorkingSetBytes int32 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/modify_setting_request.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/modify_setting_request.go new file mode 100644 index 000000000000..d29455a3e434 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/modify_setting_request.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ModifySettingRequest struct { + ResourcePath string `json:"ResourcePath,omitempty"` + + RequestType string `json:"RequestType,omitempty"` + + Settings interface{} `json:"Settings,omitempty"` // NOTE: Swagger generated as *interface{}. Locally updated + + GuestRequest interface{} `json:"GuestRequest,omitempty"` // NOTE: Swagger generated as *interface{}. Locally updated +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/mouse.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/mouse.go new file mode 100644 index 000000000000..ccf8b938f3ac --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/mouse.go @@ -0,0 +1,13 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Mouse struct { +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/network_adapter.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/network_adapter.go new file mode 100644 index 000000000000..c586f66c251b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/network_adapter.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type NetworkAdapter struct { + + EndpointId string `json:"EndpointId,omitempty"` + + MacAddress string `json:"MacAddress,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/networking.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/networking.go new file mode 100644 index 000000000000..12c47827c5db --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/networking.go @@ -0,0 +1,24 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Networking struct { + + AllowUnqualifiedDnsQuery bool `json:"AllowUnqualifiedDnsQuery,omitempty"` + + DnsSearchList string `json:"DnsSearchList,omitempty"` + + NetworkSharedContainerName string `json:"NetworkSharedContainerName,omitempty"` + + // Guid in windows; string in linux + Namespace string `json:"Namespace,omitempty"` + + NetworkAdapters []string `json:"NetworkAdapters,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/pause_notification.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/pause_notification.go new file mode 100644 index 000000000000..1cd70d17902e --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/pause_notification.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Notification data that is indicated to components running in the Virtual Machine. +type PauseNotification struct { + + Reason string `json:"Reason,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/pause_options.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/pause_options.go new file mode 100644 index 000000000000..780a5cae2c1c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/pause_options.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Options for HcsPauseComputeSystem +type PauseOptions struct { + + SuspensionLevel string `json:"SuspensionLevel,omitempty"` + + HostedNotification *PauseNotification `json:"HostedNotification,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/plan9.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/plan9.go new file mode 100644 index 000000000000..705c677e1f81 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/plan9.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Plan9 struct { + + Shares []Plan9Share `json:"Shares,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/plan9_share.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/plan9_share.go new file mode 100644 index 000000000000..b2bc58b83c25 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/plan9_share.go @@ -0,0 +1,26 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Plan9Share struct { + + Name string `json:"Name,omitempty"` + + // The name by which the guest operation system can access this share, via the aname parameter in the Plan9 protocol. + AccessName string `json:"AccessName,omitempty"` + + Path string `json:"Path,omitempty"` + + Port int32 `json:"Port,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` + + UseShareRootIdentity bool `json:"UseShareRootIdentity,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_details.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_details.go new file mode 100644 index 000000000000..63e0b7f8fe5f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_details.go @@ -0,0 +1,34 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import ( + "time" +) + +// Information about a process running in a container +type ProcessDetails struct { + + ProcessId int32 `json:"ProcessId,omitempty"` + + ImageName string `json:"ImageName,omitempty"` + + CreateTimestamp time.Time `json:"CreateTimestamp,omitempty"` + + UserTime100ns int32 `json:"UserTime100ns,omitempty"` + + KernelTime100ns int32 `json:"KernelTime100ns,omitempty"` + + MemoryCommitBytes int32 `json:"MemoryCommitBytes,omitempty"` + + MemoryWorkingSetPrivateBytes int32 `json:"MemoryWorkingSetPrivateBytes,omitempty"` + + MemoryWorkingSetSharedBytes int32 `json:"MemoryWorkingSetSharedBytes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_modify_request.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_modify_request.go new file mode 100644 index 000000000000..29bc2e3d00bb --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_modify_request.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Passed to HcsRpc_ModifyProcess +type ProcessModifyRequest struct { + + Operation string `json:"Operation,omitempty"` + + ConsoleSize *ConsoleSize `json:"ConsoleSize,omitempty"` + + CloseHandle *CloseHandle `json:"CloseHandle,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_parameters.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_parameters.go new file mode 100644 index 000000000000..470c55734ed3 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_parameters.go @@ -0,0 +1,47 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type ProcessParameters struct { + + ApplicationName string `json:"ApplicationName,omitempty"` + + CommandLine string `json:"CommandLine,omitempty"` + + // optional alternative to CommandLine, currently only supported by Linux GCS + CommandArgs []string `json:"CommandArgs,omitempty"` + + User string `json:"User,omitempty"` + + WorkingDirectory string `json:"WorkingDirectory,omitempty"` + + Environment map[string]string `json:"Environment,omitempty"` + + // if set, will run as low-privilege process + RestrictedToken bool `json:"RestrictedToken,omitempty"` + + // if set, ignore StdErrPipe + EmulateConsole bool `json:"EmulateConsole,omitempty"` + + CreateStdInPipe bool `json:"CreateStdInPipe,omitempty"` + + CreateStdOutPipe bool `json:"CreateStdOutPipe,omitempty"` + + CreateStdErrPipe bool `json:"CreateStdErrPipe,omitempty"` + + // height then width + ConsoleSize []int32 `json:"ConsoleSize,omitempty"` + + // if set, find an existing session for the user and create the process in it + UseExistingLogin bool `json:"UseExistingLogin,omitempty"` + + // if set, use the legacy console instead of conhost + UseLegacyConsole bool `json:"UseLegacyConsole,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_status.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_status.go new file mode 100644 index 000000000000..20793d1503bd --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/process_status.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Status of a process running in a container +type ProcessStatus struct { + + ProcessId int32 `json:"ProcessId,omitempty"` + + Exited bool `json:"Exited,omitempty"` + + ExitCode int32 `json:"ExitCode,omitempty"` + + LastWaitResult int32 `json:"LastWaitResult,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor.go new file mode 100644 index 000000000000..7a60b0245aa9 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Processor struct { + + Count int32 `json:"Count,omitempty"` + + Maximum int32 `json:"Maximum,omitempty"` + + Weight int32 `json:"Weight,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor_2.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor_2.go new file mode 100644 index 000000000000..40d3e7356d5a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor_2.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Processor2 struct { + + Count int32 `json:"Count,omitempty"` + + Limit int32 `json:"Limit,omitempty"` + + Weight int32 `json:"Weight,omitempty"` + + ExposeVirtualizationExtensions bool `json:"ExposeVirtualizationExtensions,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor_stats.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor_stats.go new file mode 100644 index 000000000000..9d3b77e57286 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/processor_stats.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// CPU runtime statistics +type ProcessorStats struct { + + TotalRuntime100ns int32 `json:"TotalRuntime100ns,omitempty"` + + RuntimeUser100ns int32 `json:"RuntimeUser100ns,omitempty"` + + RuntimeKernel100ns int32 `json:"RuntimeKernel100ns,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/properties.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/properties.go new file mode 100644 index 000000000000..6db2a48f66c9 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/properties.go @@ -0,0 +1,47 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Properties struct { + + Id string `json:"Id,omitempty"` + + SystemType string `json:"SystemType,omitempty"` + + RuntimeOsType string `json:"RuntimeOsType,omitempty"` + + Name string `json:"Name,omitempty"` + + Owner string `json:"Owner,omitempty"` + + RuntimeId string `json:"RuntimeId,omitempty"` + + RuntimeTemplateId string `json:"RuntimeTemplateId,omitempty"` + + State string `json:"State,omitempty"` + + Stopped bool `json:"Stopped,omitempty"` + + ExitType string `json:"ExitType,omitempty"` + + Memory *MemoryInformationForVm `json:"Memory,omitempty"` + + Statistics *Statistics `json:"Statistics,omitempty"` + + ProcessList []ProcessDetails `json:"ProcessList,omitempty"` + + TerminateOnLastHandleClosed bool `json:"TerminateOnLastHandleClosed,omitempty"` + + HostingSystemId string `json:"HostingSystemId,omitempty"` + + SharedMemoryRegionInfo []SharedMemoryRegionInfo `json:"SharedMemoryRegionInfo,omitempty"` + + GuestConnectionInfo *GuestConnectionInfo `json:"GuestConnectionInfo,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/property_query.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/property_query.go new file mode 100644 index 000000000000..22b92ffdfd46 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/property_query.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// By default the basic properties will be returned. This query provides a way to request specific properties. +type PropertyQuery struct { + + PropertyTypes []string `json:"PropertyTypes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/rdp_connection_options.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/rdp_connection_options.go new file mode 100644 index 000000000000..97e453128342 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/rdp_connection_options.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RdpConnectionOptions struct { + + AccessSids []string `json:"AccessSids,omitempty"` + + NamedPipe string `json:"NamedPipe,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_changes.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_changes.go new file mode 100644 index 000000000000..fa574ccc801b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_changes.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RegistryChanges struct { + + AddValues []RegistryValue `json:"AddValues,omitempty"` + + DeleteKeys []RegistryKey `json:"DeleteKeys,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_key.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_key.go new file mode 100644 index 000000000000..fab03bc60bee --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_key.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RegistryKey struct { + + Hive string `json:"Hive,omitempty"` + + Name string `json:"Name,omitempty"` + + Volatile bool `json:"Volatile,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_value.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_value.go new file mode 100644 index 000000000000..1589f4841340 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/registry_value.go @@ -0,0 +1,31 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RegistryValue struct { + + Key *RegistryKey `json:"Key,omitempty"` + + Name string `json:"Name,omitempty"` + + Type_ string `json:"Type,omitempty"` + + // One and only one value type must be set. + StringValue string `json:"StringValue,omitempty"` + + BinaryValue string `json:"BinaryValue,omitempty"` + + DWordValue int32 `json:"DWordValue,omitempty"` + + QWordValue int32 `json:"QWordValue,omitempty"` + + // Only used if RegistryValueType is CustomType The data is in BinaryValue + CustomType int32 `json:"CustomType,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/restore_state.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/restore_state.go new file mode 100644 index 000000000000..778ff58735aa --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/restore_state.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type RestoreState struct { + + // The path to the save state file to restore the system from. + SaveStateFilePath string `json:"SaveStateFilePath,omitempty"` + + // The ID of the template system to clone this new system off of. An empty string indicates the system should not be cloned from a template. + TemplateSystemId string `json:"TemplateSystemId,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/save_options.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/save_options.go new file mode 100644 index 000000000000..e55fa1d98a5b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/save_options.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SaveOptions struct { + + // The type of save operation to be performed. + SaveType string `json:"SaveType,omitempty"` + + // The path to the file that will container the saved state. + SaveStateFilePath string `json:"SaveStateFilePath,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/scsi.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/scsi.go new file mode 100644 index 000000000000..bf253a470b65 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/scsi.go @@ -0,0 +1,16 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Scsi struct { + + // Map of attachments, where the key is the integer LUN number on the controller. + Attachments map[string]Attachment `json:"Attachments,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_configuration.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_configuration.go new file mode 100644 index 000000000000..bd573f6cd4e3 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_configuration.go @@ -0,0 +1,15 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SharedMemoryConfiguration struct { + + Regions []SharedMemoryRegion `json:"Regions,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_region.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_region.go new file mode 100644 index 000000000000..a57b2cba73b8 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_region.go @@ -0,0 +1,23 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SharedMemoryRegion struct { + + SectionName string `json:"SectionName,omitempty"` + + StartOffset int32 `json:"StartOffset,omitempty"` + + Length int32 `json:"Length,omitempty"` + + AllowGuestWrite bool `json:"AllowGuestWrite,omitempty"` + + HiddenFromGuest bool `json:"HiddenFromGuest,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_region_info.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_region_info.go new file mode 100644 index 000000000000..d9a50cc7da88 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/shared_memory_region_info.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type SharedMemoryRegionInfo struct { + + SectionName string `json:"SectionName,omitempty"` + + GuestPhysicalAddress int32 `json:"GuestPhysicalAddress,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/silo_properties.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/silo_properties.go new file mode 100644 index 000000000000..599c06e8aa13 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/silo_properties.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Silo job information +type SiloProperties struct { + + Enabled bool `json:"Enabled,omitempty"` + + JobName string `json:"JobName,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/statistics.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/statistics.go new file mode 100644 index 000000000000..5cb3ed93b598 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/statistics.go @@ -0,0 +1,30 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +import ( + "time" +) + +// Runtime statistics for a container +type Statistics struct { + + Timestamp time.Time `json:"Timestamp,omitempty"` + + ContainerStartTime time.Time `json:"ContainerStartTime,omitempty"` + + Uptime100ns int32 `json:"Uptime100ns,omitempty"` + + Processor *ProcessorStats `json:"Processor,omitempty"` + + Memory *MemoryStats `json:"Memory,omitempty"` + + Storage *StorageStats `json:"Storage,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage.go new file mode 100644 index 000000000000..2627af91323e --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Storage struct { + + // List of layers that describe the parent hierarchy for a container's storage. These layers combined together, presented as a disposable and/or committable working storage, are used by the container to record all changes done to the parent layers. + Layers []Layer `json:"Layers,omitempty"` + + // Path that points to the scratch space of a container, where parent layers are combined together to present a new disposable and/or committable layer with the changes done during its runtime. + Path string `json:"Path,omitempty"` + + QoS *StorageQoS `json:"QoS,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage_qo_s.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage_qo_s.go new file mode 100644 index 000000000000..8c5255df1e39 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage_qo_s.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type StorageQoS struct { + + IopsMaximum int32 `json:"IopsMaximum,omitempty"` + + BandwidthMaximum int32 `json:"BandwidthMaximum,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage_stats.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage_stats.go new file mode 100644 index 000000000000..198ea57d7506 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/storage_stats.go @@ -0,0 +1,22 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +// Storage runtime statistics +type StorageStats struct { + + ReadCountNormalized int32 `json:"ReadCountNormalized,omitempty"` + + ReadSizeBytes int32 `json:"ReadSizeBytes,omitempty"` + + WriteCountNormalized int32 `json:"WriteCountNormalized,omitempty"` + + WriteSizeBytes int32 `json:"WriteSizeBytes,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/topology.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/topology.go new file mode 100644 index 000000000000..af2e3c823464 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/topology.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Topology struct { + + Memory *Memory2 `json:"Memory,omitempty"` + + Processor *Processor2 `json:"Processor,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/uefi.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/uefi.go new file mode 100644 index 000000000000..ba91178f96cd --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/uefi.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Uefi struct { + + EnableDebugger bool `json:"EnableDebugger,omitempty"` + + SecureBootTemplateId string `json:"SecureBootTemplateId,omitempty"` + + BootThis *UefiBootEntry `json:"BootThis,omitempty"` + + Console string `json:"Console,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/uefi_boot_entry.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/uefi_boot_entry.go new file mode 100644 index 000000000000..6620fb2bcf8d --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/uefi_boot_entry.go @@ -0,0 +1,23 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type UefiBootEntry struct { + + DeviceType string `json:"DeviceType,omitempty"` + + DevicePath string `json:"DevicePath,omitempty"` + + DiskNumber int32 `json:"DiskNumber,omitempty"` + + OptionalData string `json:"OptionalData,omitempty"` + + VmbFsRootPath string `json:"VmbFsRootPath,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/version.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/version.go new file mode 100644 index 000000000000..62c0e4d12abb --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/version.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type Version struct { + + Major int32 `json:"Major,omitempty"` + + Minor int32 `json:"Minor,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/video_monitor.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/video_monitor.go new file mode 100644 index 000000000000..0958e5606250 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/video_monitor.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VideoMonitor struct { + + HorizontalResolution int32 `json:"HorizontalResolution,omitempty"` + + VerticalResolution int32 `json:"VerticalResolution,omitempty"` + + ConnectionOptions *RdpConnectionOptions `json:"ConnectionOptions,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_machine.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_machine.go new file mode 100644 index 000000000000..11f39eea7bb4 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_machine.go @@ -0,0 +1,29 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualMachine struct { + + Chipset *Chipset `json:"Chipset,omitempty"` + + ComputeTopology *Topology `json:"ComputeTopology,omitempty"` + + Devices *Devices `json:"Devices,omitempty"` + + GuestState *GuestState `json:"GuestState,omitempty"` + + RestoreState *RestoreState `json:"RestoreState,omitempty"` + + RegistryChanges *RegistryChanges `json:"RegistryChanges,omitempty"` + + StorageQoS *StorageQoS `json:"StorageQoS,omitempty"` + + GuestConnection *GuestConnection `json:"GuestConnection,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_node_info.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_node_info.go new file mode 100644 index 000000000000..48402d8ecb14 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_node_info.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualNodeInfo struct { + + VirtualNodeIndex int32 `json:"VirtualNodeIndex,omitempty"` + + PhysicalNodeNumber int32 `json:"PhysicalNodeNumber,omitempty"` + + VirtualProcessorCount int32 `json:"VirtualProcessorCount,omitempty"` + + MemoryUsageInPages int32 `json:"MemoryUsageInPages,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_controller.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_controller.go new file mode 100644 index 000000000000..f5b7f3e38c0f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_controller.go @@ -0,0 +1,20 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualPMemController struct { + Devices map[string]VirtualPMemDevice `json:"Devices,omitempty"` + + MaximumCount uint32 `json:"MaximumCount,omitempty"` + + MaximumSizeBytes uint64 `json:"MaximumSizeBytes,omitempty"` + + Backing string `json:"Backing,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_device.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_device.go new file mode 100644 index 000000000000..47714444aa09 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_device.go @@ -0,0 +1,19 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualPMemDevice struct { + + HostPath string `json:"HostPath,omitempty"` + + ReadOnly bool `json:"ReadOnly,omitempty"` + + ImageFormat string `json:"ImageFormat,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb.go new file mode 100644 index 000000000000..76131b3a71ac --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualSmb struct { + + Shares []VirtualSmbShare `json:"Shares,omitempty"` + + DirectFileMappingInMB int64 `json:"DirectFileMappingInMB,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb_share.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb_share.go new file mode 100644 index 000000000000..b50098a4232f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb_share.go @@ -0,0 +1,21 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualSmbShare struct { + + Name string `json:"Name,omitempty"` + + Path string `json:"Path,omitempty"` + + AllowedFiles []string `json:"AllowedFiles,omitempty"` + + Options *VirtualSmbShareOptions `json:"Options,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb_share_options.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb_share_options.go new file mode 100644 index 000000000000..c1894279dc85 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_smb_share_options.go @@ -0,0 +1,63 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VirtualSmbShareOptions struct { + + ReadOnly bool `json:"ReadOnly,omitempty"` + + // convert exclusive access to shared read access + ShareRead bool `json:"ShareRead,omitempty"` + + // all opens will use cached I/O + CacheIo bool `json:"CacheIo,omitempty"` + + // disable oplock support + NoOplocks bool `json:"NoOplocks,omitempty"` + + // Acquire the backup privilege when attempting to open + TakeBackupPrivilege bool `json:"TakeBackupPrivilege,omitempty"` + + // Use the identity of the share root when opening + UseShareRootIdentity bool `json:"UseShareRootIdentity,omitempty"` + + // disable Direct Mapping + NoDirectmap bool `json:"NoDirectmap,omitempty"` + + // disable Byterange locks + NoLocks bool `json:"NoLocks,omitempty"` + + // disable Directory CHange Notifications + NoDirnotify bool `json:"NoDirnotify,omitempty"` + + // share is use for VM shared memory + VmSharedMemory bool `json:"VmSharedMemory,omitempty"` + + // allow access only to the files specified in AllowedFiles + RestrictFileAccess bool `json:"RestrictFileAccess,omitempty"` + + // disable all oplocks except Level II + ForceLevelIIOplocks bool `json:"ForceLevelIIOplocks,omitempty"` + + // Allow the host to reparse this base layer + ReparseBaseLayer bool `json:"ReparseBaseLayer,omitempty"` + + // Enable pseudo-oplocks + PseudoOplocks bool `json:"PseudoOplocks,omitempty"` + + // All opens will use non-cached IO + NonCacheIo bool `json:"NonCacheIo,omitempty"` + + // Enable pseudo directory change notifications + PseudoDirnotify bool `json:"PseudoDirnotify,omitempty"` + + // Block directory enumeration, renames, and deletes. + SingleFileMapping bool `json:"SingleFileMapping,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/vm_memory.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/vm_memory.go new file mode 100644 index 000000000000..39f628667cd0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/vm_memory.go @@ -0,0 +1,27 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type VmMemory struct { + + AvailableMemory int32 `json:"AvailableMemory,omitempty"` + + AvailableMemoryBuffer int32 `json:"AvailableMemoryBuffer,omitempty"` + + ReservedMemory int32 `json:"ReservedMemory,omitempty"` + + AssignedMemory int32 `json:"AssignedMemory,omitempty"` + + SlpActive bool `json:"SlpActive,omitempty"` + + BalancingEnabled bool `json:"BalancingEnabled,omitempty"` + + DmOperationInProgress bool `json:"DmOperationInProgress,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/windows_crash_reporting.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/windows_crash_reporting.go new file mode 100644 index 000000000000..cf632bbc8343 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/windows_crash_reporting.go @@ -0,0 +1,17 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.1 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type WindowsCrashReporting struct { + + DumpFileName string `json:"DumpFileName,omitempty"` + + MaxDumpSize int64 `json:"MaxDumpSize,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go index a3817843a64c..d15817730885 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go @@ -9,15 +9,15 @@ import ( // the parent layer provided. func CreateLayer(path, parent string) error { title := "hcsshim::CreateLayer " - logrus.Debugf(title+"Flavour %d ID %s parent %s", path, parent) + logrus.Debugf(title+"ID %s parent %s", path, parent) err := createLayer(&stdDriverInfo, path, parent) if err != nil { - err = hcserror.Errorf(err, title, "path=%s parent=%s flavour=%d", path, parent) + err = hcserror.Errorf(err, title, "path=%s parent=%s", path, parent) logrus.Error(err) return err } - logrus.Debugf(title+" - succeeded path=%s parent=%s flavour=%d", path, parent) + logrus.Debugf(title+"- succeeded path=%s parent=%s", path, parent) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/layer.go b/vendor/github.com/Microsoft/hcsshim/layer.go index 8cdc247dcdb5..d143efc49aa4 100644 --- a/vendor/github.com/Microsoft/hcsshim/layer.go +++ b/vendor/github.com/Microsoft/hcsshim/layer.go @@ -19,6 +19,7 @@ func ActivateLayer(info DriverInfo, id string) error { func CreateLayer(info DriverInfo, id, parent string) error { return wclayer.CreateLayer(layerPath(&info, id), parent) } + // New clients should use CreateScratchLayer instead. Kept in to preserve API compatibility. func CreateSandboxLayer(info DriverInfo, layerId, parentId string, parentLayerPaths []string) error { return wclayer.CreateScratchLayer(layerPath(&info, layerId), parentLayerPaths) @@ -32,6 +33,7 @@ func DeactivateLayer(info DriverInfo, id string) error { func DestroyLayer(info DriverInfo, id string) error { return wclayer.DestroyLayer(layerPath(&info, id)) } + // New clients should use ExpandScratchSize instead. Kept in to preserve API compatibility. func ExpandSandboxSize(info DriverInfo, layerId string, size uint64) error { return wclayer.ExpandScratchSize(layerPath(&info, layerId), size) diff --git a/vendor/github.com/Microsoft/hcsshim/version.go b/vendor/github.com/Microsoft/hcsshim/version.go deleted file mode 100644 index 9ebb257b3e4c..000000000000 --- a/vendor/github.com/Microsoft/hcsshim/version.go +++ /dev/null @@ -1,6 +0,0 @@ -package hcsshim - -// IsTP4 returns whether the currently running Windows build is at least TP4. -func IsTP4() bool { - return false -} diff --git a/vendor/github.com/containerd/containerd/README.md b/vendor/github.com/containerd/containerd/README.md index c4d9dffdbdc5..2055404b5b73 100644 --- a/vendor/github.com/containerd/containerd/README.md +++ b/vendor/github.com/containerd/containerd/README.md @@ -1,4 +1,4 @@ -![banner](https://github.com/containerd/containerd.io/blob/master/static/img/containerd-dark.png?raw=true) +![containerd banner](https://raw.githubusercontent.com/cncf/artwork/master/containerd/horizontal/color/containerd-horizontal-color.png) [![GoDoc](https://godoc.org/github.com/containerd/containerd?status.svg)](https://godoc.org/github.com/containerd/containerd) [![Build Status](https://travis-ci.org/containerd/containerd.svg?branch=master)](https://travis-ci.org/containerd/containerd) @@ -236,3 +236,16 @@ The containerd codebase is released under the [Apache 2.0 license](LICENSE.code) The README.md file, and files in the "docs" folder are licensed under the Creative Commons Attribution 4.0 International License. You may obtain a copy of the license, titled CC-BY-4.0, at http://creativecommons.org/licenses/by/4.0/. + +## Project details + +**containerd** is the primary open source project within the broader containerd GitHub repository. +However, all projects within the repo have common maintainership, governance, and contributing +guidelines which are stored in a `project` repository commonly for all containerd projects. + +Please find all these core project documents, including the: + * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/vendor/github.com/containerd/containerd/api/services/content/v1/content.pb.go b/vendor/github.com/containerd/containerd/api/services/content/v1/content.pb.go index b539ae1aee4e..ec08c3b233d2 100644 --- a/vendor/github.com/containerd/containerd/api/services/content/v1/content.pb.go +++ b/vendor/github.com/containerd/containerd/api/services/content/v1/content.pb.go @@ -443,7 +443,7 @@ type ContentClient interface { // Only one active stream may exist at a time for each ref. // // Once a write stream has started, it may only write to a single ref, thus - // once a stream is started, the ref may be ommitted on subsequent writes. + // once a stream is started, the ref may be omitted on subsequent writes. // // For any write transaction represented by a ref, only a single write may // be made to a given offset. If overlapping writes occur, it is an error. @@ -658,7 +658,7 @@ type ContentServer interface { // Only one active stream may exist at a time for each ref. // // Once a write stream has started, it may only write to a single ref, thus - // once a stream is started, the ref may be ommitted on subsequent writes. + // once a stream is started, the ref may be omitted on subsequent writes. // // For any write transaction represented by a ref, only a single write may // be made to a given offset. If overlapping writes occur, it is an error. diff --git a/vendor/github.com/containerd/containerd/api/services/content/v1/content.proto b/vendor/github.com/containerd/containerd/api/services/content/v1/content.proto index 4f1187145bb4..086b3e39b1fe 100644 --- a/vendor/github.com/containerd/containerd/api/services/content/v1/content.proto +++ b/vendor/github.com/containerd/containerd/api/services/content/v1/content.proto @@ -55,7 +55,7 @@ service Content { // Only one active stream may exist at a time for each ref. // // Once a write stream has started, it may only write to a single ref, thus - // once a stream is started, the ref may be ommitted on subsequent writes. + // once a stream is started, the ref may be omitted on subsequent writes. // // For any write transaction represented by a ref, only a single write may // be made to a given offset. If overlapping writes occur, it is an error. diff --git a/vendor/github.com/containerd/containerd/archive/compression/compression.go b/vendor/github.com/containerd/containerd/archive/compression/compression.go index bd50f083b201..60c80e98a595 100644 --- a/vendor/github.com/containerd/containerd/archive/compression/compression.go +++ b/vendor/github.com/containerd/containerd/archive/compression/compression.go @@ -20,9 +20,15 @@ import ( "bufio" "bytes" "compress/gzip" + "context" "fmt" "io" + "os" + "os/exec" + "strconv" "sync" + + "github.com/containerd/containerd/log" ) type ( @@ -37,6 +43,13 @@ const ( Gzip ) +const disablePigzEnv = "CONTAINERD_DISABLE_PIGZ" + +var ( + initPigz sync.Once + unpigzPath string +) + var ( bufioReader32KPool = &sync.Pool{ New: func() interface{} { return bufio.NewReaderSize(nil, 32*1024) }, @@ -79,6 +92,36 @@ func (w *writeCloserWrapper) Close() error { return nil } +type bufferedReader struct { + buf *bufio.Reader +} + +func newBufferedReader(r io.Reader) *bufferedReader { + buf := bufioReader32KPool.Get().(*bufio.Reader) + buf.Reset(r) + return &bufferedReader{buf} +} + +func (r *bufferedReader) Read(p []byte) (n int, err error) { + if r.buf == nil { + return 0, io.EOF + } + n, err = r.buf.Read(p) + if err == io.EOF { + r.buf.Reset(nil) + bufioReader32KPool.Put(r.buf) + r.buf = nil + } + return +} + +func (r *bufferedReader) Peek(n int) ([]byte, error) { + if r.buf == nil { + return nil, io.EOF + } + return r.buf.Peek(n) +} + // DetectCompression detects the compression algorithm of the source. func DetectCompression(source []byte) Compression { for compression, m := range map[Compression][]byte{ @@ -97,8 +140,7 @@ func DetectCompression(source []byte) Compression { // DecompressStream decompresses the archive and returns a ReaderCloser with the decompressed archive. func DecompressStream(archive io.Reader) (DecompressReadCloser, error) { - buf := bufioReader32KPool.Get().(*bufio.Reader) - buf.Reset(archive) + buf := newBufferedReader(archive) bs, err := buf.Peek(10) if err != nil && err != io.EOF { // Note: we'll ignore any io.EOF error because there are some odd @@ -110,22 +152,29 @@ func DecompressStream(archive io.Reader) (DecompressReadCloser, error) { return nil, err } - closer := func() error { - buf.Reset(nil) - bufioReader32KPool.Put(buf) - return nil - } switch compression := DetectCompression(bs); compression { case Uncompressed: - readBufWrapper := &readCloserWrapper{buf, compression, closer} - return readBufWrapper, nil + return &readCloserWrapper{ + Reader: buf, + compression: compression, + }, nil case Gzip: - gzReader, err := gzip.NewReader(buf) + ctx, cancel := context.WithCancel(context.Background()) + gzReader, err := gzipDecompress(ctx, buf) if err != nil { + cancel() return nil, err } - readBufWrapper := &readCloserWrapper{gzReader, compression, closer} - return readBufWrapper, nil + + return &readCloserWrapper{ + Reader: gzReader, + compression: compression, + closer: func() error { + cancel() + return gzReader.Close() + }, + }, nil + default: return nil, fmt.Errorf("unsupported compression format %s", (&compression).Extension()) } @@ -151,3 +200,67 @@ func (compression *Compression) Extension() string { } return "" } + +func gzipDecompress(ctx context.Context, buf io.Reader) (io.ReadCloser, error) { + initPigz.Do(func() { + if unpigzPath = detectPigz(); unpigzPath != "" { + log.L.Debug("using pigz for decompression") + } + }) + + if unpigzPath == "" { + return gzip.NewReader(buf) + } + + return cmdStream(exec.CommandContext(ctx, unpigzPath, "-d", "-c"), buf) +} + +func cmdStream(cmd *exec.Cmd, in io.Reader) (io.ReadCloser, error) { + reader, writer := io.Pipe() + + cmd.Stdin = in + cmd.Stdout = writer + + var errBuf bytes.Buffer + cmd.Stderr = &errBuf + + if err := cmd.Start(); err != nil { + return nil, err + } + + go func() { + if err := cmd.Wait(); err != nil { + writer.CloseWithError(fmt.Errorf("%s: %s", err, errBuf.String())) + } else { + writer.Close() + } + }() + + return reader, nil +} + +func detectPigz() string { + path, err := exec.LookPath("unpigz") + if err != nil { + log.L.WithError(err).Debug("unpigz not found, falling back to go gzip") + return "" + } + + // Check if pigz disabled via CONTAINERD_DISABLE_PIGZ env variable + value := os.Getenv(disablePigzEnv) + if value == "" { + return path + } + + disable, err := strconv.ParseBool(value) + if err != nil { + log.L.WithError(err).Warnf("could not parse %s: %s", disablePigzEnv, value) + return path + } + + if disable { + return "" + } + + return path +} diff --git a/vendor/github.com/containerd/containerd/archive/time_unix.go b/vendor/github.com/containerd/containerd/archive/time_unix.go index 4a69cb7d0ed6..53d655be465d 100644 --- a/vendor/github.com/containerd/containerd/archive/time_unix.go +++ b/vendor/github.com/containerd/containerd/archive/time_unix.go @@ -1,4 +1,4 @@ -// +build linux freebsd solaris +// +build freebsd linux openbsd solaris /* Copyright The containerd Authors. diff --git a/vendor/github.com/containerd/containerd/container_opts.go b/vendor/github.com/containerd/containerd/container_opts.go index 580715feeb69..ca4bf67486a1 100644 --- a/vendor/github.com/containerd/containerd/container_opts.go +++ b/vendor/github.com/containerd/containerd/container_opts.go @@ -76,6 +76,23 @@ func WithContainerLabels(labels map[string]string) NewContainerOpts { } } +// WithImageStopSignal sets a well-known containerd label (StopSignalLabel) +// on the container for storing the stop signal specified in the OCI image +// config +func WithImageStopSignal(image Image, defaultSignal string) NewContainerOpts { + return func(ctx context.Context, _ *Client, c *containers.Container) error { + if c.Labels == nil { + c.Labels = make(map[string]string) + } + stopSignal, err := GetOCIStopSignal(ctx, image, defaultSignal) + if err != nil { + return err + } + c.Labels[StopSignalLabel] = stopSignal + return nil + } +} + // WithSnapshotter sets the provided snapshotter for use by the container // // This option must appear before other snapshotter options to have an effect. diff --git a/vendor/github.com/containerd/containerd/containers/containers.go b/vendor/github.com/containerd/containerd/containers/containers.go index e6a562730ebd..a658b57082d7 100644 --- a/vendor/github.com/containerd/containerd/containers/containers.go +++ b/vendor/github.com/containerd/containerd/containers/containers.go @@ -28,12 +28,12 @@ import ( // // The resources specified in this object are used to create tasks from the container. type Container struct { - // ID uniquely identifies the container in a nameapace. + // ID uniquely identifies the container in a namespace. // // This property is required and cannot be changed after creation. ID string - // Labels provide metadata extension for a contaienr. + // Labels provide metadata extension for a container. // // These are optional and fully mutable. Labels map[string]string diff --git a/vendor/github.com/containerd/containerd/content/content.go b/vendor/github.com/containerd/containerd/content/content.go index aabf4c8f31ea..d8141a68bc05 100644 --- a/vendor/github.com/containerd/containerd/content/content.go +++ b/vendor/github.com/containerd/containerd/content/content.go @@ -110,8 +110,9 @@ type IngestManager interface { // Writer handles the write of content into a content store type Writer interface { - // Close is expected to be called after Commit() when commission is needed. - // Closing a writer without commit allows resuming or aborting. + // Close closes the writer, if the writer has not been + // committed this allows resuming or aborting. + // Calling Close on a closed writer will not error. io.WriteCloser // Digest may return empty digest or panics until committed. @@ -119,6 +120,8 @@ type Writer interface { // Commit commits the blob (but no roll-back is guaranteed on an error). // size and expected can be zero-value when unknown. + // Commit always closes the writer, even on error. + // ErrAlreadyExists aborts the writer. Commit(ctx context.Context, size int64, expected digest.Digest, opts ...Opt) error // Status returns the current state of write diff --git a/vendor/github.com/containerd/containerd/content/helpers.go b/vendor/github.com/containerd/containerd/content/helpers.go index 819b7ea1e320..3e231408d55e 100644 --- a/vendor/github.com/containerd/containerd/content/helpers.go +++ b/vendor/github.com/containerd/containerd/content/helpers.go @@ -70,7 +70,7 @@ func WriteBlob(ctx context.Context, cs Ingester, ref string, r io.Reader, desc o cw, err := OpenWriter(ctx, cs, WithRef(ref), WithDescriptor(desc)) if err != nil { if !errdefs.IsAlreadyExists(err) { - return err + return errors.Wrap(err, "failed to open writer") } return nil // all ready present @@ -127,7 +127,7 @@ func OpenWriter(ctx context.Context, cs Ingester, opts ...WriterOpt) (Writer, er func Copy(ctx context.Context, cw Writer, r io.Reader, size int64, expected digest.Digest, opts ...Opt) error { ws, err := cw.Status() if err != nil { - return err + return errors.Wrap(err, "failed to get status") } if ws.Offset > 0 { @@ -138,7 +138,7 @@ func Copy(ctx context.Context, cw Writer, r io.Reader, size int64, expected dige } if _, err := copyWithBuffer(cw, r); err != nil { - return err + return errors.Wrap(err, "failed to copy") } if err := cw.Commit(ctx, size, expected, opts...); err != nil { diff --git a/vendor/github.com/containerd/containerd/content/proxy/content_writer.go b/vendor/github.com/containerd/containerd/content/proxy/content_writer.go index 6d35ba61a87c..5434a156864c 100644 --- a/vendor/github.com/containerd/containerd/content/proxy/content_writer.go +++ b/vendor/github.com/containerd/containerd/content/proxy/content_writer.go @@ -57,7 +57,7 @@ func (rw *remoteWriter) Status() (content.Status, error) { Action: contentapi.WriteActionStat, }) if err != nil { - return content.Status{}, errors.Wrap(err, "error getting writer status") + return content.Status{}, errors.Wrap(errdefs.FromGRPC(err), "error getting writer status") } return content.Status{ @@ -82,7 +82,7 @@ func (rw *remoteWriter) Write(p []byte) (n int, err error) { Data: p, }) if err != nil { - return 0, err + return 0, errors.Wrap(errdefs.FromGRPC(err), "failed to send write") } n = int(resp.Offset - offset) @@ -112,7 +112,7 @@ func (rw *remoteWriter) Commit(ctx context.Context, size int64, expected digest. Labels: base.Labels, }) if err != nil { - return errdefs.FromGRPC(err) + return errors.Wrap(errdefs.FromGRPC(err), "commit failed") } if size != 0 && resp.Offset != size { diff --git a/vendor/github.com/containerd/containerd/events.go b/vendor/github.com/containerd/containerd/events.go index 92e9cd510457..3577b7c3a9fc 100644 --- a/vendor/github.com/containerd/containerd/events.go +++ b/vendor/github.com/containerd/containerd/events.go @@ -110,6 +110,9 @@ func (e *eventRemote) Subscribe(ctx context.Context, filters ...string) (ch <-ch Event: ev.Event, }: case <-ctx.Done(): + if cerr := ctx.Err(); cerr != context.Canceled { + errq <- cerr + } return } } diff --git a/vendor/github.com/containerd/containerd/export.go b/vendor/github.com/containerd/containerd/export.go index 7aac309ba0e4..bfc25316ca94 100644 --- a/vendor/github.com/containerd/containerd/export.go +++ b/vendor/github.com/containerd/containerd/export.go @@ -22,6 +22,7 @@ import ( "github.com/containerd/containerd/images" ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" ) type exportOpts struct { @@ -51,7 +52,7 @@ func (c *Client) Export(ctx context.Context, exporter images.Exporter, desc ocis } pr, pw := io.Pipe() go func() { - pw.CloseWithError(exporter.Export(ctx, c.ContentStore(), desc, pw)) + pw.CloseWithError(errors.Wrap(exporter.Export(ctx, c.ContentStore(), desc, pw), "export failed")) }() return pr, nil } diff --git a/vendor/github.com/containerd/containerd/image.go b/vendor/github.com/containerd/containerd/image.go index f12cd59c00d3..62fba9de754a 100644 --- a/vendor/github.com/containerd/containerd/image.go +++ b/vendor/github.com/containerd/containerd/image.go @@ -37,6 +37,8 @@ type Image interface { Name() string // Target descriptor for the image content Target() ocispec.Descriptor + // Labels of the image + Labels() map[string]string // Unpack unpacks the image's content into a snapshot Unpack(context.Context, string) error // RootFS returns the unpacked diffids that make up images rootfs. @@ -86,6 +88,10 @@ func (i *image) Target() ocispec.Descriptor { return i.i.Target } +func (i *image) Labels() map[string]string { + return i.i.Labels +} + func (i *image) RootFS(ctx context.Context) ([]digest.Digest, error) { provider := i.client.ContentStore() return i.i.RootFS(ctx, provider, i.platform) diff --git a/vendor/github.com/containerd/containerd/images/archive/importer.go b/vendor/github.com/containerd/containerd/images/archive/importer.go new file mode 100644 index 000000000000..da83275c3aaf --- /dev/null +++ b/vendor/github.com/containerd/containerd/images/archive/importer.go @@ -0,0 +1,262 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +// Package archive provides a Docker and OCI compatible importer +package archive + +import ( + "archive/tar" + "bytes" + "context" + "encoding/json" + "io" + "io/ioutil" + "path" + + "github.com/containerd/containerd/archive/compression" + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/log" + digest "github.com/opencontainers/go-digest" + specs "github.com/opencontainers/image-spec/specs-go" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/pkg/errors" +) + +// ImportIndex imports an index from a tar archive image bundle +// - implements Docker v1.1, v1.2 and OCI v1. +// - prefers OCI v1 when provided +// - creates OCI index for Docker formats +// - normalizes Docker references and adds as OCI ref name +// e.g. alpine:latest -> docker.io/library/alpine:latest +// - existing OCI reference names are untouched +// - TODO: support option to compress layers on ingest +func ImportIndex(ctx context.Context, store content.Store, reader io.Reader) (ocispec.Descriptor, error) { + var ( + tr = tar.NewReader(reader) + + ociLayout ocispec.ImageLayout + mfsts []struct { + Config string + RepoTags []string + Layers []string + } + symlinks = make(map[string]string) + blobs = make(map[string]ocispec.Descriptor) + ) + for { + hdr, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return ocispec.Descriptor{}, err + } + if hdr.Typeflag == tar.TypeSymlink { + symlinks[hdr.Name] = path.Join(path.Dir(hdr.Name), hdr.Linkname) + } + + if hdr.Typeflag != tar.TypeReg && hdr.Typeflag != tar.TypeRegA { + if hdr.Typeflag != tar.TypeDir { + log.G(ctx).WithField("file", hdr.Name).Debug("file type ignored") + } + continue + } + + hdrName := path.Clean(hdr.Name) + if hdrName == ocispec.ImageLayoutFile { + if err = onUntarJSON(tr, &ociLayout); err != nil { + return ocispec.Descriptor{}, errors.Wrapf(err, "untar oci layout %q", hdr.Name) + } + } else if hdrName == "manifest.json" { + if err = onUntarJSON(tr, &mfsts); err != nil { + return ocispec.Descriptor{}, errors.Wrapf(err, "untar manifest %q", hdr.Name) + } + } else { + dgst, err := onUntarBlob(ctx, tr, store, hdr.Size, "tar-"+hdrName) + if err != nil { + return ocispec.Descriptor{}, errors.Wrapf(err, "failed to ingest %q", hdr.Name) + } + + blobs[hdrName] = ocispec.Descriptor{ + Digest: dgst, + Size: hdr.Size, + } + } + } + + // If OCI layout was given, interpret the tar as an OCI layout. + // When not provided, the layout of the tar will be interpretted + // as Docker v1.1 or v1.2. + if ociLayout.Version != "" { + if ociLayout.Version != ocispec.ImageLayoutVersion { + return ocispec.Descriptor{}, errors.Errorf("unsupported OCI version %s", ociLayout.Version) + } + + idx, ok := blobs["index.json"] + if !ok { + return ocispec.Descriptor{}, errors.Errorf("missing index.json in OCI layout %s", ocispec.ImageLayoutVersion) + } + + idx.MediaType = ocispec.MediaTypeImageIndex + return idx, nil + } + + if mfsts == nil { + return ocispec.Descriptor{}, errors.Errorf("unrecognized image format") + } + + for name, linkname := range symlinks { + desc, ok := blobs[linkname] + if !ok { + return ocispec.Descriptor{}, errors.Errorf("no target for symlink layer from %q to %q", name, linkname) + } + blobs[name] = desc + } + + idx := ocispec.Index{ + Versioned: specs.Versioned{ + SchemaVersion: 2, + }, + } + for _, mfst := range mfsts { + config, ok := blobs[mfst.Config] + if !ok { + return ocispec.Descriptor{}, errors.Errorf("image config %q not found", mfst.Config) + } + config.MediaType = ocispec.MediaTypeImageConfig + + layers, err := resolveLayers(ctx, store, mfst.Layers, blobs) + if err != nil { + return ocispec.Descriptor{}, errors.Wrap(err, "failed to resolve layers") + } + + manifest := ocispec.Manifest{ + Versioned: specs.Versioned{ + SchemaVersion: 2, + }, + Config: config, + Layers: layers, + } + + desc, err := writeManifest(ctx, store, manifest, ocispec.MediaTypeImageManifest) + if err != nil { + return ocispec.Descriptor{}, errors.Wrap(err, "write docker manifest") + } + + platforms, err := images.Platforms(ctx, store, desc) + if err != nil { + return ocispec.Descriptor{}, errors.Wrap(err, "unable to resolve platform") + } + if len(platforms) > 0 { + // Only one platform can be resolved from non-index manifest, + // The platform can only come from the config included above, + // if the config has no platform it can be safely omitted. + desc.Platform = &platforms[0] + } + + if len(mfst.RepoTags) == 0 { + idx.Manifests = append(idx.Manifests, desc) + } else { + // Add descriptor per tag + for _, ref := range mfst.RepoTags { + mfstdesc := desc + + normalized, err := normalizeReference(ref) + if err != nil { + return ocispec.Descriptor{}, err + } + + mfstdesc.Annotations = map[string]string{ + ocispec.AnnotationRefName: normalized, + } + + idx.Manifests = append(idx.Manifests, mfstdesc) + } + } + } + + return writeManifest(ctx, store, idx, ocispec.MediaTypeImageIndex) +} + +func onUntarJSON(r io.Reader, j interface{}) error { + b, err := ioutil.ReadAll(r) + if err != nil { + return err + } + if err := json.Unmarshal(b, j); err != nil { + return err + } + return nil +} + +func onUntarBlob(ctx context.Context, r io.Reader, store content.Ingester, size int64, ref string) (digest.Digest, error) { + dgstr := digest.Canonical.Digester() + + if err := content.WriteBlob(ctx, store, ref, io.TeeReader(r, dgstr.Hash()), ocispec.Descriptor{Size: size}); err != nil { + return "", err + } + + return dgstr.Digest(), nil +} + +func resolveLayers(ctx context.Context, store content.Store, layerFiles []string, blobs map[string]ocispec.Descriptor) ([]ocispec.Descriptor, error) { + var layers []ocispec.Descriptor + for _, f := range layerFiles { + desc, ok := blobs[f] + if !ok { + return nil, errors.Errorf("layer %q not found", f) + } + + // Open blob, resolve media type + ra, err := store.ReaderAt(ctx, desc) + if err != nil { + return nil, errors.Wrapf(err, "failed to open %q (%s)", f, desc.Digest) + } + s, err := compression.DecompressStream(content.NewReader(ra)) + if err != nil { + return nil, errors.Wrapf(err, "failed to detect compression for %q", f) + } + if s.GetCompression() == compression.Uncompressed { + // TODO: Support compressing and writing back to content store + desc.MediaType = ocispec.MediaTypeImageLayer + } else { + desc.MediaType = ocispec.MediaTypeImageLayerGzip + } + s.Close() + + layers = append(layers, desc) + } + return layers, nil +} + +func writeManifest(ctx context.Context, cs content.Ingester, manifest interface{}, mediaType string) (ocispec.Descriptor, error) { + manifestBytes, err := json.Marshal(manifest) + if err != nil { + return ocispec.Descriptor{}, err + } + + desc := ocispec.Descriptor{ + MediaType: mediaType, + Digest: digest.FromBytes(manifestBytes), + Size: int64(len(manifestBytes)), + } + if err := content.WriteBlob(ctx, cs, "manifest-"+desc.Digest.String(), bytes.NewReader(manifestBytes), desc); err != nil { + return ocispec.Descriptor{}, err + } + + return desc, nil +} diff --git a/vendor/github.com/containerd/containerd/images/archive/reference.go b/vendor/github.com/containerd/containerd/images/archive/reference.go new file mode 100644 index 000000000000..0b1310181e42 --- /dev/null +++ b/vendor/github.com/containerd/containerd/images/archive/reference.go @@ -0,0 +1,86 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package archive + +import ( + "strings" + + "github.com/containerd/cri/pkg/util" + digest "github.com/opencontainers/go-digest" + "github.com/pkg/errors" +) + +// FilterRefPrefix restricts references to having the given image +// prefix. Tag-only references will have the prefix prepended. +func FilterRefPrefix(image string) func(string) string { + return refTranslator(image, true) +} + +// AddRefPrefix prepends the given image prefix to tag-only references, +// while leaving returning full references unmodified. +func AddRefPrefix(image string) func(string) string { + return refTranslator(image, false) +} + +// refTranslator creates a reference which only has a tag or verifies +// a full reference. +func refTranslator(image string, checkPrefix bool) func(string) string { + return func(ref string) string { + // Check if ref is full reference + if strings.ContainsAny(ref, "/:@") { + // If not prefixed, don't include image + if checkPrefix && !isImagePrefix(ref, image) { + return "" + } + return ref + } + return image + ":" + ref + } +} + +func isImagePrefix(s, prefix string) bool { + if !strings.HasPrefix(s, prefix) { + return false + } + if len(s) > len(prefix) { + switch s[len(prefix)] { + case '/', ':', '@': + // Prevent matching partial namespaces + default: + return false + } + } + return true +} + +func normalizeReference(ref string) (string, error) { + // TODO: Replace this function to not depend on reference package + normalized, err := util.NormalizeImageRef(ref) + if err != nil { + return "", errors.Wrapf(err, "normalize image ref %q", ref) + } + + return normalized.String(), nil +} + +// DigestTranslator creates a digest reference by adding the +// digest to an image name +func DigestTranslator(prefix string) func(digest.Digest) string { + return func(dgst digest.Digest) string { + return prefix + "@" + dgst.String() + } +} diff --git a/vendor/github.com/containerd/containerd/images/image.go b/vendor/github.com/containerd/containerd/images/image.go index 4d6979d7af21..f72684d82946 100644 --- a/vendor/github.com/containerd/containerd/images/image.go +++ b/vendor/github.com/containerd/containerd/images/image.go @@ -129,6 +129,13 @@ type platformManifest struct { // Manifest resolves a manifest from the image for the given platform. // +// When a manifest descriptor inside of a manifest index does not have +// a platform defined, the platform from the image config is considered. +// +// If the descriptor points to a non-index manifest, then the manifest is +// unmarshalled and returned without considering the platform inside of the +// config. +// // TODO(stevvooe): This violates the current platform agnostic approach to this // package by returning a specific manifest type. We'll need to refactor this // to return a manifest descriptor or decide that we want to bring the API in @@ -152,7 +159,7 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc return nil, err } - if platform != nil { + if desc.Digest != image.Digest && platform != nil { if desc.Platform != nil && !platform.Match(*desc.Platform) { return nil, nil } diff --git a/vendor/github.com/containerd/containerd/images/importexport.go b/vendor/github.com/containerd/containerd/images/importexport.go index 04a55fd383fd..843adcadc7ec 100644 --- a/vendor/github.com/containerd/containerd/images/importexport.go +++ b/vendor/github.com/containerd/containerd/images/importexport.go @@ -27,7 +27,7 @@ import ( // Importer is the interface for image importer. type Importer interface { // Import imports an image from a tar stream. - Import(ctx context.Context, store content.Store, reader io.Reader) ([]Image, error) + Import(ctx context.Context, store content.Store, reader io.Reader) (ocispec.Descriptor, error) } // Exporter is the interface for image exporter. diff --git a/vendor/github.com/containerd/containerd/import.go b/vendor/github.com/containerd/containerd/import.go index 7a69f1d45aa5..365056824097 100644 --- a/vendor/github.com/containerd/containerd/import.go +++ b/vendor/github.com/containerd/containerd/import.go @@ -18,35 +18,61 @@ package containerd import ( "context" + "encoding/json" "io" + "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" + "github.com/containerd/containerd/images/archive" + digest "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) type importOpts struct { + indexName string + imageRefT func(string) string + dgstRefT func(digest.Digest) string } // ImportOpt allows the caller to specify import specific options -type ImportOpt func(c *importOpts) error +type ImportOpt func(*importOpts) error + +// WithImageRefTranslator is used to translate the index reference +// to an image reference for the image store. +func WithImageRefTranslator(f func(string) string) ImportOpt { + return func(c *importOpts) error { + c.imageRefT = f + return nil + } +} -func resolveImportOpt(opts ...ImportOpt) (importOpts, error) { - var iopts importOpts - for _, o := range opts { - if err := o(&iopts); err != nil { - return iopts, err - } +// WithDigestRef is used to create digest images for each +// manifest in the index. +func WithDigestRef(f func(digest.Digest) string) ImportOpt { + return func(c *importOpts) error { + c.dgstRefT = f + return nil + } +} + +// WithIndexName creates a tag pointing to the imported index +func WithIndexName(name string) ImportOpt { + return func(c *importOpts) error { + c.indexName = name + return nil } - return iopts, nil } // Import imports an image from a Tar stream using reader. // Caller needs to specify importer. Future version may use oci.v1 as the default. // Note that unreferrenced blobs may be imported to the content store as well. -func (c *Client) Import(ctx context.Context, importer images.Importer, reader io.Reader, opts ...ImportOpt) ([]Image, error) { - _, err := resolveImportOpt(opts...) // unused now - if err != nil { - return nil, err +func (c *Client) Import(ctx context.Context, reader io.Reader, opts ...ImportOpt) ([]images.Image, error) { + var iopts importOpts + for _, o := range opts { + if err := o(&iopts); err != nil { + return nil, err + } } ctx, done, err := c.WithLease(ctx) @@ -55,31 +81,86 @@ func (c *Client) Import(ctx context.Context, importer images.Importer, reader io } defer done(ctx) - imgrecs, err := importer.Import(ctx, c.ContentStore(), reader) + index, err := archive.ImportIndex(ctx, c.ContentStore(), reader) if err != nil { - // is.Update() is not called on error return nil, err } - is := c.ImageService() - var images []Image - for _, imgrec := range imgrecs { - if updated, err := is.Update(ctx, imgrec, "target"); err != nil { + var ( + imgs []images.Image + cs = c.ContentStore() + is = c.ImageService() + ) + + if iopts.indexName != "" { + imgs = append(imgs, images.Image{ + Name: iopts.indexName, + Target: index, + }) + } + + var handler images.HandlerFunc + handler = func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { + // Only save images at top level + if desc.Digest != index.Digest { + return images.Children(ctx, cs, desc) + } + + p, err := content.ReadBlob(ctx, cs, desc) + if err != nil { + return nil, err + } + + var idx ocispec.Index + if err := json.Unmarshal(p, &idx); err != nil { + return nil, err + } + + for _, m := range idx.Manifests { + if ref := m.Annotations[ocispec.AnnotationRefName]; ref != "" { + if iopts.imageRefT != nil { + ref = iopts.imageRefT(ref) + } + if ref != "" { + imgs = append(imgs, images.Image{ + Name: ref, + Target: m, + }) + } + } + if iopts.dgstRefT != nil { + ref := iopts.dgstRefT(m.Digest) + if ref != "" { + imgs = append(imgs, images.Image{ + Name: ref, + Target: m, + }) + } + } + } + + return idx.Manifests, nil + } + + handler = images.SetChildrenLabels(cs, handler) + if err := images.Walk(ctx, handler, index); err != nil { + return nil, err + } + + for i := range imgs { + img, err := is.Update(ctx, imgs[i], "target") + if err != nil { if !errdefs.IsNotFound(err) { return nil, err } - created, err := is.Create(ctx, imgrec) + img, err = is.Create(ctx, imgs[i]) if err != nil { return nil, err } - - imgrec = created - } else { - imgrec = updated } - - images = append(images, NewImage(c, imgrec)) + imgs[i] = img } - return images, nil + + return imgs, nil } diff --git a/vendor/github.com/containerd/containerd/mount/mount_unix.go b/vendor/github.com/containerd/containerd/mount/mount_unix.go index 6741293f89f8..95da9428e6a7 100644 --- a/vendor/github.com/containerd/containerd/mount/mount_unix.go +++ b/vendor/github.com/containerd/containerd/mount/mount_unix.go @@ -1,4 +1,4 @@ -// +build darwin freebsd +// +build darwin freebsd openbsd /* Copyright The containerd Authors. diff --git a/vendor/github.com/containerd/containerd/mount/mountinfo_freebsd.go b/vendor/github.com/containerd/containerd/mount/mountinfo_bsd.go similarity index 98% rename from vendor/github.com/containerd/containerd/mount/mountinfo_freebsd.go rename to vendor/github.com/containerd/containerd/mount/mountinfo_bsd.go index bbe79767e334..8f8dbf95a4f9 100644 --- a/vendor/github.com/containerd/containerd/mount/mountinfo_freebsd.go +++ b/vendor/github.com/containerd/containerd/mount/mountinfo_bsd.go @@ -1,3 +1,5 @@ +// +build freebsd openbsd + /* Copyright The containerd Authors. diff --git a/vendor/github.com/containerd/containerd/mount/mountinfo_unsupported.go b/vendor/github.com/containerd/containerd/mount/mountinfo_unsupported.go index eba602f1a648..ae998db6b5ad 100644 --- a/vendor/github.com/containerd/containerd/mount/mountinfo_unsupported.go +++ b/vendor/github.com/containerd/containerd/mount/mountinfo_unsupported.go @@ -1,4 +1,4 @@ -// +build !linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo +// +build !linux,!freebsd,!solaris,!openbsd freebsd,!cgo solaris,!cgo openbsd,!cgo /* Copyright The containerd Authors. diff --git a/vendor/github.com/containerd/containerd/oci/spec.go b/vendor/github.com/containerd/containerd/oci/spec.go index 6fb31e454c9d..a30c95306925 100644 --- a/vendor/github.com/containerd/containerd/oci/spec.go +++ b/vendor/github.com/containerd/containerd/oci/spec.go @@ -167,6 +167,7 @@ func populateDefaultUnixSpec(ctx context.Context, s *Spec, id string) error { Destination: "/proc", Type: "proc", Source: "proc", + Options: []string{"nosuid", "noexec", "nodev"}, }, { Destination: "/dev", @@ -208,6 +209,7 @@ func populateDefaultUnixSpec(ctx context.Context, s *Spec, id string) error { Linux: &specs.Linux{ MaskedPaths: []string{ "/proc/acpi", + "/proc/asound", "/proc/kcore", "/proc/keys", "/proc/latency_stats", @@ -218,7 +220,6 @@ func populateDefaultUnixSpec(ctx context.Context, s *Spec, id string) error { "/proc/scsi", }, ReadonlyPaths: []string{ - "/proc/asound", "/proc/bus", "/proc/fs", "/proc/irq", diff --git a/vendor/github.com/containerd/containerd/oci/spec_opts.go b/vendor/github.com/containerd/containerd/oci/spec_opts.go index 50c77396d203..8b599f80586c 100644 --- a/vendor/github.com/containerd/containerd/oci/spec_opts.go +++ b/vendor/github.com/containerd/containerd/oci/spec_opts.go @@ -268,6 +268,14 @@ func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts { } } +// WithNewPrivileges turns off the NoNewPrivileges feature flag in the spec +func WithNewPrivileges(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + s.Process.NoNewPrivileges = false + + return nil +} + // WithImageConfig configures the spec to from the configuration of an Image func WithImageConfig(image Image) SpecOpts { return WithImageConfigArgs(image, nil) @@ -646,6 +654,10 @@ func WithUsername(username string) SpecOpts { // The passed in user can be either a uid or a username. func WithAdditionalGIDs(userstr string) SpecOpts { return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) { + // For LCOW additional GID's not supported + if s.Windows != nil { + return nil + } setProcess(s) setAdditionalGids := func(root string) error { var username string @@ -1003,3 +1015,14 @@ var WithPrivileged = Compose( WithApparmorProfile(""), WithSeccompUnconfined, ) + +// WithWindowsHyperV sets the Windows.HyperV section for HyperV isolation of containers. +func WithWindowsHyperV(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + if s.Windows == nil { + s.Windows = &specs.Windows{} + } + if s.Windows.HyperV == nil { + s.Windows.HyperV = &specs.WindowsHyperV{} + } + return nil +} diff --git a/vendor/github.com/containerd/containerd/platforms/cpuinfo.go b/vendor/github.com/containerd/containerd/platforms/cpuinfo.go index a5c5ab42b91f..bf6476b6419a 100644 --- a/vendor/github.com/containerd/containerd/platforms/cpuinfo.go +++ b/vendor/github.com/containerd/containerd/platforms/cpuinfo.go @@ -74,6 +74,22 @@ func getCPUInfo(pattern string) (info string, err error) { } func getCPUVariant() string { + if runtime.GOOS == "windows" { + // Windows only supports v7 for ARM32 and v8 for ARM64 and so we can use + // runtime.GOARCH to determine the variants + var variant string + switch runtime.GOARCH { + case "arm64": + variant = "v8" + case "arm": + variant = "v7" + default: + variant = "unknown" + } + + return variant + } + variant, err := getCPUInfo("Cpu architecture") if err != nil { log.L.WithError(err).Error("failure getting variant") diff --git a/vendor/github.com/containerd/containerd/remotes/docker/authorizer.go b/vendor/github.com/containerd/containerd/remotes/docker/authorizer.go new file mode 100644 index 000000000000..2d88c9f17303 --- /dev/null +++ b/vendor/github.com/containerd/containerd/remotes/docker/authorizer.go @@ -0,0 +1,313 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package docker + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + "sync" + "time" + + "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/log" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/net/context/ctxhttp" +) + +type dockerAuthorizer struct { + credentials func(string) (string, string, error) + + client *http.Client + mu sync.Mutex + + auth map[string]string +} + +// NewAuthorizer creates a Docker authorizer using the provided function to +// get credentials for the token server or basic auth. +func NewAuthorizer(client *http.Client, f func(string) (string, string, error)) Authorizer { + if client == nil { + client = http.DefaultClient + } + return &dockerAuthorizer{ + credentials: f, + client: client, + auth: map[string]string{}, + } +} + +func (a *dockerAuthorizer) Authorize(ctx context.Context, req *http.Request) error { + // TODO: Lookup matching challenge and scope rather than just host + if auth := a.getAuth(req.URL.Host); auth != "" { + req.Header.Set("Authorization", auth) + } + + return nil +} + +func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.Response) error { + last := responses[len(responses)-1] + host := last.Request.URL.Host + for _, c := range parseAuthHeader(last.Header) { + if c.scheme == bearerAuth { + if err := invalidAuthorization(c, responses); err != nil { + // TODO: Clear token + a.setAuth(host, "") + return err + } + + // TODO(dmcg): Store challenge, not token + // Move token fetching to authorize + return a.setTokenAuth(ctx, host, c.parameters) + } else if c.scheme == basicAuth { + // TODO: Resolve credentials on authorize + username, secret, err := a.credentials(host) + if err != nil { + return err + } + if username != "" && secret != "" { + auth := username + ":" + secret + a.setAuth(host, fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(auth)))) + return nil + } + } + } + + return errors.Wrap(errdefs.ErrNotImplemented, "failed to find supported auth scheme") +} + +func (a *dockerAuthorizer) getAuth(host string) string { + a.mu.Lock() + defer a.mu.Unlock() + + return a.auth[host] +} + +func (a *dockerAuthorizer) setAuth(host string, auth string) bool { + a.mu.Lock() + defer a.mu.Unlock() + + changed := a.auth[host] != auth + a.auth[host] = auth + + return changed +} + +func (a *dockerAuthorizer) setTokenAuth(ctx context.Context, host string, params map[string]string) error { + realm, ok := params["realm"] + if !ok { + return errors.New("no realm specified for token auth challenge") + } + + realmURL, err := url.Parse(realm) + if err != nil { + return errors.Wrap(err, "invalid token auth challenge realm") + } + + to := tokenOptions{ + realm: realmURL.String(), + service: params["service"], + } + + to.scopes = getTokenScopes(ctx, params) + if len(to.scopes) == 0 { + return errors.Errorf("no scope specified for token auth challenge") + } + + if a.credentials != nil { + to.username, to.secret, err = a.credentials(host) + if err != nil { + return err + } + } + + var token string + if to.secret != "" { + // Credential information is provided, use oauth POST endpoint + token, err = a.fetchTokenWithOAuth(ctx, to) + if err != nil { + return errors.Wrap(err, "failed to fetch oauth token") + } + } else { + // Do request anonymously + token, err = a.fetchToken(ctx, to) + if err != nil { + return errors.Wrap(err, "failed to fetch anonymous token") + } + } + a.setAuth(host, fmt.Sprintf("Bearer %s", token)) + + return nil +} + +type tokenOptions struct { + realm string + service string + scopes []string + username string + secret string +} + +type postTokenResponse struct { + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + ExpiresIn int `json:"expires_in"` + IssuedAt time.Time `json:"issued_at"` + Scope string `json:"scope"` +} + +func (a *dockerAuthorizer) fetchTokenWithOAuth(ctx context.Context, to tokenOptions) (string, error) { + form := url.Values{} + form.Set("scope", strings.Join(to.scopes, " ")) + form.Set("service", to.service) + // TODO: Allow setting client_id + form.Set("client_id", "containerd-client") + + if to.username == "" { + form.Set("grant_type", "refresh_token") + form.Set("refresh_token", to.secret) + } else { + form.Set("grant_type", "password") + form.Set("username", to.username) + form.Set("password", to.secret) + } + + resp, err := ctxhttp.PostForm(ctx, a.client, to.realm, form) + if err != nil { + return "", err + } + defer resp.Body.Close() + + // Registries without support for POST may return 404 for POST /v2/token. + // As of September 2017, GCR is known to return 404. + // As of February 2018, JFrog Artifactory is known to return 401. + if (resp.StatusCode == 405 && to.username != "") || resp.StatusCode == 404 || resp.StatusCode == 401 { + return a.fetchToken(ctx, to) + } else if resp.StatusCode < 200 || resp.StatusCode >= 400 { + b, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 64000)) // 64KB + log.G(ctx).WithFields(logrus.Fields{ + "status": resp.Status, + "body": string(b), + }).Debugf("token request failed") + // TODO: handle error body and write debug output + return "", errors.Errorf("unexpected status: %s", resp.Status) + } + + decoder := json.NewDecoder(resp.Body) + + var tr postTokenResponse + if err = decoder.Decode(&tr); err != nil { + return "", fmt.Errorf("unable to decode token response: %s", err) + } + + return tr.AccessToken, nil +} + +type getTokenResponse struct { + Token string `json:"token"` + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` + IssuedAt time.Time `json:"issued_at"` + RefreshToken string `json:"refresh_token"` +} + +// getToken fetches a token using a GET request +func (a *dockerAuthorizer) fetchToken(ctx context.Context, to tokenOptions) (string, error) { + req, err := http.NewRequest("GET", to.realm, nil) + if err != nil { + return "", err + } + + reqParams := req.URL.Query() + + if to.service != "" { + reqParams.Add("service", to.service) + } + + for _, scope := range to.scopes { + reqParams.Add("scope", scope) + } + + if to.secret != "" { + req.SetBasicAuth(to.username, to.secret) + } + + req.URL.RawQuery = reqParams.Encode() + + resp, err := ctxhttp.Do(ctx, a.client, req) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode >= 400 { + // TODO: handle error body and write debug output + return "", errors.Errorf("unexpected status: %s", resp.Status) + } + + decoder := json.NewDecoder(resp.Body) + + var tr getTokenResponse + if err = decoder.Decode(&tr); err != nil { + return "", fmt.Errorf("unable to decode token response: %s", err) + } + + // `access_token` is equivalent to `token` and if both are specified + // the choice is undefined. Canonicalize `access_token` by sticking + // things in `token`. + if tr.AccessToken != "" { + tr.Token = tr.AccessToken + } + + if tr.Token == "" { + return "", ErrNoToken + } + + return tr.Token, nil +} + +func invalidAuthorization(c challenge, responses []*http.Response) error { + errStr := c.parameters["error"] + if errStr == "" { + return nil + } + + n := len(responses) + if n == 1 || (n > 1 && !sameRequest(responses[n-2].Request, responses[n-1].Request)) { + return nil + } + + return errors.Wrapf(ErrInvalidAuthorization, "server message: %s", errStr) +} + +func sameRequest(r1, r2 *http.Request) bool { + if r1.Method != r2.Method { + return false + } + if *r1.URL != *r2.URL { + return false + } + return true +} diff --git a/vendor/github.com/containerd/containerd/remotes/docker/resolver.go b/vendor/github.com/containerd/containerd/remotes/docker/resolver.go index f0a677c83900..5cccdecba082 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/resolver.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/resolver.go @@ -18,18 +18,13 @@ package docker import ( "context" - "encoding/json" - "fmt" - "io" - "io/ioutil" "net/http" "net/url" "path" "strconv" "strings" - "sync" - "time" + "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/log" "github.com/containerd/containerd/reference" @@ -51,19 +46,37 @@ var ( ErrInvalidAuthorization = errors.New("authorization failed") ) -type dockerResolver struct { - credentials func(string) (string, string, error) - host func(string) (string, error) - plainHTTP bool - client *http.Client - tracker StatusTracker +// Authorizer is used to authorize HTTP requests based on 401 HTTP responses. +// An Authorizer is responsible for caching tokens or credentials used by +// requests. +type Authorizer interface { + // Authorize sets the appropriate `Authorization` header on the given + // request. + // + // If no authorization is found for the request, the request remains + // unmodified. It may also add an `Authorization` header as + // "bearer " + // "basic " + Authorize(context.Context, *http.Request) error + + // AddResponses adds a 401 response for the authorizer to consider when + // authorizing requests. The last response should be unauthorized and + // the previous requests are used to consider redirects and retries + // that may have led to the 401. + // + // If response is not handled, returns `ErrNotImplemented` + AddResponses(context.Context, []*http.Response) error } // ResolverOptions are used to configured a new Docker register resolver type ResolverOptions struct { + // Authorizer is used to authorize registry requests + Authorizer Authorizer + // Credentials provides username and secret given a host. // If username is empty but a secret is given, that secret // is interpretted as a long lived token. + // Deprecated: use Authorizer Credentials func(string) (string, string, error) // Host provides the hostname given a namespace. @@ -89,22 +102,31 @@ func DefaultHost(ns string) (string, error) { return ns, nil } +type dockerResolver struct { + auth Authorizer + host func(string) (string, error) + plainHTTP bool + client *http.Client + tracker StatusTracker +} + // NewResolver returns a new resolver to a Docker registry func NewResolver(options ResolverOptions) remotes.Resolver { - tracker := options.Tracker - if tracker == nil { - tracker = NewInMemoryTracker() + if options.Tracker == nil { + options.Tracker = NewInMemoryTracker() + } + if options.Host == nil { + options.Host = DefaultHost } - host := options.Host - if host == nil { - host = DefaultHost + if options.Authorizer == nil { + options.Authorizer = NewAuthorizer(options.Client, options.Credentials) } return &dockerResolver{ - credentials: options.Credentials, - host: host, - plainHTTP: options.PlainHTTP, - client: options.Client, - tracker: tracker, + auth: options.Authorizer, + host: options.Host, + plainHTTP: options.PlainHTTP, + client: options.Client, + tracker: options.Tracker, } } @@ -272,18 +294,14 @@ type dockerBase struct { refspec reference.Spec base url.URL - client *http.Client - useBasic bool - username, secret string - token string - mu sync.Mutex + client *http.Client + auth Authorizer } func (r *dockerResolver) base(refspec reference.Spec) (*dockerBase, error) { var ( - err error - base url.URL - username, secret string + err error + base url.URL ) host := refspec.Hostname() @@ -300,61 +318,40 @@ func (r *dockerResolver) base(refspec reference.Spec) (*dockerBase, error) { base.Scheme = "http" } - if r.credentials != nil { - username, secret, err = r.credentials(base.Host) - if err != nil { - return nil, err - } - } - prefix := strings.TrimPrefix(refspec.Locator, host+"/") base.Path = path.Join("/v2", prefix) return &dockerBase{ - refspec: refspec, - base: base, - client: r.client, - username: username, - secret: secret, + refspec: refspec, + base: base, + client: r.client, + auth: r.auth, }, nil } -func (r *dockerBase) getToken() string { - r.mu.Lock() - defer r.mu.Unlock() - - return r.token -} - -func (r *dockerBase) setToken(token string) bool { - r.mu.Lock() - defer r.mu.Unlock() - - changed := r.token != token - r.token = token - - return changed -} - func (r *dockerBase) url(ps ...string) string { url := r.base url.Path = path.Join(url.Path, path.Join(ps...)) return url.String() } -func (r *dockerBase) authorize(req *http.Request) { - token := r.getToken() - if r.useBasic { - req.SetBasicAuth(r.username, r.secret) - } else if token != "" { - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) +func (r *dockerBase) authorize(ctx context.Context, req *http.Request) error { + // Check if has header for host + if r.auth != nil { + if err := r.auth.Authorize(ctx, req); err != nil { + return err + } } + + return nil } func (r *dockerBase) doRequest(ctx context.Context, req *http.Request) (*http.Response, error) { ctx = log.WithLogger(ctx, log.G(ctx).WithField("url", req.URL.String())) log.G(ctx).WithField("request.headers", req.Header).WithField("request.method", req.Method).Debug("do request") - r.authorize(req) + if err := r.authorize(ctx, req); err != nil { + return nil, errors.Wrap(err, "failed to authorize") + } resp, err := ctxhttp.Do(ctx, r.client, req) if err != nil { return nil, errors.Wrap(err, "failed to do request") @@ -392,23 +389,14 @@ func (r *dockerBase) retryRequest(ctx context.Context, req *http.Request, respon last := responses[len(responses)-1] if last.StatusCode == http.StatusUnauthorized { log.G(ctx).WithField("header", last.Header.Get("WWW-Authenticate")).Debug("Unauthorized") - for _, c := range parseAuthHeader(last.Header) { - if c.scheme == bearerAuth { - if err := invalidAuthorization(c, responses); err != nil { - r.setToken("") - return nil, err - } - if err := r.setTokenAuth(ctx, c.parameters); err != nil { - return nil, err - } - return copyRequest(req) - } else if c.scheme == basicAuth { - if r.username != "" && r.secret != "" { - r.useBasic = true - } + if r.auth != nil { + if err := r.auth.AddResponses(ctx, responses); err == nil { return copyRequest(req) + } else if !errdefs.IsNotImplemented(err) { + return nil, err } } + return nil, nil } else if last.StatusCode == http.StatusMethodNotAllowed && req.Method == http.MethodHead { // Support registries which have not properly implemented the HEAD method for @@ -424,30 +412,6 @@ func (r *dockerBase) retryRequest(ctx context.Context, req *http.Request, respon return nil, nil } -func invalidAuthorization(c challenge, responses []*http.Response) error { - errStr := c.parameters["error"] - if errStr == "" { - return nil - } - - n := len(responses) - if n == 1 || (n > 1 && !sameRequest(responses[n-2].Request, responses[n-1].Request)) { - return nil - } - - return errors.Wrapf(ErrInvalidAuthorization, "server message: %s", errStr) -} - -func sameRequest(r1, r2 *http.Request) bool { - if r1.Method != r2.Method { - return false - } - if *r1.URL != *r2.URL { - return false - } - return true -} - func copyRequest(req *http.Request) (*http.Request, error) { ireq := *req if ireq.GetBody != nil { @@ -459,167 +423,3 @@ func copyRequest(req *http.Request) (*http.Request, error) { } return &ireq, nil } - -func (r *dockerBase) setTokenAuth(ctx context.Context, params map[string]string) error { - realm, ok := params["realm"] - if !ok { - return errors.New("no realm specified for token auth challenge") - } - - realmURL, err := url.Parse(realm) - if err != nil { - return fmt.Errorf("invalid token auth challenge realm: %s", err) - } - - to := tokenOptions{ - realm: realmURL.String(), - service: params["service"], - } - - to.scopes = getTokenScopes(ctx, params) - if len(to.scopes) == 0 { - return errors.Errorf("no scope specified for token auth challenge") - } - - var token string - if r.secret != "" { - // Credential information is provided, use oauth POST endpoint - token, err = r.fetchTokenWithOAuth(ctx, to) - if err != nil { - return errors.Wrap(err, "failed to fetch oauth token") - } - } else { - // Do request anonymously - token, err = r.fetchToken(ctx, to) - if err != nil { - return errors.Wrap(err, "failed to fetch anonymous token") - } - } - r.setToken(token) - - return nil -} - -type tokenOptions struct { - realm string - service string - scopes []string -} - -type postTokenResponse struct { - AccessToken string `json:"access_token"` - RefreshToken string `json:"refresh_token"` - ExpiresIn int `json:"expires_in"` - IssuedAt time.Time `json:"issued_at"` - Scope string `json:"scope"` -} - -func (r *dockerBase) fetchTokenWithOAuth(ctx context.Context, to tokenOptions) (string, error) { - form := url.Values{} - form.Set("scope", strings.Join(to.scopes, " ")) - form.Set("service", to.service) - // TODO: Allow setting client_id - form.Set("client_id", "containerd-dist-tool") - - if r.username == "" { - form.Set("grant_type", "refresh_token") - form.Set("refresh_token", r.secret) - } else { - form.Set("grant_type", "password") - form.Set("username", r.username) - form.Set("password", r.secret) - } - - resp, err := ctxhttp.PostForm(ctx, r.client, to.realm, form) - if err != nil { - return "", err - } - defer resp.Body.Close() - - // Registries without support for POST may return 404 for POST /v2/token. - // As of September 2017, GCR is known to return 404. - // As of February 2018, JFrog Artifactory is known to return 401. - if (resp.StatusCode == 405 && r.username != "") || resp.StatusCode == 404 || resp.StatusCode == 401 { - return r.fetchToken(ctx, to) - } else if resp.StatusCode < 200 || resp.StatusCode >= 400 { - b, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 64000)) // 64KB - log.G(ctx).WithFields(logrus.Fields{ - "status": resp.Status, - "body": string(b), - }).Debugf("token request failed") - // TODO: handle error body and write debug output - return "", errors.Errorf("unexpected status: %s", resp.Status) - } - - decoder := json.NewDecoder(resp.Body) - - var tr postTokenResponse - if err = decoder.Decode(&tr); err != nil { - return "", fmt.Errorf("unable to decode token response: %s", err) - } - - return tr.AccessToken, nil -} - -type getTokenResponse struct { - Token string `json:"token"` - AccessToken string `json:"access_token"` - ExpiresIn int `json:"expires_in"` - IssuedAt time.Time `json:"issued_at"` - RefreshToken string `json:"refresh_token"` -} - -// getToken fetches a token using a GET request -func (r *dockerBase) fetchToken(ctx context.Context, to tokenOptions) (string, error) { - req, err := http.NewRequest("GET", to.realm, nil) - if err != nil { - return "", err - } - - reqParams := req.URL.Query() - - if to.service != "" { - reqParams.Add("service", to.service) - } - - for _, scope := range to.scopes { - reqParams.Add("scope", scope) - } - - if r.secret != "" { - req.SetBasicAuth(r.username, r.secret) - } - - req.URL.RawQuery = reqParams.Encode() - - resp, err := ctxhttp.Do(ctx, r.client, req) - if err != nil { - return "", err - } - defer resp.Body.Close() - - if resp.StatusCode < 200 || resp.StatusCode >= 400 { - // TODO: handle error body and write debug output - return "", errors.Errorf("unexpected status: %s", resp.Status) - } - - decoder := json.NewDecoder(resp.Body) - - var tr getTokenResponse - if err = decoder.Decode(&tr); err != nil { - return "", fmt.Errorf("unable to decode token response: %s", err) - } - - // `access_token` is equivalent to `token` and if both are specified - // the choice is undefined. Canonicalize `access_token` by sticking - // things in `token`. - if tr.AccessToken != "" { - tr.Token = tr.AccessToken - } - - if tr.Token == "" { - return "", ErrNoToken - } - - return tr.Token, nil -} diff --git a/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go b/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go index 45ac1933fd9f..766c24a26d42 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go @@ -24,6 +24,7 @@ import ( "fmt" "io" "io/ioutil" + "strconv" "strings" "sync" "time" @@ -42,7 +43,10 @@ import ( "github.com/pkg/errors" ) -const manifestSizeLimit = 8e6 // 8MB +const ( + manifestSizeLimit = 8e6 // 8MB + labelDockerSchema1EmptyLayer = "containerd.io/docker.schema1.empty-layer" +) type blobState struct { diffID digest.Digest @@ -353,10 +357,11 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro Digest: desc.Digest, Labels: map[string]string{ "containerd.io/uncompressed": state.diffID.String(), + labelDockerSchema1EmptyLayer: strconv.FormatBool(state.empty), }, } - if _, err := c.contentStore.Update(ctx, cinfo, "labels.containerd.io/uncompressed"); err != nil { + if _, err := c.contentStore.Update(ctx, cinfo, "labels.containerd.io/uncompressed", fmt.Sprintf("labels.%s", labelDockerSchema1EmptyLayer)); err != nil { return errors.Wrap(err, "failed to update uncompressed label") } @@ -380,7 +385,18 @@ func (c *Converter) reuseLabelBlobState(ctx context.Context, desc ocispec.Descri return false, nil } - bState := blobState{empty: false} + emptyVal, ok := cinfo.Labels[labelDockerSchema1EmptyLayer] + if !ok { + return false, nil + } + + isEmpty, err := strconv.ParseBool(emptyVal) + if err != nil { + log.G(ctx).WithField("id", desc.Digest).Warnf("failed to parse bool from label %s: %v", labelDockerSchema1EmptyLayer, isEmpty) + return false, nil + } + + bState := blobState{empty: isEmpty} if bState.diffID, err = digest.Parse(diffID); err != nil { log.G(ctx).WithField("id", desc.Digest).Warnf("failed to parse digest from label containerd.io/uncompressed: %v", diffID) diff --git a/vendor/github.com/containerd/containerd/signal_map_linux.go b/vendor/github.com/containerd/containerd/signal_map_linux.go new file mode 100644 index 000000000000..554011074c09 --- /dev/null +++ b/vendor/github.com/containerd/containerd/signal_map_linux.go @@ -0,0 +1,60 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package containerd + +import ( + "syscall" + + "golang.org/x/sys/unix" +) + +var signalMap = map[string]syscall.Signal{ + "ABRT": unix.SIGABRT, + "ALRM": unix.SIGALRM, + "BUS": unix.SIGBUS, + "CHLD": unix.SIGCHLD, + "CLD": unix.SIGCLD, + "CONT": unix.SIGCONT, + "FPE": unix.SIGFPE, + "HUP": unix.SIGHUP, + "ILL": unix.SIGILL, + "INT": unix.SIGINT, + "IO": unix.SIGIO, + "IOT": unix.SIGIOT, + "KILL": unix.SIGKILL, + "PIPE": unix.SIGPIPE, + "POLL": unix.SIGPOLL, + "PROF": unix.SIGPROF, + "PWR": unix.SIGPWR, + "QUIT": unix.SIGQUIT, + "SEGV": unix.SIGSEGV, + "STKFLT": unix.SIGSTKFLT, + "STOP": unix.SIGSTOP, + "SYS": unix.SIGSYS, + "TERM": unix.SIGTERM, + "TRAP": unix.SIGTRAP, + "TSTP": unix.SIGTSTP, + "TTIN": unix.SIGTTIN, + "TTOU": unix.SIGTTOU, + "URG": unix.SIGURG, + "USR1": unix.SIGUSR1, + "USR2": unix.SIGUSR2, + "VTALRM": unix.SIGVTALRM, + "WINCH": unix.SIGWINCH, + "XCPU": unix.SIGXCPU, + "XFSZ": unix.SIGXFSZ, +} diff --git a/vendor/github.com/containerd/containerd/signal_map_unix.go b/vendor/github.com/containerd/containerd/signal_map_unix.go new file mode 100644 index 000000000000..62ccba9ace33 --- /dev/null +++ b/vendor/github.com/containerd/containerd/signal_map_unix.go @@ -0,0 +1,58 @@ +// +build darwin freebsd solaris + +/* + Copyright The containerd Authors. + + 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. +*/ + +package containerd + +import ( + "syscall" + + "golang.org/x/sys/unix" +) + +var signalMap = map[string]syscall.Signal{ + "ABRT": unix.SIGABRT, + "ALRM": unix.SIGALRM, + "BUS": unix.SIGBUS, + "CHLD": unix.SIGCHLD, + "CONT": unix.SIGCONT, + "FPE": unix.SIGFPE, + "HUP": unix.SIGHUP, + "ILL": unix.SIGILL, + "INT": unix.SIGINT, + "IO": unix.SIGIO, + "IOT": unix.SIGIOT, + "KILL": unix.SIGKILL, + "PIPE": unix.SIGPIPE, + "PROF": unix.SIGPROF, + "QUIT": unix.SIGQUIT, + "SEGV": unix.SIGSEGV, + "STOP": unix.SIGSTOP, + "SYS": unix.SIGSYS, + "TERM": unix.SIGTERM, + "TRAP": unix.SIGTRAP, + "TSTP": unix.SIGTSTP, + "TTIN": unix.SIGTTIN, + "TTOU": unix.SIGTTOU, + "URG": unix.SIGURG, + "USR1": unix.SIGUSR1, + "USR2": unix.SIGUSR2, + "VTALRM": unix.SIGVTALRM, + "WINCH": unix.SIGWINCH, + "XCPU": unix.SIGXCPU, + "XFSZ": unix.SIGXFSZ, +} diff --git a/vendor/github.com/containerd/containerd/signal_map_windows.go b/vendor/github.com/containerd/containerd/signal_map_windows.go new file mode 100644 index 000000000000..ef17a8fdb169 --- /dev/null +++ b/vendor/github.com/containerd/containerd/signal_map_windows.go @@ -0,0 +1,39 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package containerd + +import ( + "syscall" + + "golang.org/x/sys/windows" +) + +var signalMap = map[string]syscall.Signal{ + "HUP": syscall.Signal(windows.SIGHUP), + "INT": syscall.Signal(windows.SIGINT), + "QUIT": syscall.Signal(windows.SIGQUIT), + "SIGILL": syscall.Signal(windows.SIGILL), + "TRAP": syscall.Signal(windows.SIGTRAP), + "ABRT": syscall.Signal(windows.SIGABRT), + "BUS": syscall.Signal(windows.SIGBUS), + "FPE": syscall.Signal(windows.SIGFPE), + "KILL": syscall.Signal(windows.SIGKILL), + "SEGV": syscall.Signal(windows.SIGSEGV), + "PIPE": syscall.Signal(windows.SIGPIPE), + "ALRM": syscall.Signal(windows.SIGALRM), + "TERM": syscall.Signal(windows.SIGTERM), +} diff --git a/vendor/github.com/containerd/containerd/signals.go b/vendor/github.com/containerd/containerd/signals.go new file mode 100644 index 000000000000..32c34309de11 --- /dev/null +++ b/vendor/github.com/containerd/containerd/signals.go @@ -0,0 +1,105 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package containerd + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "strings" + "syscall" + + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/images" + "github.com/opencontainers/image-spec/specs-go/v1" +) + +// StopSignalLabel is a well-known containerd label for storing the stop +// signal specified in the OCI image config +const StopSignalLabel = "io.containerd.image.config.stop-signal" + +// GetStopSignal retrieves the container stop signal, specified by the +// well-known containerd label (StopSignalLabel) +func GetStopSignal(ctx context.Context, container Container, defaultSignal syscall.Signal) (syscall.Signal, error) { + labels, err := container.Labels(ctx) + if err != nil { + return -1, err + } + + if stopSignal, ok := labels[StopSignalLabel]; ok { + return ParseSignal(stopSignal) + } + + return defaultSignal, nil +} + +// GetOCIStopSignal retrieves the stop signal specified in the OCI image config +func GetOCIStopSignal(ctx context.Context, image Image, defaultSignal string) (string, error) { + _, err := ParseSignal(defaultSignal) + if err != nil { + return "", err + } + ic, err := image.Config(ctx) + if err != nil { + return "", err + } + var ( + ociimage v1.Image + config v1.ImageConfig + ) + switch ic.MediaType { + case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config: + p, err := content.ReadBlob(ctx, image.ContentStore(), ic) + if err != nil { + return "", err + } + + if err := json.Unmarshal(p, &ociimage); err != nil { + return "", err + } + config = ociimage.Config + default: + return "", fmt.Errorf("unknown image config media type %s", ic.MediaType) + } + + if config.StopSignal == "" { + return defaultSignal, nil + } + + return config.StopSignal, nil +} + +// ParseSignal parses a given string into a syscall.Signal +// it checks that the signal exists in the platform-appropriate signalMap +func ParseSignal(rawSignal string) (syscall.Signal, error) { + s, err := strconv.Atoi(rawSignal) + if err == nil { + sig := syscall.Signal(s) + for _, msig := range signalMap { + if sig == msig { + return sig, nil + } + } + return -1, fmt.Errorf("unknown signal %q", rawSignal) + } + signal, ok := signalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")] + if !ok { + return -1, fmt.Errorf("unknown signal %q", rawSignal) + } + return signal, nil +} diff --git a/vendor/github.com/containerd/containerd/vendor.conf b/vendor/github.com/containerd/containerd/vendor.conf index 1bcd620ea3c3..c439d85cd903 100644 --- a/vendor/github.com/containerd/containerd/vendor.conf +++ b/vendor/github.com/containerd/containerd/vendor.conf @@ -4,7 +4,7 @@ github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2 github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c github.com/containerd/btrfs 2e1aa0ddf94f91fa282b6ed87c23bf0d64911244 -github.com/containerd/continuity f44b615e492bdfb371aae2f76ec694d9da1db537 +github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4 github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6 github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 @@ -20,7 +20,7 @@ github.com/gogo/protobuf v1.0.0 github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef github.com/golang/protobuf v1.1.0 github.com/opencontainers/runtime-spec eba862dc2470385a233c7507392675cbeadf7353 # v1.0.1-45-geba862d -github.com/opencontainers/runc 20aff4f0488c6d4b8df4d85b4f63f1f704c11abd +github.com/opencontainers/runc 96ec2177ae841256168fcf76954f7177af9446eb github.com/sirupsen/logrus v1.0.0 github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac @@ -32,19 +32,19 @@ github.com/opencontainers/image-spec v1.0.1 golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 -github.com/Microsoft/go-winio v0.4.10 -github.com/Microsoft/hcsshim 44c060121b68e8bdc40b411beba551f3b4ee9e55 +github.com/Microsoft/go-winio v0.4.11 +github.com/Microsoft/hcsshim v0.8.1 google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 -github.com/containerd/ttrpc 94dde388801693c54f88a6596f713b51a8b30b2d +github.com/containerd/ttrpc 2a805f71863501300ae1976d29f0454ae003e85a github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 gotest.tools v2.1.0 github.com/google/go-cmp v0.1.0 go.etcd.io/bbolt v1.3.1-etcd.8 # cri dependencies -github.com/containerd/cri 9f39e3289533fc228c5e5fcac0a6dbdd60c6047b # release/1.2 branch -github.com/containerd/go-cni 6d7b509a054a3cb1c35ed1865d4fde2f0cb547cd +github.com/containerd/cri 0ca1e3c2b73b5c38e72f29bb76338d0078b23d6c # release/1.2 branch +github.com/containerd/go-cni 40bcf8ec8acd7372be1d77031d585d5d8e561c90 github.com/blang/semver v3.1.0 github.com/containernetworking/cni v0.6.0 github.com/containernetworking/plugins v0.7.0 @@ -73,12 +73,12 @@ golang.org/x/oauth2 a6bd8cefa1811bd24b86f8902872e4e8225f74c4 golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 gopkg.in/yaml.v2 v2.2.1 -k8s.io/api 012f271b5d41baad56190c5f1ae19bff16df0fd8 -k8s.io/apimachinery 6429050ef506887d121f3e7306e894f8900d8a63 -k8s.io/apiserver e9312c15296b6c2c923ebd5031ff5d1d5fd022d7 -k8s.io/client-go 37c3c02ec96533daec0dbda1f39a6b1d68505c79 -k8s.io/kubernetes v1.12.0-beta.1 -k8s.io/utils 982821ea41da7e7c15f3d3738921eb2e7e241ccd +k8s.io/api kubernetes-1.12.0 +k8s.io/apimachinery kubernetes-1.12.0 +k8s.io/apiserver kubernetes-1.12.0 +k8s.io/client-go kubernetes-1.12.0 +k8s.io/kubernetes v1.12.0 +k8s.io/utils cd34563cd63c2bd7c6fe88a73c4dcf34ed8a67cb # zfs dependencies github.com/containerd/zfs 9a0b8b8b5982014b729cd34eb7cd7a11062aa6ec diff --git a/vendor/github.com/containerd/cri/LICENSE b/vendor/github.com/containerd/cri/LICENSE new file mode 100644 index 000000000000..8dada3edaf50 --- /dev/null +++ b/vendor/github.com/containerd/cri/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/containerd/cri/README.md b/vendor/github.com/containerd/cri/README.md new file mode 100644 index 000000000000..a97c2fe3da60 --- /dev/null +++ b/vendor/github.com/containerd/cri/README.md @@ -0,0 +1,176 @@ +# cri +

+ + +

+ +*Note: The standalone `cri-containerd` binary is end-of-life. `cri-containerd` is +transitioning from a standalone binary that talks to containerd to a plugin within +containerd. This github branch is for the `cri` plugin. See +[standalone-cri-containerd branch](https://github.com/containerd/cri/tree/standalone-cri-containerd) +for information about the standalone version of `cri-containerd`.* + +*Note: You need to [drain your node](https://kubernetes.io/docs/tasks/administer-cluster/safely-drain-node/) before upgrading from standalone `cri-containerd` to containerd with `cri` plugin.* + +[![Build Status](https://api.travis-ci.org/containerd/cri.svg?style=flat-square)](https://travis-ci.org/containerd/cri) +[![Go Report Card](https://goreportcard.com/badge/github.com/containerd/cri)](https://goreportcard.com/report/github.com/containerd/cri) + +`cri` is a [containerd](https://containerd.io/) plugin implementation of Kubernetes [container runtime interface (CRI)](https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/apis/cri/runtime/v1alpha2/api.proto). + +With it, you could run Kubernetes using containerd as the container runtime. +![cri](./docs/cri.png) +## Current Status +`cri` is a native plugin of containerd 1.1 and above. It is built into containerd and enabled by default. + +`cri` is in GA: +* It is feature complete. +* It (the GA version) works with Kubernetes 1.10 and above. +* It has passed all [CRI validation tests](https://github.com/kubernetes/community/blob/master/contributors/devel/cri-validation.md). +* It has passed all [node e2e tests](https://github.com/kubernetes/community/blob/master/contributors/devel/e2e-node-tests.md). +* It has passed all [e2e tests](https://github.com/kubernetes/community/blob/master/contributors/devel/e2e-tests.md). + +See [test dashboard](https://k8s-testgrid.appspot.com/sig-node-containerd) +## Support Metrics +| CRI-Containerd Version | Containerd Version | Kubernetes Version | CRI Version | +|:----------------------:|:------------------:|:------------------:|:-----------:| +| v1.0.0-alpha.x | | 1.7, 1.8 | v1alpha1 | +| v1.0.0-beta.x | | 1.9 | v1alpha1 | +| End-Of-Life | v1.1 | 1.10+ | v1alpha2 | +| | HEAD | 1.10+ | v1alpha2 | + +## Production Quality Cluster on GCE +For a production quality cluster on GCE brought up with `kube-up.sh` refer [here](docs/kube-up.md). +## Installing with Ansible and Kubeadm +For a multi node cluster installer and bring up steps using ansible and kubeadm refer [here](contrib/ansible/README.md). +## Custom Installation +For non ansible users, you can download the `cri-containerd` release tarball and deploy +kubernetes cluster using kubeadm as described [here](docs/installation.md). +## Getting Started for Developers +### Binary Dependencies and Specifications +The current release of the `cri` plugin has the following dependencies: +* [containerd](https://github.com/containerd/containerd) +* [runc](https://github.com/opencontainers/runc) +* [CNI](https://github.com/containernetworking/cni) + +See [versions](./vendor.conf) of these dependencies `cri` is tested with. + +As containerd and runc move to their respective general availability releases, +we will do our best to rebase/retest `cri` with these releases on a +weekly/monthly basis. Similarly, given that `cri` uses the Open +Container Initiative (OCI) [image](https://github.com/opencontainers/image-spec) +and [runtime](https://github.com/opencontainers/runtime-spec) specifications, we +will also do our best to update `cri` to the latest releases of these +specifications as appropriate. +### Install Dependencies +1. Install development libraries: +* **libseccomp development library.** Required by `cri` and runc seccomp support. `libseccomp-dev` (Ubuntu, Debian) / `libseccomp-devel` +(Fedora, CentOS, RHEL). On releases of Ubuntu <=Trusty and Debian <=jessie a +backport version of `libseccomp-dev` is required. See [travis.yml](.travis.yml) for an example on trusty. +* **btrfs development library.** Required by containerd btrfs support. `btrfs-tools`(Ubuntu, Debian) / `btrfs-progs-devel`(Fedora, CentOS, RHEL) +2. Install **`socat`** (required by portforward). +2. Install and setup a go 1.10 development environment. +3. Make a local clone of this repository. +4. Install binary dependencies by running the following command from your cloned `cri/` project directory: +```bash +# Note: install.deps installs the above mentioned runc, containerd, and CNI +# binary dependencies. install.deps is only provided for general use and ease of +# testing. To customize `runc` and `containerd` build tags and/or to configure +# `cni`, please follow instructions in their documents. +make install.deps +``` +### Build and Install `cri` +To build and install a version of containerd with the `cri` plugin, enter the +following commands from your `cri` project directory: +```bash +make +sudo make install +``` +*NOTE: The version of containerd built and installed from the `Makefile` is only for +testing purposes. The version tag carries the suffix "-TEST".* +#### Build Tags +`cri` supports optional build tags for compiling support of various features. +To add build tags to the make option the `BUILD_TAGS` variable must be set. + +```bash +make BUILD_TAGS='seccomp apparmor' +``` + +| Build Tag | Feature | Dependency | +|-----------|------------------------------------|---------------------------------| +| seccomp | syscall filtering | libseccomp development library | +| selinux | selinux process and mount labeling | | +| apparmor | apparmor profile support | | +### Validate Your `cri` Setup +A Kubernetes incubator project called [cri-tools](https://github.com/kubernetes-sigs/cri-tools) +includes programs for exercising CRI implementations such as the `cri` plugin. +More importantly, cri-tools includes the program `critest` which is used for running +[CRI Validation Testing](https://github.com/kubernetes/community/blob/master/contributors/devel/cri-validation.md). + +Run the CRI Validation test to validate your installation of `containerd` with `cri` built in: +```bash +make test-cri +``` +### Running a Kubernetes local cluster +If you already have a working development environment for supported Kubernetes +version, you can try `cri` in a local cluster: + +1. Start the version of `containerd` with `cri` plugin that you built and installed +above as root in a first terminal: +```bash +sudo containerd +``` +2. From the Kubernetes project directory startup a local cluster using `containerd`: +```bash +CONTAINER_RUNTIME=remote CONTAINER_RUNTIME_ENDPOINT='unix:///run/containerd/containerd.sock' ./hack/local-up-cluster.sh +``` +### Test +See [here](./docs/testing.md) for information about test. +## Using crictl +See [here](./docs/crictl.md) for information about using `crictl` to debug +pods, containers, and images. +## Configurations +See [here](./docs/config.md) for information about how to configure cri plugins +and [here](https://github.com/containerd/containerd/blob/master/docs/man/containerd-config.1.md) +for information about how to configure containerd +## Documentation +See [here](./docs) for additional documentation. +## Contributing +Interested in contributing? Check out the [documentation](./CONTRIBUTING.md). + +## Communication +This project was originally established in April of 2017 in the Kubernetes +Incubator program. After reaching the Beta stage, In January of 2018, the +project was merged into [containerd](https://github.com/containerd/containerd). + +For async communication and long running discussions please use issues and pull +requests on this github repo. This will be the best place to discuss design and +implementation. + +For sync communication we have a community slack with a #containerd channel that +everyone is welcome to join and chat about development. + +**Slack:** https://dockr.ly/community + +## Other Communications +As this project is tightly coupled to CRI and CRI-Tools and they are Kubernetes +projects, some of our project communications take place in the Kubernetes' SIG: +`sig-node.` + +For more information about `sig-node`, `CRI`, and the `CRI-Tools` projects: +* [sig-node community site](https://github.com/kubernetes/community/tree/master/sig-node) +* Slack: `#sig-node` channel in Kubernetes (kubernetes.slack.com) +* Mailing List: https://groups.google.com/forum/#!forum/kubernetes-sig-node + +### Reporting Security Issues + +__If you are reporting a security issue, please reach out discreetly at security@containerd.io__. + +## Licenses +The containerd codebase is released under the [Apache 2.0 license](https://github.com/containerd/containerd/blob/master/LICENSE.code). +The README.md file, and files in the "docs" folder are licensed under the +Creative Commons Attribution 4.0 International License under the terms and +conditions set forth in the file "[LICENSE.docs](https://github.com/containerd/containerd/blob/master/LICENSE.docs)". You may obtain a duplicate +copy of the same license, titled CC-BY-4.0, at http://creativecommons.org/licenses/by/4.0/. + +## Code of Conduct +This project follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). diff --git a/vendor/github.com/containerd/cri/pkg/util/deep_copy.go b/vendor/github.com/containerd/cri/pkg/util/deep_copy.go new file mode 100644 index 000000000000..5fdee984b528 --- /dev/null +++ b/vendor/github.com/containerd/cri/pkg/util/deep_copy.go @@ -0,0 +1,42 @@ +/* +Copyright 2017 The Kubernetes Authors. + +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. +*/ + +package util + +import ( + "encoding/json" + + "github.com/pkg/errors" +) + +// DeepCopy makes a deep copy from src into dst. +func DeepCopy(dst interface{}, src interface{}) error { + if dst == nil { + return errors.New("dst cannot be nil") + } + if src == nil { + return errors.New("src cannot be nil") + } + bytes, err := json.Marshal(src) + if err != nil { + return errors.Wrap(err, "unable to marshal src") + } + err = json.Unmarshal(bytes, dst) + if err != nil { + return errors.Wrap(err, "unable to unmarshal into dst") + } + return nil +} diff --git a/vendor/github.com/containerd/cri/pkg/util/id.go b/vendor/github.com/containerd/cri/pkg/util/id.go new file mode 100644 index 000000000000..11b0a70a65c2 --- /dev/null +++ b/vendor/github.com/containerd/cri/pkg/util/id.go @@ -0,0 +1,29 @@ +/* +Copyright 2017 The Kubernetes Authors. + +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. +*/ + +package util + +import ( + "encoding/hex" + "math/rand" +) + +// GenerateID generates a random unique id. +func GenerateID() string { + b := make([]byte, 32) + rand.Read(b) + return hex.EncodeToString(b) +} diff --git a/vendor/github.com/containerd/cri/pkg/util/image.go b/vendor/github.com/containerd/cri/pkg/util/image.go new file mode 100644 index 000000000000..0f471fc42243 --- /dev/null +++ b/vendor/github.com/containerd/cri/pkg/util/image.go @@ -0,0 +1,50 @@ +/* +Copyright 2017 The Kubernetes Authors. + +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. +*/ + +package util + +import ( + "github.com/docker/distribution/reference" +) + +// NormalizeImageRef normalizes the image reference following the docker convention. This is added +// mainly for backward compatibility. +// The reference returned can only be either tagged or digested. For reference contains both tag +// and digest, the function returns digested reference, e.g. docker.io/library/busybox:latest@ +// sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa will be returned as +// docker.io/library/busybox@sha256:7cc4b5aefd1d0cadf8d97d4350462ba51c694ebca145b08d7d41b41acc8db5aa. +func NormalizeImageRef(ref string) (reference.Named, error) { + named, err := reference.ParseNormalizedNamed(ref) + if err != nil { + return nil, err + } + if _, ok := named.(reference.NamedTagged); ok { + if canonical, ok := named.(reference.Canonical); ok { + // The reference is both tagged and digested, only + // return digested. + newNamed, err := reference.WithName(canonical.Name()) + if err != nil { + return nil, err + } + newCanonical, err := reference.WithDigest(newNamed, canonical.Digest()) + if err != nil { + return nil, err + } + return newCanonical, nil + } + } + return reference.TagNameOnly(named), nil +} diff --git a/vendor/github.com/containerd/cri/pkg/util/strings.go b/vendor/github.com/containerd/cri/pkg/util/strings.go new file mode 100644 index 000000000000..d5cbc2e8ef30 --- /dev/null +++ b/vendor/github.com/containerd/cri/pkg/util/strings.go @@ -0,0 +1,59 @@ +/* +Copyright 2017 The Kubernetes Authors. + +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. +*/ + +package util + +import "strings" + +// InStringSlice checks whether a string is inside a string slice. +// Comparison is case insensitive. +func InStringSlice(ss []string, str string) bool { + for _, s := range ss { + if strings.ToLower(s) == strings.ToLower(str) { + return true + } + } + return false +} + +// SubtractStringSlice subtracts string from string slice. +// Comparison is case insensitive. +func SubtractStringSlice(ss []string, str string) []string { + var res []string + for _, s := range ss { + if strings.ToLower(s) == strings.ToLower(str) { + continue + } + res = append(res, s) + } + return res +} + +// MergeStringSlices merges 2 string slices into one and remove duplicated elements. +func MergeStringSlices(a []string, b []string) []string { + set := map[string]struct{}{} + for _, s := range a { + set[s] = struct{}{} + } + for _, s := range b { + set[s] = struct{}{} + } + var ss []string + for s := range set { + ss = append(ss, s) + } + return ss +} diff --git a/vendor/github.com/containerd/cri/vendor.conf b/vendor/github.com/containerd/cri/vendor.conf new file mode 100644 index 000000000000..208a079b6ab4 --- /dev/null +++ b/vendor/github.com/containerd/cri/vendor.conf @@ -0,0 +1,78 @@ +github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 +github.com/blang/semver v3.1.0 +github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 +github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2 +github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 +github.com/containerd/containerd 4b284fa3ab61832b022ba428055f793a75ffc251 +github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4 +github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c +github.com/containerd/go-cni 40bcf8ec8acd7372be1d77031d585d5d8e561c90 +github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3 +github.com/containerd/ttrpc 2a805f71863501300ae1976d29f0454ae003e85a +github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 +github.com/containernetworking/cni v0.6.0 +github.com/containernetworking/plugins v0.7.0 +github.com/coreos/go-systemd v14 +github.com/davecgh/go-spew v1.1.0 +github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621 +github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00 +github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 +github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098 +github.com/docker/go-units v0.3.1 +github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528 +github.com/emicklei/go-restful v2.2.1 +github.com/ghodss/yaml v1.0.0 +github.com/godbus/dbus v3 +github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef +github.com/gogo/protobuf v1.0.0 +github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed +github.com/golang/protobuf v1.1.0 +github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c +github.com/grpc-ecosystem/go-grpc-prometheus v1.1 +github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55 +github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f +github.com/json-iterator/go 1.1.5 +github.com/matttproud/golang_protobuf_extensions v1.0.0 +github.com/Microsoft/go-winio v0.4.11 +github.com/Microsoft/hcsshim v0.8.1 +github.com/modern-go/concurrent 1.0.3 +github.com/modern-go/reflect2 1.0.1 +github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7 +github.com/opencontainers/image-spec v1.0.1 +github.com/opencontainers/runc v1.0.0-rc6 +github.com/opencontainers/runtime-spec eba862dc2470385a233c7507392675cbeadf7353 +github.com/opencontainers/runtime-tools v0.6.0 +github.com/opencontainers/selinux b6fa367ed7f534f9ba25391cc2d467085dbb445a +github.com/pkg/errors v0.8.0 +github.com/pmezard/go-difflib v1.0.0 +github.com/prometheus/client_golang f4fb1b73fb099f396a7f0036bf86aa8def4ed823 +github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c +github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563 +github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd +github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 +github.com/sirupsen/logrus v1.0.0 +github.com/stretchr/testify v1.1.4 +github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 +github.com/tchap/go-patricia v2.2.6 +github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c +github.com/xeipuuv/gojsonpointer 4e3ac2762d5f479393488629ee9370b50873b3a6 +github.com/xeipuuv/gojsonreference bd5ef7bd5415a7ac448318e64f11a24cd21e594b +github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874 +go.etcd.io/bbolt v1.3.1-etcd.8 +golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067 +golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac +golang.org/x/oauth2 a6bd8cefa1811bd24b86f8902872e4e8225f74c4 +golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c +golang.org/x/sys 1b2967e3c290b7c545b3db0deeda16e9be4f98a2 https://github.com/golang/sys +golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 +golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 +google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 +google.golang.org/grpc v1.12.0 +gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 +gopkg.in/yaml.v2 53feefa2559fb8dfa8d81baad31be332c97d6c77 +k8s.io/api kubernetes-1.12.0 +k8s.io/apimachinery kubernetes-1.12.0 +k8s.io/apiserver kubernetes-1.12.0 +k8s.io/client-go kubernetes-1.12.0 +k8s.io/kubernetes v1.12.0 +k8s.io/utils cd34563cd63c2bd7c6fe88a73c4dcf34ed8a67cb diff --git a/vendor/github.com/containerd/typeurl/types.go b/vendor/github.com/containerd/typeurl/types.go index 10a78228ba35..153c488d0aa6 100644 --- a/vendor/github.com/containerd/typeurl/types.go +++ b/vendor/github.com/containerd/typeurl/types.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package typeurl import ( diff --git a/vendor/github.com/opencontainers/runc/README.md b/vendor/github.com/opencontainers/runc/README.md index 5215e32c1fe2..e755fb7bcdda 100644 --- a/vendor/github.com/opencontainers/runc/README.md +++ b/vendor/github.com/opencontainers/runc/README.md @@ -68,6 +68,7 @@ make BUILDTAGS='seccomp apparmor' | selinux | selinux process and mount labeling | | | apparmor | apparmor profile support | | | ambient | ambient capability support | kernel 4.3 | +| nokmem | disable kernel memory account | | ### Running the test suite @@ -87,6 +88,18 @@ You can run a specific test case by setting the `TESTFLAGS` variable. # make test TESTFLAGS="-run=SomeTestFunction" ``` +You can run a specific integration test by setting the `TESTPATH` variable. + +```bash +# make test TESTPATH="/checkpoint.bats" +``` + +You can run a test in your proxy environment by setting `DOCKER_BUILD_PROXY` and `DOCKER_RUN_PROXY` variables. + +```bash +# make test DOCKER_BUILD_PROXY="--build-arg HTTP_PROXY=http://yourproxy/" DOCKER_RUN_PROXY="-e HTTP_PROXY=http://yourproxy/" +``` + ### Dependencies Management `runc` uses [vndr](https://github.com/LK4D4/vndr) for dependencies management. @@ -251,3 +264,7 @@ PIDFile=/run/mycontainerid.pid [Install] WantedBy=multi-user.target ``` + +## License + +The code and docs are released under the [Apache 2.0 license](LICENSE). diff --git a/vendor/github.com/opencontainers/runc/libcontainer/README.md b/vendor/github.com/opencontainers/runc/libcontainer/README.md index 42f3efe56399..1d7fa04c0827 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/README.md +++ b/vendor/github.com/opencontainers/runc/libcontainer/README.md @@ -148,6 +148,7 @@ config := &configs.Config{ {Type: configs.NEWPID}, {Type: configs.NEWUSER}, {Type: configs.NEWNET}, + {Type: configs.NEWCGROUP}, }), Cgroups: &configs.Cgroup{ Name: "test-container", @@ -323,6 +324,7 @@ generated when building libcontainer with docker. ## Copyright and license -Code and documentation copyright 2014 Docker, inc. Code released under the Apache 2.0 license. -Docs released under Creative commons. - +Code and documentation copyright 2014 Docker, inc. +The code and documentation are released under the [Apache 2.0 license](../LICENSE). +The documentation is also released under Creative Commons Attribution 4.0 International License. +You may obtain a copy of the license, titled CC-BY-4.0, at http://creativecommons.org/licenses/by/4.0/. diff --git a/vendor/github.com/opencontainers/runc/libcontainer/nsenter/README.md b/vendor/github.com/opencontainers/runc/libcontainer/nsenter/README.md index 575701371238..9ec6c39316b9 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/nsenter/README.md +++ b/vendor/github.com/opencontainers/runc/libcontainer/nsenter/README.md @@ -10,8 +10,8 @@ The `nsenter` package will `import "C"` and it uses [cgo](https://golang.org/cmd package. In cgo, if the import of "C" is immediately preceded by a comment, that comment, called the preamble, is used as a header when compiling the C parts of the package. So every time we import package `nsenter`, the C code function `nsexec()` would be -called. And package `nsenter` is now only imported in `main_unix.go`, so every time -before we call `cmd.Start` on linux, that C code would run. +called. And package `nsenter` is only imported in `init.go`, so every time the runc +`init` command is invoked, that C code is run. Because `nsexec()` must be run before the Go runtime in order to use the Linux kernel namespace, you must `import` this library into a package if @@ -37,7 +37,7 @@ the parent `nsexec()` will exit and the child `nsexec()` process will return to allow the Go runtime take over. NOTE: We do both `setns(2)` and `clone(2)` even if we don't have any -CLONE_NEW* clone flags because we must fork a new process in order to +`CLONE_NEW*` clone flags because we must fork a new process in order to enter the PID namespace. diff --git a/vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c b/vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c index a4cd1399d9eb..28269dfc027f 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c +++ b/vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c @@ -42,6 +42,12 @@ enum sync_t { SYNC_ERR = 0xFF, /* Fatal error, no turning back. The error code follows. */ }; +/* + * Synchronisation value for cgroup namespace setup. + * The same constant is defined in process_linux.go as "createCgroupns". + */ +#define CREATECGROUPNS 0x80 + /* longjmp() arguments. */ #define JUMP_PARENT 0x00 #define JUMP_CHILD 0xA0 @@ -82,7 +88,7 @@ struct nlconfig_t { uint8_t is_setgroup; /* Rootless container settings. */ - uint8_t is_rootless; + uint8_t is_rootless_euid; /* boolean */ char *uidmappath; size_t uidmappath_len; char *gidmappath; @@ -100,7 +106,7 @@ struct nlconfig_t { #define GIDMAP_ATTR 27284 #define SETGROUP_ATTR 27285 #define OOM_SCORE_ADJ_ATTR 27286 -#define ROOTLESS_ATTR 27287 +#define ROOTLESS_EUID_ATTR 27287 #define UIDMAPPATH_ATTR 27288 #define GIDMAPPATH_ATTR 27289 @@ -211,7 +217,7 @@ static int try_mapping_tool(const char *app, int pid, char *map, size_t map_len) /* * If @app is NULL, execve will segfault. Just check it here and bail (if - * we're in this path, the caller is already getting desparate and there + * we're in this path, the caller is already getting desperate and there * isn't a backup to this failing). This usually would be a configuration * or programming issue. */ @@ -419,8 +425,8 @@ static void nl_parse(int fd, struct nlconfig_t *config) case CLONE_FLAGS_ATTR: config->cloneflags = readint32(current); break; - case ROOTLESS_ATTR: - config->is_rootless = readint8(current); + case ROOTLESS_EUID_ATTR: + config->is_rootless_euid = readint8(current); /* boolean */ break; case OOM_SCORE_ADJ_ATTR: config->oom_score_adj = current; @@ -640,7 +646,6 @@ void nsexec(void) case JUMP_PARENT:{ int len; pid_t child, first_child = -1; - char buf[JSON_MAX]; bool ready = false; /* For debugging. */ @@ -687,7 +692,7 @@ void nsexec(void) * newuidmap/newgidmap shall be used. */ - if (config.is_rootless && !config.is_setgroup) + if (config.is_rootless_euid && !config.is_setgroup) update_setgroups(child, SETGROUPS_DENY); /* Set up mappings. */ @@ -716,6 +721,18 @@ void nsexec(void) kill(child, SIGKILL); bail("failed to sync with child: write(SYNC_RECVPID_ACK)"); } + + /* Send the init_func pid back to our parent. + * + * Send the init_func pid and the pid of the first child back to our parent. + * We need to send both back because we can't reap the first child we created (CLONE_PARENT). + * It becomes the responsibility of our parent to reap the first child. + */ + len = dprintf(pipenum, "{\"pid\": %d, \"pid_first\": %d}\n", child, first_child); + if (len < 0) { + kill(child, SIGKILL); + bail("unable to generate JSON for child pid"); + } } break; case SYNC_CHILD_READY: @@ -759,23 +776,6 @@ void nsexec(void) bail("unexpected sync value: %u", s); } } - - /* - * Send the init_func pid and the pid of the first child back to our parent. - * - * We need to send both back because we can't reap the first child we created (CLONE_PARENT). - * It becomes the responsibility of our parent to reap the first child. - */ - len = snprintf(buf, JSON_MAX, "{\"pid\": %d, \"pid_first\": %d}\n", child, first_child); - if (len < 0) { - kill(child, SIGKILL); - bail("unable to generate JSON for child pid"); - } - if (write(pipenum, buf, len) != len) { - kill(child, SIGKILL); - bail("unable to send child pid to bootstrapper"); - } - exit(0); } @@ -862,14 +862,17 @@ void nsexec(void) if (setresuid(0, 0, 0) < 0) bail("failed to become root in user namespace"); } - /* - * Unshare all of the namespaces. Note that we don't merge this - * with clone() because there were some old kernel versions where - * clone(CLONE_PARENT | CLONE_NEWPID) was broken, so we'll just do - * it the long way. + * Unshare all of the namespaces. Now, it should be noted that this + * ordering might break in the future (especially with rootless + * containers). But for now, it's not possible to split this into + * CLONE_NEWUSER + [the rest] because of some RHEL SELinux issues. + * + * Note that we don't merge this with clone() because there were + * some old kernel versions where clone(CLONE_PARENT | CLONE_NEWPID) + * was broken, so we'll just do it the long way anyway. */ - if (unshare(config.cloneflags) < 0) + if (unshare(config.cloneflags & ~CLONE_NEWCGROUP) < 0) bail("failed to unshare namespaces"); /* @@ -953,11 +956,23 @@ void nsexec(void) if (setgid(0) < 0) bail("setgid failed"); - if (!config.is_rootless && config.is_setgroup) { + if (!config.is_rootless_euid && config.is_setgroup) { if (setgroups(0, NULL) < 0) bail("setgroups failed"); } + /* ... wait until our topmost parent has finished cgroup setup in p.manager.Apply() ... */ + if (config.cloneflags & CLONE_NEWCGROUP) { + uint8_t value; + if (read(pipenum, &value, sizeof(value)) != sizeof(value)) + bail("read synchronisation value failed"); + if (value == CREATECGROUPNS) { + if (unshare(CLONE_NEWCGROUP) < 0) + bail("failed to unshare cgroup namespace"); + } else + bail("received unknown synchronisation value"); + } + s = SYNC_CHILD_READY; if (write(syncfd, &s, sizeof(s)) != sizeof(s)) bail("failed to sync with patent: write(SYNC_CHILD_READY)"); diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go index c1e634c949db..92b5ae8de017 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go @@ -5,6 +5,7 @@ package user import ( "io" "os" + "strconv" "golang.org/x/sys/unix" ) @@ -115,22 +116,23 @@ func CurrentGroup() (Group, error) { return LookupGid(unix.Getgid()) } -func CurrentUserSubUIDs() ([]SubID, error) { +func currentUserSubIDs(fileName string) ([]SubID, error) { u, err := CurrentUser() if err != nil { return nil, err } - return ParseSubIDFileFilter("/etc/subuid", - func(entry SubID) bool { return entry.Name == u.Name }) + filter := func(entry SubID) bool { + return entry.Name == u.Name || entry.Name == strconv.Itoa(u.Uid) + } + return ParseSubIDFileFilter(fileName, filter) } -func CurrentGroupSubGIDs() ([]SubID, error) { - g, err := CurrentGroup() - if err != nil { - return nil, err - } - return ParseSubIDFileFilter("/etc/subgid", - func(entry SubID) bool { return entry.Name == g.Name }) +func CurrentUserSubUIDs() ([]SubID, error) { + return currentUserSubIDs("/etc/subuid") +} + +func CurrentUserSubGIDs() ([]SubID, error) { + return currentUserSubIDs("/etc/subgid") } func CurrentProcessUIDMap() ([]IDMap, error) { diff --git a/vendor/github.com/opencontainers/runc/vendor.conf b/vendor/github.com/opencontainers/runc/vendor.conf index e2b519e67382..fadbe07071ef 100644 --- a/vendor/github.com/opencontainers/runc/vendor.conf +++ b/vendor/github.com/opencontainers/runc/vendor.conf @@ -1,7 +1,7 @@ # OCI runtime-spec. When updating this, make sure you use a version tag rather # than a commit ID so it's much more obvious what version of the spec we are # using. -github.com/opencontainers/runtime-spec v1.0.0 +github.com/opencontainers/runtime-spec 5684b8af48c1ac3b1451fa499724e30e3c20a294 # Core libcontainer functionality. github.com/mrunalp/fileutils ed869b029674c0e9ce4c0dfa781405c2d9946d08 github.com/opencontainers/selinux v1.0.0-rc1 From 7df6bb51ab61dd90ba94556815e1e2c0ac0eede0 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Fri, 4 Jan 2019 16:07:38 +0100 Subject: [PATCH 53/60] Update github.com/containerd/continuity Signed-off-by: Sebastiaan van Stijn --- vendor.conf | 2 +- .../containerd/continuity/fs/copy.go | 16 ++++++++++++++ .../containerd/continuity/fs/copy_linux.go | 16 ++++++++++++++ .../containerd/continuity/fs/copy_unix.go | 16 ++++++++++++++ .../containerd/continuity/fs/copy_windows.go | 16 ++++++++++++++ .../containerd/continuity/fs/diff.go | 16 ++++++++++++++ .../containerd/continuity/fs/diff_unix.go | 16 ++++++++++++++ .../containerd/continuity/fs/diff_windows.go | 16 ++++++++++++++ .../containerd/continuity/fs/dtype_linux.go | 16 ++++++++++++++ .../github.com/containerd/continuity/fs/du.go | 16 ++++++++++++++ .../containerd/continuity/fs/du_unix.go | 16 ++++++++++++++ .../containerd/continuity/fs/du_windows.go | 16 ++++++++++++++ .../containerd/continuity/fs/hardlink.go | 16 ++++++++++++++ .../containerd/continuity/fs/hardlink_unix.go | 16 ++++++++++++++ .../continuity/fs/hardlink_windows.go | 16 ++++++++++++++ .../containerd/continuity/fs/path.go | 22 ++++++++++++++----- .../containerd/continuity/fs/stat_bsd.go | 16 ++++++++++++++ .../containerd/continuity/fs/stat_linux.go | 16 ++++++++++++++ .../containerd/continuity/fs/time.go | 16 ++++++++++++++ .../continuity/pathdriver/path_driver.go | 16 ++++++++++++++ .../continuity/syscallx/syscall_unix.go | 16 ++++++++++++++ .../continuity/syscallx/syscall_windows.go | 16 ++++++++++++++ .../containerd/continuity/sysx/file_posix.go | 16 ++++++++++++++ .../continuity/sysx/nodata_linux.go | 16 ++++++++++++++ .../continuity/sysx/nodata_solaris.go | 16 ++++++++++++++ .../containerd/continuity/sysx/nodata_unix.go | 16 ++++++++++++++ .../containerd/continuity/sysx/xattr.go | 16 ++++++++++++++ .../continuity/sysx/xattr_unsupported.go | 16 ++++++++++++++ 28 files changed, 433 insertions(+), 7 deletions(-) diff --git a/vendor.conf b/vendor.conf index 049b6e16eb14..c5acbef0ee77 100755 --- a/vendor.conf +++ b/vendor.conf @@ -5,7 +5,7 @@ github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109 github.com/beorn7/perks 3a771d992973f24aa725d07868b467d1ddfceafb github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 github.com/containerd/containerd 9b32062dc1f5a7c2564315c269b5059754f12b9d # v1.2.1 -github.com/containerd/continuity f44b615e492bdfb371aae2f76ec694d9da1db537 +github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4 github.com/containerd/cri 0ca1e3c2b73b5c38e72f29bb76338d0078b23d6c # release/1.2 branch github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 diff --git a/vendor/github.com/containerd/continuity/fs/copy.go b/vendor/github.com/containerd/continuity/fs/copy.go index 2ac474b92665..42df6a9a541e 100644 --- a/vendor/github.com/containerd/continuity/fs/copy.go +++ b/vendor/github.com/containerd/continuity/fs/copy.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import ( diff --git a/vendor/github.com/containerd/continuity/fs/copy_linux.go b/vendor/github.com/containerd/continuity/fs/copy_linux.go index b244e3185b56..e041b5661f03 100644 --- a/vendor/github.com/containerd/continuity/fs/copy_linux.go +++ b/vendor/github.com/containerd/continuity/fs/copy_linux.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import ( diff --git a/vendor/github.com/containerd/continuity/fs/copy_unix.go b/vendor/github.com/containerd/continuity/fs/copy_unix.go index 29cbb81ed5de..1a8ae5ebd193 100644 --- a/vendor/github.com/containerd/continuity/fs/copy_unix.go +++ b/vendor/github.com/containerd/continuity/fs/copy_unix.go @@ -1,5 +1,21 @@ // +build solaris darwin freebsd +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import ( diff --git a/vendor/github.com/containerd/continuity/fs/copy_windows.go b/vendor/github.com/containerd/continuity/fs/copy_windows.go index 6fb3de571056..be8e6489bf96 100644 --- a/vendor/github.com/containerd/continuity/fs/copy_windows.go +++ b/vendor/github.com/containerd/continuity/fs/copy_windows.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import ( diff --git a/vendor/github.com/containerd/continuity/fs/diff.go b/vendor/github.com/containerd/continuity/fs/diff.go index f2300e845dfb..e64f9e73d304 100644 --- a/vendor/github.com/containerd/continuity/fs/diff.go +++ b/vendor/github.com/containerd/continuity/fs/diff.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import ( diff --git a/vendor/github.com/containerd/continuity/fs/diff_unix.go b/vendor/github.com/containerd/continuity/fs/diff_unix.go index 37518144437f..7913af27d903 100644 --- a/vendor/github.com/containerd/continuity/fs/diff_unix.go +++ b/vendor/github.com/containerd/continuity/fs/diff_unix.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import ( diff --git a/vendor/github.com/containerd/continuity/fs/diff_windows.go b/vendor/github.com/containerd/continuity/fs/diff_windows.go index 8eed36507ee6..4bfa72d3a19a 100644 --- a/vendor/github.com/containerd/continuity/fs/diff_windows.go +++ b/vendor/github.com/containerd/continuity/fs/diff_windows.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import ( diff --git a/vendor/github.com/containerd/continuity/fs/dtype_linux.go b/vendor/github.com/containerd/continuity/fs/dtype_linux.go index cc06573f1bb3..10510d8decee 100644 --- a/vendor/github.com/containerd/continuity/fs/dtype_linux.go +++ b/vendor/github.com/containerd/continuity/fs/dtype_linux.go @@ -1,5 +1,21 @@ // +build linux +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import ( diff --git a/vendor/github.com/containerd/continuity/fs/du.go b/vendor/github.com/containerd/continuity/fs/du.go index f8fc9a994620..fccc985dc5b5 100644 --- a/vendor/github.com/containerd/continuity/fs/du.go +++ b/vendor/github.com/containerd/continuity/fs/du.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import "context" diff --git a/vendor/github.com/containerd/continuity/fs/du_unix.go b/vendor/github.com/containerd/continuity/fs/du_unix.go index 9f6bc55fd9b3..e22ffbea378f 100644 --- a/vendor/github.com/containerd/continuity/fs/du_unix.go +++ b/vendor/github.com/containerd/continuity/fs/du_unix.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import ( diff --git a/vendor/github.com/containerd/continuity/fs/du_windows.go b/vendor/github.com/containerd/continuity/fs/du_windows.go index faa443fedacc..8f25ec59c569 100644 --- a/vendor/github.com/containerd/continuity/fs/du_windows.go +++ b/vendor/github.com/containerd/continuity/fs/du_windows.go @@ -1,5 +1,21 @@ // +build windows +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import ( diff --git a/vendor/github.com/containerd/continuity/fs/hardlink.go b/vendor/github.com/containerd/continuity/fs/hardlink.go index 38da93813ce8..762aa45e694a 100644 --- a/vendor/github.com/containerd/continuity/fs/hardlink.go +++ b/vendor/github.com/containerd/continuity/fs/hardlink.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import "os" diff --git a/vendor/github.com/containerd/continuity/fs/hardlink_unix.go b/vendor/github.com/containerd/continuity/fs/hardlink_unix.go index a6f99778de16..f95f0904c192 100644 --- a/vendor/github.com/containerd/continuity/fs/hardlink_unix.go +++ b/vendor/github.com/containerd/continuity/fs/hardlink_unix.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import ( diff --git a/vendor/github.com/containerd/continuity/fs/hardlink_windows.go b/vendor/github.com/containerd/continuity/fs/hardlink_windows.go index ad8845a7fb2c..748554714707 100644 --- a/vendor/github.com/containerd/continuity/fs/hardlink_windows.go +++ b/vendor/github.com/containerd/continuity/fs/hardlink_windows.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import "os" diff --git a/vendor/github.com/containerd/continuity/fs/path.go b/vendor/github.com/containerd/continuity/fs/path.go index 13fb82638533..995981780078 100644 --- a/vendor/github.com/containerd/continuity/fs/path.go +++ b/vendor/github.com/containerd/continuity/fs/path.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import ( @@ -232,12 +248,6 @@ func walkLink(root, path string, linksWalked *int) (newpath string, islink bool, if err != nil { return "", false, err } - if filepath.IsAbs(newpath) && strings.HasPrefix(newpath, root) { - newpath = newpath[:len(root)] - if !strings.HasPrefix(newpath, "/") { - newpath = "/" + newpath - } - } *linksWalked++ return newpath, true, nil } diff --git a/vendor/github.com/containerd/continuity/fs/stat_bsd.go b/vendor/github.com/containerd/continuity/fs/stat_bsd.go index a1b776fdf527..cb7400a33e13 100644 --- a/vendor/github.com/containerd/continuity/fs/stat_bsd.go +++ b/vendor/github.com/containerd/continuity/fs/stat_bsd.go @@ -1,5 +1,21 @@ // +build darwin freebsd +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import ( diff --git a/vendor/github.com/containerd/continuity/fs/stat_linux.go b/vendor/github.com/containerd/continuity/fs/stat_linux.go index 1dbb0212b6ec..4a678dd1fd46 100644 --- a/vendor/github.com/containerd/continuity/fs/stat_linux.go +++ b/vendor/github.com/containerd/continuity/fs/stat_linux.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import ( diff --git a/vendor/github.com/containerd/continuity/fs/time.go b/vendor/github.com/containerd/continuity/fs/time.go index c336f4d88190..cde45612332d 100644 --- a/vendor/github.com/containerd/continuity/fs/time.go +++ b/vendor/github.com/containerd/continuity/fs/time.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package fs import "time" diff --git a/vendor/github.com/containerd/continuity/pathdriver/path_driver.go b/vendor/github.com/containerd/continuity/pathdriver/path_driver.go index b43d55fe9593..b0d5a6b56706 100644 --- a/vendor/github.com/containerd/continuity/pathdriver/path_driver.go +++ b/vendor/github.com/containerd/continuity/pathdriver/path_driver.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package pathdriver import ( diff --git a/vendor/github.com/containerd/continuity/syscallx/syscall_unix.go b/vendor/github.com/containerd/continuity/syscallx/syscall_unix.go index 4205d1e82c54..0bfa6a0409d0 100644 --- a/vendor/github.com/containerd/continuity/syscallx/syscall_unix.go +++ b/vendor/github.com/containerd/continuity/syscallx/syscall_unix.go @@ -1,5 +1,21 @@ // +build !windows +/* + Copyright The containerd Authors. + + 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. +*/ + package syscallx import "syscall" diff --git a/vendor/github.com/containerd/continuity/syscallx/syscall_windows.go b/vendor/github.com/containerd/continuity/syscallx/syscall_windows.go index 9637a2875a21..2ba814990519 100644 --- a/vendor/github.com/containerd/continuity/syscallx/syscall_windows.go +++ b/vendor/github.com/containerd/continuity/syscallx/syscall_windows.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package syscallx import ( diff --git a/vendor/github.com/containerd/continuity/sysx/file_posix.go b/vendor/github.com/containerd/continuity/sysx/file_posix.go index d0784f819e20..e28f3a1b574c 100644 --- a/vendor/github.com/containerd/continuity/sysx/file_posix.go +++ b/vendor/github.com/containerd/continuity/sysx/file_posix.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package sysx import ( diff --git a/vendor/github.com/containerd/continuity/sysx/nodata_linux.go b/vendor/github.com/containerd/continuity/sysx/nodata_linux.go index fc47ddb8dc70..28ce5d8de331 100644 --- a/vendor/github.com/containerd/continuity/sysx/nodata_linux.go +++ b/vendor/github.com/containerd/continuity/sysx/nodata_linux.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package sysx import ( diff --git a/vendor/github.com/containerd/continuity/sysx/nodata_solaris.go b/vendor/github.com/containerd/continuity/sysx/nodata_solaris.go index 53cc8e068faa..e0575f4468ef 100644 --- a/vendor/github.com/containerd/continuity/sysx/nodata_solaris.go +++ b/vendor/github.com/containerd/continuity/sysx/nodata_solaris.go @@ -1,3 +1,19 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + package sysx import ( diff --git a/vendor/github.com/containerd/continuity/sysx/nodata_unix.go b/vendor/github.com/containerd/continuity/sysx/nodata_unix.go index 7e6851209f28..b26f5b3d0394 100644 --- a/vendor/github.com/containerd/continuity/sysx/nodata_unix.go +++ b/vendor/github.com/containerd/continuity/sysx/nodata_unix.go @@ -1,5 +1,21 @@ // +build darwin freebsd +/* + Copyright The containerd Authors. + + 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. +*/ + package sysx import ( diff --git a/vendor/github.com/containerd/continuity/sysx/xattr.go b/vendor/github.com/containerd/continuity/sysx/xattr.go index a59efee9ae79..9e4326dcfe3f 100644 --- a/vendor/github.com/containerd/continuity/sysx/xattr.go +++ b/vendor/github.com/containerd/continuity/sysx/xattr.go @@ -1,5 +1,21 @@ // +build linux darwin +/* + Copyright The containerd Authors. + + 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. +*/ + package sysx import ( diff --git a/vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go b/vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go index 4f6a12e35592..c9ef3a1d251f 100644 --- a/vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go +++ b/vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go @@ -1,5 +1,21 @@ // +build !linux,!darwin +/* + Copyright The containerd Authors. + + 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. +*/ + package sysx import ( From d034df736b010dd855aa6625ffa85167e8c2dfdb Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 2 Feb 2019 14:14:12 +0100 Subject: [PATCH 54/60] Update docker, swarmkit, containerd v1.2.2 Also update the tests to account for the new "Builder" field in docker info. Signed-off-by: Sebastiaan van Stijn --- .../testdata/docker-info-badsec.json.golden | 2 +- .../docker-info-daemon-warnings.json.golden | 2 +- .../docker-info-legacy-warnings.json.golden | 2 +- .../testdata/docker-info-no-swarm.json.golden | 2 +- .../testdata/docker-info-plugins.json.golden | 2 +- .../docker-info-with-swarm.json.golden | 2 +- vendor.conf | 9 +- vendor/github.com/Nvveen/Gotty/LICENSE | 26 - vendor/github.com/Nvveen/Gotty/README | 5 - vendor/github.com/Nvveen/Gotty/attributes.go | 514 --------- vendor/github.com/Nvveen/Gotty/gotty.go | 244 ---- vendor/github.com/Nvveen/Gotty/parser.go | 362 ------ vendor/github.com/Nvveen/Gotty/types.go | 23 - .../containerd/containerd/vendor.conf | 2 +- .../docker/api/types/container/host_config.go | 7 +- .../docker/docker/api/types/types.go | 1 + .../github.com/docker/docker/client/README.md | 2 +- .../github.com/docker/docker/client/client.go | 156 +-- .../docker/docker/client/client_deprecated.go | 23 + .../docker/docker/client/options.go | 144 +++ .../github.com/docker/docker/client/ping.go | 48 +- .../docker/docker/client/request.go | 36 +- .../docker/docker/errdefs/helpers.go | 55 +- .../docker/docker/pkg/archive/archive.go | 4 +- .../docker/pkg/archive/archive_linux.go | 179 ++- .../docker/pkg/archive/archive_other.go | 2 +- .../docker/docker/pkg/archive/copy.go | 8 + .../docker/pkg/jsonmessage/jsonmessage.go | 104 +- vendor/github.com/docker/docker/vendor.conf | 10 +- .../github.com/docker/swarmkit/api/ca.pb.go | 1 + .../docker/swarmkit/api/types.pb.go | 1026 +++++++++++------ .../docker/swarmkit/api/types.proto | 15 +- vendor/github.com/docker/swarmkit/vendor.conf | 40 +- 33 files changed, 1200 insertions(+), 1858 deletions(-) delete mode 100644 vendor/github.com/Nvveen/Gotty/LICENSE delete mode 100644 vendor/github.com/Nvveen/Gotty/README delete mode 100644 vendor/github.com/Nvveen/Gotty/attributes.go delete mode 100644 vendor/github.com/Nvveen/Gotty/gotty.go delete mode 100644 vendor/github.com/Nvveen/Gotty/parser.go delete mode 100644 vendor/github.com/Nvveen/Gotty/types.go create mode 100644 vendor/github.com/docker/docker/client/client_deprecated.go create mode 100644 vendor/github.com/docker/docker/client/options.go diff --git a/cli/command/system/testdata/docker-info-badsec.json.golden b/cli/command/system/testdata/docker-info-badsec.json.golden index 3f3eea239385..32502c8647b1 100644 --- a/cli/command/system/testdata/docker-info-badsec.json.golden +++ b/cli/command/system/testdata/docker-info-badsec.json.golden @@ -1 +1 @@ -{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["foo="],"Warnings":null,"ServerErrors":["an error happened"],"ClientInfo":{"Debug":false,"Plugins":[],"Warnings":null}} +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Builder":"","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["foo="],"Warnings":null,"ServerErrors":["an error happened"],"ClientInfo":{"Debug":false,"Plugins":[],"Warnings":null}} diff --git a/cli/command/system/testdata/docker-info-daemon-warnings.json.golden b/cli/command/system/testdata/docker-info-daemon-warnings.json.golden index 0a387bb51072..b9bc4b7ae757 100644 --- a/cli/command/system/testdata/docker-info-daemon-warnings.json.golden +++ b/cli/command/system/testdata/docker-info-daemon-warnings.json.golden @@ -1 +1 @@ -{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":["WARNING: No memory limit support","WARNING: No swap limit support","WARNING: No kernel memory limit support","WARNING: No oom kill disable support","WARNING: No cpu cfs quota support","WARNING: No cpu cfs period support","WARNING: No cpu shares support","WARNING: No cpuset support","WARNING: IPv4 forwarding is disabled","WARNING: bridge-nf-call-iptables is disabled","WARNING: bridge-nf-call-ip6tables is disabled"],"ClientInfo":{"Debug":true,"Plugins":[],"Warnings":null}} +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Builder":"","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":["WARNING: No memory limit support","WARNING: No swap limit support","WARNING: No kernel memory limit support","WARNING: No oom kill disable support","WARNING: No cpu cfs quota support","WARNING: No cpu cfs period support","WARNING: No cpu shares support","WARNING: No cpuset support","WARNING: IPv4 forwarding is disabled","WARNING: bridge-nf-call-iptables is disabled","WARNING: bridge-nf-call-ip6tables is disabled"],"ClientInfo":{"Debug":true,"Plugins":[],"Warnings":null}} diff --git a/cli/command/system/testdata/docker-info-legacy-warnings.json.golden b/cli/command/system/testdata/docker-info-legacy-warnings.json.golden index 1996dd6d359a..d4a815cc9e2d 100644 --- a/cli/command/system/testdata/docker-info-legacy-warnings.json.golden +++ b/cli/command/system/testdata/docker-info-legacy-warnings.json.golden @@ -1 +1 @@ -{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":false,"SwapLimit":false,"KernelMemory":false,"KernelMemoryTCP":false,"CpuCfsPeriod":false,"CpuCfsQuota":false,"CPUShares":false,"CPUSet":false,"IPv4Forwarding":false,"BridgeNfIptables":false,"BridgeNfIp6tables":false,"Debug":true,"NFd":33,"OomKillDisable":false,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":true,"Plugins":[],"Warnings":null}} +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Builder":"","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":false,"SwapLimit":false,"KernelMemory":false,"KernelMemoryTCP":false,"CpuCfsPeriod":false,"CpuCfsQuota":false,"CPUShares":false,"CPUSet":false,"IPv4Forwarding":false,"BridgeNfIptables":false,"BridgeNfIp6tables":false,"Debug":true,"NFd":33,"OomKillDisable":false,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":true,"Plugins":[],"Warnings":null}} diff --git a/cli/command/system/testdata/docker-info-no-swarm.json.golden b/cli/command/system/testdata/docker-info-no-swarm.json.golden index 7dd6c0eb2a57..397dd00be604 100644 --- a/cli/command/system/testdata/docker-info-no-swarm.json.golden +++ b/cli/command/system/testdata/docker-info-no-swarm.json.golden @@ -1 +1 @@ -{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":true,"Plugins":[],"Warnings":null}} +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Builder":"","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":true,"Plugins":[],"Warnings":null}} diff --git a/cli/command/system/testdata/docker-info-plugins.json.golden b/cli/command/system/testdata/docker-info-plugins.json.golden index f7c8435b3b10..0527157b5b56 100644 --- a/cli/command/system/testdata/docker-info-plugins.json.golden +++ b/cli/command/system/testdata/docker-info-plugins.json.golden @@ -1 +1 @@ -{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":false,"Plugins":[{"SchemaVersion":"0.1.0","Vendor":"ACME Corp","Version":"0.1.0","ShortDescription":"unit test is good","Name":"goodplugin","Path":"/path/to/docker-goodplugin"},{"Name":"badplugin","Path":"/path/to/docker-badplugin","Err":"something wrong"}],"Warnings":null}} +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Builder":"","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":false,"Plugins":[{"SchemaVersion":"0.1.0","Vendor":"ACME Corp","Version":"0.1.0","ShortDescription":"unit test is good","Name":"goodplugin","Path":"/path/to/docker-goodplugin"},{"Name":"badplugin","Path":"/path/to/docker-badplugin","Err":"something wrong"}],"Warnings":null}} diff --git a/cli/command/system/testdata/docker-info-with-swarm.json.golden b/cli/command/system/testdata/docker-info-with-swarm.json.golden index da49769c8d64..50ffc05c8ecb 100644 --- a/cli/command/system/testdata/docker-info-with-swarm.json.golden +++ b/cli/command/system/testdata/docker-info-with-swarm.json.golden @@ -1 +1 @@ -{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"qo2dfdig9mmxqkawulggepdih","NodeAddr":"165.227.107.89","LocalNodeState":"active","ControlAvailable":true,"Error":"","RemoteManagers":[{"NodeID":"qo2dfdig9mmxqkawulggepdih","Addr":"165.227.107.89:2377"}],"Nodes":1,"Managers":1,"Cluster":{"ID":"9vs5ygs0gguyyec4iqf2314c0","Version":{"Index":11},"CreatedAt":"2017-08-24T17:34:19.278062352Z","UpdatedAt":"2017-08-24T17:34:42.398815481Z","Spec":{"Name":"default","Labels":null,"Orchestration":{"TaskHistoryRetentionLimit":5},"Raft":{"SnapshotInterval":10000,"KeepOldSnapshots":0,"LogEntriesForSlowFollowers":500,"ElectionTick":3,"HeartbeatTick":1},"Dispatcher":{"HeartbeatPeriod":5000000000},"CAConfig":{"NodeCertExpiry":7776000000000000},"TaskDefaults":{},"EncryptionConfig":{"AutoLockManagers":true}},"TLSInfo":{"TrustRoot":"\n-----BEGIN CERTIFICATE-----\nMIIBajCCARCgAwIBAgIUaFCW5xsq8eyiJ+Pmcv3MCflMLnMwCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwODI0MTcyOTAwWhcNMzcwODE5MTcy\nOTAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABDy7NebyUJyUjWJDBUdnZoV6GBxEGKO4TZPNDwnxDxJcUdLVaB7WGa4/DLrW\nUfsVgh1JGik2VTiLuTMA1tLlNPOjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBQl16XFtaaXiUAwEuJptJlDjfKskDAKBggqhkjO\nPQQDAgNIADBFAiEAo9fTQNM5DP9bHVcTJYfl2Cay1bFu1E+lnpmN+EYJfeACIGKH\n1pCUkZ+D0IB6CiEZGWSHyLuXPM1rlP+I5KuS7sB8\n-----END CERTIFICATE-----\n","CertIssuerSubject":"MBMxETAPBgNVBAMTCHN3YXJtLWNh","CertIssuerPublicKey":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPLs15vJQnJSNYkMFR2dmhXoYHEQYo7hNk80PCfEPElxR0tVoHtYZrj8MutZR+xWCHUkaKTZVOIu5MwDW0uU08w=="},"RootRotationInProgress":false,"DefaultAddrPool":null,"SubnetSize":0,"DataPathPort":0}},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":false,"Plugins":[],"Warnings":null}} +{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Builder":"","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"SystemStatus":null,"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","ClusterStore":"","ClusterAdvertise":"","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"qo2dfdig9mmxqkawulggepdih","NodeAddr":"165.227.107.89","LocalNodeState":"active","ControlAvailable":true,"Error":"","RemoteManagers":[{"NodeID":"qo2dfdig9mmxqkawulggepdih","Addr":"165.227.107.89:2377"}],"Nodes":1,"Managers":1,"Cluster":{"ID":"9vs5ygs0gguyyec4iqf2314c0","Version":{"Index":11},"CreatedAt":"2017-08-24T17:34:19.278062352Z","UpdatedAt":"2017-08-24T17:34:42.398815481Z","Spec":{"Name":"default","Labels":null,"Orchestration":{"TaskHistoryRetentionLimit":5},"Raft":{"SnapshotInterval":10000,"KeepOldSnapshots":0,"LogEntriesForSlowFollowers":500,"ElectionTick":3,"HeartbeatTick":1},"Dispatcher":{"HeartbeatPeriod":5000000000},"CAConfig":{"NodeCertExpiry":7776000000000000},"TaskDefaults":{},"EncryptionConfig":{"AutoLockManagers":true}},"TLSInfo":{"TrustRoot":"\n-----BEGIN CERTIFICATE-----\nMIIBajCCARCgAwIBAgIUaFCW5xsq8eyiJ+Pmcv3MCflMLnMwCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwODI0MTcyOTAwWhcNMzcwODE5MTcy\nOTAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABDy7NebyUJyUjWJDBUdnZoV6GBxEGKO4TZPNDwnxDxJcUdLVaB7WGa4/DLrW\nUfsVgh1JGik2VTiLuTMA1tLlNPOjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBQl16XFtaaXiUAwEuJptJlDjfKskDAKBggqhkjO\nPQQDAgNIADBFAiEAo9fTQNM5DP9bHVcTJYfl2Cay1bFu1E+lnpmN+EYJfeACIGKH\n1pCUkZ+D0IB6CiEZGWSHyLuXPM1rlP+I5KuS7sB8\n-----END CERTIFICATE-----\n","CertIssuerSubject":"MBMxETAPBgNVBAMTCHN3YXJtLWNh","CertIssuerPublicKey":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPLs15vJQnJSNYkMFR2dmhXoYHEQYo7hNk80PCfEPElxR0tVoHtYZrj8MutZR+xWCHUkaKTZVOIu5MwDW0uU08w=="},"RootRotationInProgress":false,"DefaultAddrPool":null,"SubnetSize":0,"DataPathPort":0}},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"Warnings":null,"ClientInfo":{"Debug":false,"Plugins":[],"Warnings":null}} diff --git a/vendor.conf b/vendor.conf index c5acbef0ee77..c0264a0f1a24 100755 --- a/vendor.conf +++ b/vendor.conf @@ -4,9 +4,9 @@ github.com/asaskevich/govalidator f9ffefc3facfbe0caee3fea233cbb6e8208f4541 github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109 github.com/beorn7/perks 3a771d992973f24aa725d07868b467d1ddfceafb github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 -github.com/containerd/containerd 9b32062dc1f5a7c2564315c269b5059754f12b9d # v1.2.1 +github.com/containerd/containerd 9754871865f7fe2f4e74d43e2fc7ccd237edcbce # v1.2.2 github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4 -github.com/containerd/cri 0ca1e3c2b73b5c38e72f29bb76338d0078b23d6c # release/1.2 branch +github.com/containerd/cri 0d5cabd006cb5319dc965046067b8432d9fa5ef8 # release/1.2 branch github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 github.com/coreos/etcd v3.3.9 @@ -14,7 +14,7 @@ github.com/cpuguy83/go-md2man v1.0.8 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 # v1.1.0 github.com/dgrijalva/jwt-go a2c85815a77d0f951e33ba4db5ae93629a1530af github.com/docker/distribution 83389a148052d74ac602f5f1d62f86ff2f3c4aa5 -github.com/docker/docker f76d6a078d881f410c00e8d900dcdfc2e026c841 +github.com/docker/docker 50e63adf30d33fc1547527a4097c796cbe4b770f github.com/docker/compose-on-kubernetes 356b2919c496f7e988f6e0dfe7e67d919602e14e # master w/ v1alpha3+pullsecrets+pull-policy github.com/docker/docker-credential-helpers 5241b46610f2491efdf9d1c85f1ddf5b02f6d962 # the docker/go package contains a customized version of canonical/json @@ -26,7 +26,7 @@ github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18 github.com/docker/go-units 47565b4f722fb6ceae66b95f853feed578a4a51c # v0.3.3 github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a github.com/docker/licensing 1c117a1720cb413dd6a101d36a6c567b1ccb90fe -github.com/docker/swarmkit 8af8c420f491f006ab1730e08d446a795b1667d7 +github.com/docker/swarmkit ebfb0aa1118ebfd35a224d72a5d337ce0addd907 github.com/flynn-archive/go-shlex 3f9db97f856818214da2e1057f8ad84803971cff github.com/ghodss/yaml 0ca9ea5df5451ffdf184b4428c902747c2c11cd7 # v1.0.0 github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef @@ -58,7 +58,6 @@ github.com/moby/buildkit 520201006c9dc676da9cf9655337ac711f7f127d github.com/modern-go/concurrent bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94 # 1.0.3 github.com/modern-go/reflect2 4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd # 1.0.1 github.com/morikuni/aec 39771216ff4c63d11f5e604076f9c45e8be1067b -github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github.com/ijc25/Gotty github.com/opencontainers/go-digest v1.0.0-rc1 github.com/opencontainers/image-spec v1.0.1 github.com/opencontainers/runc 96ec2177ae841256168fcf76954f7177af9446eb diff --git a/vendor/github.com/Nvveen/Gotty/LICENSE b/vendor/github.com/Nvveen/Gotty/LICENSE deleted file mode 100644 index 0b71c97360eb..000000000000 --- a/vendor/github.com/Nvveen/Gotty/LICENSE +++ /dev/null @@ -1,26 +0,0 @@ -Copyright (c) 2012, Neal van Veen (nealvanveen@gmail.com) -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. - -The views and conclusions contained in the software and documentation are those -of the authors and should not be interpreted as representing official policies, -either expressed or implied, of the FreeBSD Project. diff --git a/vendor/github.com/Nvveen/Gotty/README b/vendor/github.com/Nvveen/Gotty/README deleted file mode 100644 index a6b0d9a8fe25..000000000000 --- a/vendor/github.com/Nvveen/Gotty/README +++ /dev/null @@ -1,5 +0,0 @@ -Gotty is a library written in Go that determines and reads termcap database -files to produce an interface for interacting with the capabilities of a -terminal. -See the godoc documentation or the source code for more information about -function usage. diff --git a/vendor/github.com/Nvveen/Gotty/attributes.go b/vendor/github.com/Nvveen/Gotty/attributes.go deleted file mode 100644 index a4c005fae583..000000000000 --- a/vendor/github.com/Nvveen/Gotty/attributes.go +++ /dev/null @@ -1,514 +0,0 @@ -// Copyright 2012 Neal van Veen. All rights reserved. -// Usage of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package gotty - -// Boolean capabilities -var BoolAttr = [...]string{ - "auto_left_margin", "bw", - "auto_right_margin", "am", - "no_esc_ctlc", "xsb", - "ceol_standout_glitch", "xhp", - "eat_newline_glitch", "xenl", - "erase_overstrike", "eo", - "generic_type", "gn", - "hard_copy", "hc", - "has_meta_key", "km", - "has_status_line", "hs", - "insert_null_glitch", "in", - "memory_above", "da", - "memory_below", "db", - "move_insert_mode", "mir", - "move_standout_mode", "msgr", - "over_strike", "os", - "status_line_esc_ok", "eslok", - "dest_tabs_magic_smso", "xt", - "tilde_glitch", "hz", - "transparent_underline", "ul", - "xon_xoff", "nxon", - "needs_xon_xoff", "nxon", - "prtr_silent", "mc5i", - "hard_cursor", "chts", - "non_rev_rmcup", "nrrmc", - "no_pad_char", "npc", - "non_dest_scroll_region", "ndscr", - "can_change", "ccc", - "back_color_erase", "bce", - "hue_lightness_saturation", "hls", - "col_addr_glitch", "xhpa", - "cr_cancels_micro_mode", "crxm", - "has_print_wheel", "daisy", - "row_addr_glitch", "xvpa", - "semi_auto_right_margin", "sam", - "cpi_changes_res", "cpix", - "lpi_changes_res", "lpix", - "backspaces_with_bs", "", - "crt_no_scrolling", "", - "no_correctly_working_cr", "", - "gnu_has_meta_key", "", - "linefeed_is_newline", "", - "has_hardware_tabs", "", - "return_does_clr_eol", "", -} - -// Numerical capabilities -var NumAttr = [...]string{ - "columns", "cols", - "init_tabs", "it", - "lines", "lines", - "lines_of_memory", "lm", - "magic_cookie_glitch", "xmc", - "padding_baud_rate", "pb", - "virtual_terminal", "vt", - "width_status_line", "wsl", - "num_labels", "nlab", - "label_height", "lh", - "label_width", "lw", - "max_attributes", "ma", - "maximum_windows", "wnum", - "max_colors", "colors", - "max_pairs", "pairs", - "no_color_video", "ncv", - "buffer_capacity", "bufsz", - "dot_vert_spacing", "spinv", - "dot_horz_spacing", "spinh", - "max_micro_address", "maddr", - "max_micro_jump", "mjump", - "micro_col_size", "mcs", - "micro_line_size", "mls", - "number_of_pins", "npins", - "output_res_char", "orc", - "output_res_line", "orl", - "output_res_horz_inch", "orhi", - "output_res_vert_inch", "orvi", - "print_rate", "cps", - "wide_char_size", "widcs", - "buttons", "btns", - "bit_image_entwining", "bitwin", - "bit_image_type", "bitype", - "magic_cookie_glitch_ul", "", - "carriage_return_delay", "", - "new_line_delay", "", - "backspace_delay", "", - "horizontal_tab_delay", "", - "number_of_function_keys", "", -} - -// String capabilities -var StrAttr = [...]string{ - "back_tab", "cbt", - "bell", "bel", - "carriage_return", "cr", - "change_scroll_region", "csr", - "clear_all_tabs", "tbc", - "clear_screen", "clear", - "clr_eol", "el", - "clr_eos", "ed", - "column_address", "hpa", - "command_character", "cmdch", - "cursor_address", "cup", - "cursor_down", "cud1", - "cursor_home", "home", - "cursor_invisible", "civis", - "cursor_left", "cub1", - "cursor_mem_address", "mrcup", - "cursor_normal", "cnorm", - "cursor_right", "cuf1", - "cursor_to_ll", "ll", - "cursor_up", "cuu1", - "cursor_visible", "cvvis", - "delete_character", "dch1", - "delete_line", "dl1", - "dis_status_line", "dsl", - "down_half_line", "hd", - "enter_alt_charset_mode", "smacs", - "enter_blink_mode", "blink", - "enter_bold_mode", "bold", - "enter_ca_mode", "smcup", - "enter_delete_mode", "smdc", - "enter_dim_mode", "dim", - "enter_insert_mode", "smir", - "enter_secure_mode", "invis", - "enter_protected_mode", "prot", - "enter_reverse_mode", "rev", - "enter_standout_mode", "smso", - "enter_underline_mode", "smul", - "erase_chars", "ech", - "exit_alt_charset_mode", "rmacs", - "exit_attribute_mode", "sgr0", - "exit_ca_mode", "rmcup", - "exit_delete_mode", "rmdc", - "exit_insert_mode", "rmir", - "exit_standout_mode", "rmso", - "exit_underline_mode", "rmul", - "flash_screen", "flash", - "form_feed", "ff", - "from_status_line", "fsl", - "init_1string", "is1", - "init_2string", "is2", - "init_3string", "is3", - "init_file", "if", - "insert_character", "ich1", - "insert_line", "il1", - "insert_padding", "ip", - "key_backspace", "kbs", - "key_catab", "ktbc", - "key_clear", "kclr", - "key_ctab", "kctab", - "key_dc", "kdch1", - "key_dl", "kdl1", - "key_down", "kcud1", - "key_eic", "krmir", - "key_eol", "kel", - "key_eos", "ked", - "key_f0", "kf0", - "key_f1", "kf1", - "key_f10", "kf10", - "key_f2", "kf2", - "key_f3", "kf3", - "key_f4", "kf4", - "key_f5", "kf5", - "key_f6", "kf6", - "key_f7", "kf7", - "key_f8", "kf8", - "key_f9", "kf9", - "key_home", "khome", - "key_ic", "kich1", - "key_il", "kil1", - "key_left", "kcub1", - "key_ll", "kll", - "key_npage", "knp", - "key_ppage", "kpp", - "key_right", "kcuf1", - "key_sf", "kind", - "key_sr", "kri", - "key_stab", "khts", - "key_up", "kcuu1", - "keypad_local", "rmkx", - "keypad_xmit", "smkx", - "lab_f0", "lf0", - "lab_f1", "lf1", - "lab_f10", "lf10", - "lab_f2", "lf2", - "lab_f3", "lf3", - "lab_f4", "lf4", - "lab_f5", "lf5", - "lab_f6", "lf6", - "lab_f7", "lf7", - "lab_f8", "lf8", - "lab_f9", "lf9", - "meta_off", "rmm", - "meta_on", "smm", - "newline", "_glitch", - "pad_char", "npc", - "parm_dch", "dch", - "parm_delete_line", "dl", - "parm_down_cursor", "cud", - "parm_ich", "ich", - "parm_index", "indn", - "parm_insert_line", "il", - "parm_left_cursor", "cub", - "parm_right_cursor", "cuf", - "parm_rindex", "rin", - "parm_up_cursor", "cuu", - "pkey_key", "pfkey", - "pkey_local", "pfloc", - "pkey_xmit", "pfx", - "print_screen", "mc0", - "prtr_off", "mc4", - "prtr_on", "mc5", - "repeat_char", "rep", - "reset_1string", "rs1", - "reset_2string", "rs2", - "reset_3string", "rs3", - "reset_file", "rf", - "restore_cursor", "rc", - "row_address", "mvpa", - "save_cursor", "row_address", - "scroll_forward", "ind", - "scroll_reverse", "ri", - "set_attributes", "sgr", - "set_tab", "hts", - "set_window", "wind", - "tab", "s_magic_smso", - "to_status_line", "tsl", - "underline_char", "uc", - "up_half_line", "hu", - "init_prog", "iprog", - "key_a1", "ka1", - "key_a3", "ka3", - "key_b2", "kb2", - "key_c1", "kc1", - "key_c3", "kc3", - "prtr_non", "mc5p", - "char_padding", "rmp", - "acs_chars", "acsc", - "plab_norm", "pln", - "key_btab", "kcbt", - "enter_xon_mode", "smxon", - "exit_xon_mode", "rmxon", - "enter_am_mode", "smam", - "exit_am_mode", "rmam", - "xon_character", "xonc", - "xoff_character", "xoffc", - "ena_acs", "enacs", - "label_on", "smln", - "label_off", "rmln", - "key_beg", "kbeg", - "key_cancel", "kcan", - "key_close", "kclo", - "key_command", "kcmd", - "key_copy", "kcpy", - "key_create", "kcrt", - "key_end", "kend", - "key_enter", "kent", - "key_exit", "kext", - "key_find", "kfnd", - "key_help", "khlp", - "key_mark", "kmrk", - "key_message", "kmsg", - "key_move", "kmov", - "key_next", "knxt", - "key_open", "kopn", - "key_options", "kopt", - "key_previous", "kprv", - "key_print", "kprt", - "key_redo", "krdo", - "key_reference", "kref", - "key_refresh", "krfr", - "key_replace", "krpl", - "key_restart", "krst", - "key_resume", "kres", - "key_save", "ksav", - "key_suspend", "kspd", - "key_undo", "kund", - "key_sbeg", "kBEG", - "key_scancel", "kCAN", - "key_scommand", "kCMD", - "key_scopy", "kCPY", - "key_screate", "kCRT", - "key_sdc", "kDC", - "key_sdl", "kDL", - "key_select", "kslt", - "key_send", "kEND", - "key_seol", "kEOL", - "key_sexit", "kEXT", - "key_sfind", "kFND", - "key_shelp", "kHLP", - "key_shome", "kHOM", - "key_sic", "kIC", - "key_sleft", "kLFT", - "key_smessage", "kMSG", - "key_smove", "kMOV", - "key_snext", "kNXT", - "key_soptions", "kOPT", - "key_sprevious", "kPRV", - "key_sprint", "kPRT", - "key_sredo", "kRDO", - "key_sreplace", "kRPL", - "key_sright", "kRIT", - "key_srsume", "kRES", - "key_ssave", "kSAV", - "key_ssuspend", "kSPD", - "key_sundo", "kUND", - "req_for_input", "rfi", - "key_f11", "kf11", - "key_f12", "kf12", - "key_f13", "kf13", - "key_f14", "kf14", - "key_f15", "kf15", - "key_f16", "kf16", - "key_f17", "kf17", - "key_f18", "kf18", - "key_f19", "kf19", - "key_f20", "kf20", - "key_f21", "kf21", - "key_f22", "kf22", - "key_f23", "kf23", - "key_f24", "kf24", - "key_f25", "kf25", - "key_f26", "kf26", - "key_f27", "kf27", - "key_f28", "kf28", - "key_f29", "kf29", - "key_f30", "kf30", - "key_f31", "kf31", - "key_f32", "kf32", - "key_f33", "kf33", - "key_f34", "kf34", - "key_f35", "kf35", - "key_f36", "kf36", - "key_f37", "kf37", - "key_f38", "kf38", - "key_f39", "kf39", - "key_f40", "kf40", - "key_f41", "kf41", - "key_f42", "kf42", - "key_f43", "kf43", - "key_f44", "kf44", - "key_f45", "kf45", - "key_f46", "kf46", - "key_f47", "kf47", - "key_f48", "kf48", - "key_f49", "kf49", - "key_f50", "kf50", - "key_f51", "kf51", - "key_f52", "kf52", - "key_f53", "kf53", - "key_f54", "kf54", - "key_f55", "kf55", - "key_f56", "kf56", - "key_f57", "kf57", - "key_f58", "kf58", - "key_f59", "kf59", - "key_f60", "kf60", - "key_f61", "kf61", - "key_f62", "kf62", - "key_f63", "kf63", - "clr_bol", "el1", - "clear_margins", "mgc", - "set_left_margin", "smgl", - "set_right_margin", "smgr", - "label_format", "fln", - "set_clock", "sclk", - "display_clock", "dclk", - "remove_clock", "rmclk", - "create_window", "cwin", - "goto_window", "wingo", - "hangup", "hup", - "dial_phone", "dial", - "quick_dial", "qdial", - "tone", "tone", - "pulse", "pulse", - "flash_hook", "hook", - "fixed_pause", "pause", - "wait_tone", "wait", - "user0", "u0", - "user1", "u1", - "user2", "u2", - "user3", "u3", - "user4", "u4", - "user5", "u5", - "user6", "u6", - "user7", "u7", - "user8", "u8", - "user9", "u9", - "orig_pair", "op", - "orig_colors", "oc", - "initialize_color", "initc", - "initialize_pair", "initp", - "set_color_pair", "scp", - "set_foreground", "setf", - "set_background", "setb", - "change_char_pitch", "cpi", - "change_line_pitch", "lpi", - "change_res_horz", "chr", - "change_res_vert", "cvr", - "define_char", "defc", - "enter_doublewide_mode", "swidm", - "enter_draft_quality", "sdrfq", - "enter_italics_mode", "sitm", - "enter_leftward_mode", "slm", - "enter_micro_mode", "smicm", - "enter_near_letter_quality", "snlq", - "enter_normal_quality", "snrmq", - "enter_shadow_mode", "sshm", - "enter_subscript_mode", "ssubm", - "enter_superscript_mode", "ssupm", - "enter_upward_mode", "sum", - "exit_doublewide_mode", "rwidm", - "exit_italics_mode", "ritm", - "exit_leftward_mode", "rlm", - "exit_micro_mode", "rmicm", - "exit_shadow_mode", "rshm", - "exit_subscript_mode", "rsubm", - "exit_superscript_mode", "rsupm", - "exit_upward_mode", "rum", - "micro_column_address", "mhpa", - "micro_down", "mcud1", - "micro_left", "mcub1", - "micro_right", "mcuf1", - "micro_row_address", "mvpa", - "micro_up", "mcuu1", - "order_of_pins", "porder", - "parm_down_micro", "mcud", - "parm_left_micro", "mcub", - "parm_right_micro", "mcuf", - "parm_up_micro", "mcuu", - "select_char_set", "scs", - "set_bottom_margin", "smgb", - "set_bottom_margin_parm", "smgbp", - "set_left_margin_parm", "smglp", - "set_right_margin_parm", "smgrp", - "set_top_margin", "smgt", - "set_top_margin_parm", "smgtp", - "start_bit_image", "sbim", - "start_char_set_def", "scsd", - "stop_bit_image", "rbim", - "stop_char_set_def", "rcsd", - "subscript_characters", "subcs", - "superscript_characters", "supcs", - "these_cause_cr", "docr", - "zero_motion", "zerom", - "char_set_names", "csnm", - "key_mouse", "kmous", - "mouse_info", "minfo", - "req_mouse_pos", "reqmp", - "get_mouse", "getm", - "set_a_foreground", "setaf", - "set_a_background", "setab", - "pkey_plab", "pfxl", - "device_type", "devt", - "code_set_init", "csin", - "set0_des_seq", "s0ds", - "set1_des_seq", "s1ds", - "set2_des_seq", "s2ds", - "set3_des_seq", "s3ds", - "set_lr_margin", "smglr", - "set_tb_margin", "smgtb", - "bit_image_repeat", "birep", - "bit_image_newline", "binel", - "bit_image_carriage_return", "bicr", - "color_names", "colornm", - "define_bit_image_region", "defbi", - "end_bit_image_region", "endbi", - "set_color_band", "setcolor", - "set_page_length", "slines", - "display_pc_char", "dispc", - "enter_pc_charset_mode", "smpch", - "exit_pc_charset_mode", "rmpch", - "enter_scancode_mode", "smsc", - "exit_scancode_mode", "rmsc", - "pc_term_options", "pctrm", - "scancode_escape", "scesc", - "alt_scancode_esc", "scesa", - "enter_horizontal_hl_mode", "ehhlm", - "enter_left_hl_mode", "elhlm", - "enter_low_hl_mode", "elohlm", - "enter_right_hl_mode", "erhlm", - "enter_top_hl_mode", "ethlm", - "enter_vertical_hl_mode", "evhlm", - "set_a_attributes", "sgr1", - "set_pglen_inch", "slength", - "termcap_init2", "", - "termcap_reset", "", - "linefeed_if_not_lf", "", - "backspace_if_not_bs", "", - "other_non_function_keys", "", - "arrow_key_map", "", - "acs_ulcorner", "", - "acs_llcorner", "", - "acs_urcorner", "", - "acs_lrcorner", "", - "acs_ltee", "", - "acs_rtee", "", - "acs_btee", "", - "acs_ttee", "", - "acs_hline", "", - "acs_vline", "", - "acs_plus", "", - "memory_lock", "", - "memory_unlock", "", - "box_chars_1", "", -} diff --git a/vendor/github.com/Nvveen/Gotty/gotty.go b/vendor/github.com/Nvveen/Gotty/gotty.go deleted file mode 100644 index c329778a1d0f..000000000000 --- a/vendor/github.com/Nvveen/Gotty/gotty.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2012 Neal van Veen. All rights reserved. -// Usage of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Gotty is a Go-package for reading and parsing the terminfo database -package gotty - -// TODO add more concurrency to name lookup, look for more opportunities. - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "os" - "path" - "reflect" - "strings" - "sync" -) - -// Open a terminfo file by the name given and construct a TermInfo object. -// If something went wrong reading the terminfo database file, an error is -// returned. -func OpenTermInfo(termName string) (*TermInfo, error) { - if len(termName) == 0 { - return nil, errors.New("No termname given") - } - // Find the environment variables - if termloc := os.Getenv("TERMINFO"); len(termloc) > 0 { - return readTermInfo(path.Join(termloc, string(termName[0]), termName)) - } else { - // Search like ncurses - locations := []string{} - if h := os.Getenv("HOME"); len(h) > 0 { - locations = append(locations, path.Join(h, ".terminfo")) - } - locations = append(locations, - "/etc/terminfo/", - "/lib/terminfo/", - "/usr/share/terminfo/") - for _, str := range locations { - term, err := readTermInfo(path.Join(str, string(termName[0]), termName)) - if err == nil { - return term, nil - } - } - return nil, errors.New("No terminfo file(-location) found") - } -} - -// Open a terminfo file from the environment variable containing the current -// terminal name and construct a TermInfo object. If something went wrong -// reading the terminfo database file, an error is returned. -func OpenTermInfoEnv() (*TermInfo, error) { - termenv := os.Getenv("TERM") - return OpenTermInfo(termenv) -} - -// Return an attribute by the name attr provided. If none can be found, -// an error is returned. -func (term *TermInfo) GetAttribute(attr string) (stacker, error) { - // Channel to store the main value in. - var value stacker - // Add a blocking WaitGroup - var block sync.WaitGroup - // Keep track of variable being written. - written := false - // Function to put into goroutine. - f := func(ats interface{}) { - var ok bool - var v stacker - // Switch on type of map to use and assign value to it. - switch reflect.TypeOf(ats).Elem().Kind() { - case reflect.Bool: - v, ok = ats.(map[string]bool)[attr] - case reflect.Int16: - v, ok = ats.(map[string]int16)[attr] - case reflect.String: - v, ok = ats.(map[string]string)[attr] - } - // If ok, a value is found, so we can write. - if ok { - value = v - written = true - } - // Goroutine is done - block.Done() - } - block.Add(3) - // Go for all 3 attribute lists. - go f(term.boolAttributes) - go f(term.numAttributes) - go f(term.strAttributes) - // Wait until every goroutine is done. - block.Wait() - // If a value has been written, return it. - if written { - return value, nil - } - // Otherwise, error. - return nil, fmt.Errorf("Erorr finding attribute") -} - -// Return an attribute by the name attr provided. If none can be found, -// an error is returned. A name is first converted to its termcap value. -func (term *TermInfo) GetAttributeName(name string) (stacker, error) { - tc := GetTermcapName(name) - return term.GetAttribute(tc) -} - -// A utility function that finds and returns the termcap equivalent of a -// variable name. -func GetTermcapName(name string) string { - // Termcap name - var tc string - // Blocking group - var wait sync.WaitGroup - // Function to put into a goroutine - f := func(attrs []string) { - // Find the string corresponding to the name - for i, s := range attrs { - if s == name { - tc = attrs[i+1] - } - } - // Goroutine is finished - wait.Done() - } - wait.Add(3) - // Go for all 3 attribute lists - go f(BoolAttr[:]) - go f(NumAttr[:]) - go f(StrAttr[:]) - // Wait until every goroutine is done - wait.Wait() - // Return the termcap name - return tc -} - -// This function takes a path to a terminfo file and reads it in binary -// form to construct the actual TermInfo file. -func readTermInfo(path string) (*TermInfo, error) { - // Open the terminfo file - file, err := os.Open(path) - defer file.Close() - if err != nil { - return nil, err - } - - // magic, nameSize, boolSize, nrSNum, nrOffsetsStr, strSize - // Header is composed of the magic 0432 octal number, size of the name - // section, size of the boolean section, the amount of number values, - // the number of offsets of strings, and the size of the string section. - var header [6]int16 - // Byte array is used to read in byte values - var byteArray []byte - // Short array is used to read in short values - var shArray []int16 - // TermInfo object to store values - var term TermInfo - - // Read in the header - err = binary.Read(file, binary.LittleEndian, &header) - if err != nil { - return nil, err - } - // If magic number isn't there or isn't correct, we have the wrong filetype - if header[0] != 0432 { - return nil, errors.New(fmt.Sprintf("Wrong filetype")) - } - - // Read in the names - byteArray = make([]byte, header[1]) - err = binary.Read(file, binary.LittleEndian, &byteArray) - if err != nil { - return nil, err - } - term.Names = strings.Split(string(byteArray), "|") - - // Read in the booleans - byteArray = make([]byte, header[2]) - err = binary.Read(file, binary.LittleEndian, &byteArray) - if err != nil { - return nil, err - } - term.boolAttributes = make(map[string]bool) - for i, b := range byteArray { - if b == 1 { - term.boolAttributes[BoolAttr[i*2+1]] = true - } - } - // If the number of bytes read is not even, a byte for alignment is added - // We know the header is an even number of bytes so only need to check the - // total of the names and booleans. - if (header[1]+header[2])%2 != 0 { - err = binary.Read(file, binary.LittleEndian, make([]byte, 1)) - if err != nil { - return nil, err - } - } - - // Read in shorts - shArray = make([]int16, header[3]) - err = binary.Read(file, binary.LittleEndian, &shArray) - if err != nil { - return nil, err - } - term.numAttributes = make(map[string]int16) - for i, n := range shArray { - if n != 0377 && n > -1 { - term.numAttributes[NumAttr[i*2+1]] = n - } - } - - // Read the offsets into the short array - shArray = make([]int16, header[4]) - err = binary.Read(file, binary.LittleEndian, &shArray) - if err != nil { - return nil, err - } - // Read the actual strings in the byte array - byteArray = make([]byte, header[5]) - err = binary.Read(file, binary.LittleEndian, &byteArray) - if err != nil { - return nil, err - } - term.strAttributes = make(map[string]string) - // We get an offset, and then iterate until the string is null-terminated - for i, offset := range shArray { - if offset > -1 { - if int(offset) >= len(byteArray) { - return nil, errors.New("array out of bounds reading string section") - } - r := bytes.IndexByte(byteArray[offset:], 0) - if r == -1 { - return nil, errors.New("missing nul byte reading string section") - } - r += int(offset) - term.strAttributes[StrAttr[i*2+1]] = string(byteArray[offset:r]) - } - } - return &term, nil -} diff --git a/vendor/github.com/Nvveen/Gotty/parser.go b/vendor/github.com/Nvveen/Gotty/parser.go deleted file mode 100644 index a9d5d23c5425..000000000000 --- a/vendor/github.com/Nvveen/Gotty/parser.go +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright 2012 Neal van Veen. All rights reserved. -// Usage of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package gotty - -import ( - "bytes" - "errors" - "fmt" - "regexp" - "strconv" - "strings" -) - -var exp = [...]string{ - "%%", - "%c", - "%s", - "%p(\\d)", - "%P([A-z])", - "%g([A-z])", - "%'(.)'", - "%{([0-9]+)}", - "%l", - "%\\+|%-|%\\*|%/|%m", - "%&|%\\||%\\^", - "%=|%>|%<", - "%A|%O", - "%!|%~", - "%i", - "%(:[\\ #\\-\\+]{0,4})?(\\d+\\.\\d+|\\d+)?[doxXs]", - "%\\?(.*?);", -} - -var regex *regexp.Regexp -var staticVar map[byte]stacker - -// Parses the attribute that is received with name attr and parameters params. -func (term *TermInfo) Parse(attr string, params ...interface{}) (string, error) { - // Get the attribute name first. - iface, err := term.GetAttribute(attr) - str, ok := iface.(string) - if err != nil { - return "", err - } - if !ok { - return str, errors.New("Only string capabilities can be parsed.") - } - // Construct the hidden parser struct so we can use a recursive stack based - // parser. - ps := &parser{} - // Dynamic variables only exist in this context. - ps.dynamicVar = make(map[byte]stacker, 26) - ps.parameters = make([]stacker, len(params)) - // Convert the parameters to insert them into the parser struct. - for i, x := range params { - ps.parameters[i] = x - } - // Recursively walk and return. - result, err := ps.walk(str) - return result, err -} - -// Parses the attribute that is received with name attr and parameters params. -// Only works on full name of a capability that is given, which it uses to -// search for the termcap name. -func (term *TermInfo) ParseName(attr string, params ...interface{}) (string, error) { - tc := GetTermcapName(attr) - return term.Parse(tc, params) -} - -// Identify each token in a stack based manner and do the actual parsing. -func (ps *parser) walk(attr string) (string, error) { - // We use a buffer to get the modified string. - var buf bytes.Buffer - // Next, find and identify all tokens by their indices and strings. - tokens := regex.FindAllStringSubmatch(attr, -1) - if len(tokens) == 0 { - return attr, nil - } - indices := regex.FindAllStringIndex(attr, -1) - q := 0 // q counts the matches of one token - // Iterate through the string per character. - for i := 0; i < len(attr); i++ { - // If the current position is an identified token, execute the following - // steps. - if q < len(indices) && i >= indices[q][0] && i < indices[q][1] { - // Switch on token. - switch { - case tokens[q][0][:2] == "%%": - // Literal percentage character. - buf.WriteByte('%') - case tokens[q][0][:2] == "%c": - // Pop a character. - c, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - buf.WriteByte(c.(byte)) - case tokens[q][0][:2] == "%s": - // Pop a string. - str, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - if _, ok := str.(string); !ok { - return buf.String(), errors.New("Stack head is not a string") - } - buf.WriteString(str.(string)) - case tokens[q][0][:2] == "%p": - // Push a parameter on the stack. - index, err := strconv.ParseInt(tokens[q][1], 10, 8) - index-- - if err != nil { - return buf.String(), err - } - if int(index) >= len(ps.parameters) { - return buf.String(), errors.New("Parameters index out of bound") - } - ps.st.push(ps.parameters[index]) - case tokens[q][0][:2] == "%P": - // Pop a variable from the stack as a dynamic or static variable. - val, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - index := tokens[q][2] - if len(index) > 1 { - errorStr := fmt.Sprintf("%s is not a valid dynamic variables index", - index) - return buf.String(), errors.New(errorStr) - } - // Specify either dynamic or static. - if index[0] >= 'a' && index[0] <= 'z' { - ps.dynamicVar[index[0]] = val - } else if index[0] >= 'A' && index[0] <= 'Z' { - staticVar[index[0]] = val - } - case tokens[q][0][:2] == "%g": - // Push a variable from the stack as a dynamic or static variable. - index := tokens[q][3] - if len(index) > 1 { - errorStr := fmt.Sprintf("%s is not a valid static variables index", - index) - return buf.String(), errors.New(errorStr) - } - var val stacker - if index[0] >= 'a' && index[0] <= 'z' { - val = ps.dynamicVar[index[0]] - } else if index[0] >= 'A' && index[0] <= 'Z' { - val = staticVar[index[0]] - } - ps.st.push(val) - case tokens[q][0][:2] == "%'": - // Push a character constant. - con := tokens[q][4] - if len(con) > 1 { - errorStr := fmt.Sprintf("%s is not a valid character constant", con) - return buf.String(), errors.New(errorStr) - } - ps.st.push(con[0]) - case tokens[q][0][:2] == "%{": - // Push an integer constant. - con, err := strconv.ParseInt(tokens[q][5], 10, 32) - if err != nil { - return buf.String(), err - } - ps.st.push(con) - case tokens[q][0][:2] == "%l": - // Push the length of the string that is popped from the stack. - popStr, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - if _, ok := popStr.(string); !ok { - errStr := fmt.Sprintf("Stack head is not a string") - return buf.String(), errors.New(errStr) - } - ps.st.push(len(popStr.(string))) - case tokens[q][0][:2] == "%?": - // If-then-else construct. First, the whole string is identified and - // then inside this substring, we can specify which parts to switch on. - ifReg, _ := regexp.Compile("%\\?(.*)%t(.*)%e(.*);|%\\?(.*)%t(.*);") - ifTokens := ifReg.FindStringSubmatch(tokens[q][0]) - var ( - ifStr string - err error - ) - // Parse the if-part to determine if-else. - if len(ifTokens[1]) > 0 { - ifStr, err = ps.walk(ifTokens[1]) - } else { // else - ifStr, err = ps.walk(ifTokens[4]) - } - // Return any errors - if err != nil { - return buf.String(), err - } else if len(ifStr) > 0 { - // Self-defined limitation, not sure if this is correct, but didn't - // seem like it. - return buf.String(), errors.New("If-clause cannot print statements") - } - var thenStr string - // Pop the first value that is set by parsing the if-clause. - choose, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - // Switch to if or else. - if choose.(int) == 0 && len(ifTokens[1]) > 0 { - thenStr, err = ps.walk(ifTokens[3]) - } else if choose.(int) != 0 { - if len(ifTokens[1]) > 0 { - thenStr, err = ps.walk(ifTokens[2]) - } else { - thenStr, err = ps.walk(ifTokens[5]) - } - } - if err != nil { - return buf.String(), err - } - buf.WriteString(thenStr) - case tokens[q][0][len(tokens[q][0])-1] == 'd': // Fallthrough for printing - fallthrough - case tokens[q][0][len(tokens[q][0])-1] == 'o': // digits. - fallthrough - case tokens[q][0][len(tokens[q][0])-1] == 'x': - fallthrough - case tokens[q][0][len(tokens[q][0])-1] == 'X': - fallthrough - case tokens[q][0][len(tokens[q][0])-1] == 's': - token := tokens[q][0] - // Remove the : that comes before a flag. - if token[1] == ':' { - token = token[:1] + token[2:] - } - digit, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - // The rest is determined like the normal formatted prints. - digitStr := fmt.Sprintf(token, digit.(int)) - buf.WriteString(digitStr) - case tokens[q][0][:2] == "%i": - // Increment the parameters by one. - if len(ps.parameters) < 2 { - return buf.String(), errors.New("Not enough parameters to increment.") - } - val1, val2 := ps.parameters[0].(int), ps.parameters[1].(int) - val1++ - val2++ - ps.parameters[0], ps.parameters[1] = val1, val2 - default: - // The rest of the tokens is a special case, where two values are - // popped and then operated on by the token that comes after them. - op1, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - op2, err := ps.st.pop() - if err != nil { - return buf.String(), err - } - var result stacker - switch tokens[q][0][:2] { - case "%+": - // Addition - result = op2.(int) + op1.(int) - case "%-": - // Subtraction - result = op2.(int) - op1.(int) - case "%*": - // Multiplication - result = op2.(int) * op1.(int) - case "%/": - // Division - result = op2.(int) / op1.(int) - case "%m": - // Modulo - result = op2.(int) % op1.(int) - case "%&": - // Bitwise AND - result = op2.(int) & op1.(int) - case "%|": - // Bitwise OR - result = op2.(int) | op1.(int) - case "%^": - // Bitwise XOR - result = op2.(int) ^ op1.(int) - case "%=": - // Equals - result = op2 == op1 - case "%>": - // Greater-than - result = op2.(int) > op1.(int) - case "%<": - // Lesser-than - result = op2.(int) < op1.(int) - case "%A": - // Logical AND - result = op2.(bool) && op1.(bool) - case "%O": - // Logical OR - result = op2.(bool) || op1.(bool) - case "%!": - // Logical complement - result = !op1.(bool) - case "%~": - // Bitwise complement - result = ^(op1.(int)) - } - ps.st.push(result) - } - - i = indices[q][1] - 1 - q++ - } else { - // We are not "inside" a token, so just skip until the end or the next - // token, and add all characters to the buffer. - j := i - if q != len(indices) { - for !(j >= indices[q][0] && j < indices[q][1]) { - j++ - } - } else { - j = len(attr) - } - buf.WriteString(string(attr[i:j])) - i = j - } - } - // Return the buffer as a string. - return buf.String(), nil -} - -// Push a stacker-value onto the stack. -func (st *stack) push(s stacker) { - *st = append(*st, s) -} - -// Pop a stacker-value from the stack. -func (st *stack) pop() (stacker, error) { - if len(*st) == 0 { - return nil, errors.New("Stack is empty.") - } - newStack := make(stack, len(*st)-1) - val := (*st)[len(*st)-1] - copy(newStack, (*st)[:len(*st)-1]) - *st = newStack - return val, nil -} - -// Initialize regexes and the static vars (that don't get changed between -// calls. -func init() { - // Initialize the main regex. - expStr := strings.Join(exp[:], "|") - regex, _ = regexp.Compile(expStr) - // Initialize the static variables. - staticVar = make(map[byte]stacker, 26) -} diff --git a/vendor/github.com/Nvveen/Gotty/types.go b/vendor/github.com/Nvveen/Gotty/types.go deleted file mode 100644 index 9bcc65e9b889..000000000000 --- a/vendor/github.com/Nvveen/Gotty/types.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2012 Neal van Veen. All rights reserved. -// Usage of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package gotty - -type TermInfo struct { - boolAttributes map[string]bool - numAttributes map[string]int16 - strAttributes map[string]string - // The various names of the TermInfo file. - Names []string -} - -type stacker interface { -} -type stack []stacker - -type parser struct { - st stack - parameters []stacker - dynamicVar map[byte]stacker -} diff --git a/vendor/github.com/containerd/containerd/vendor.conf b/vendor/github.com/containerd/containerd/vendor.conf index c439d85cd903..25b1a01bd7a6 100644 --- a/vendor/github.com/containerd/containerd/vendor.conf +++ b/vendor/github.com/containerd/containerd/vendor.conf @@ -43,7 +43,7 @@ github.com/google/go-cmp v0.1.0 go.etcd.io/bbolt v1.3.1-etcd.8 # cri dependencies -github.com/containerd/cri 0ca1e3c2b73b5c38e72f29bb76338d0078b23d6c # release/1.2 branch +github.com/containerd/cri 0d5cabd006cb5319dc965046067b8432d9fa5ef8 # release/1.2 branch github.com/containerd/go-cni 40bcf8ec8acd7372be1d77031d585d5d8e561c90 github.com/blang/semver v3.1.0 github.com/containernetworking/cni v0.6.0 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 44bdfec962bc..05dd16a92568 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 @@ -370,9 +370,10 @@ type HostConfig struct { // Applicable to UNIX platforms CapAdd strslice.StrSlice // List of kernel capabilities to add to the container CapDrop strslice.StrSlice // List of kernel capabilities to remove from the container - DNS []string `json:"Dns"` // List of DNS server to lookup - DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for - DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for + Capabilities []string `json:"Capabilities"` // List of kernel capabilities to be available for container (this overrides the default set) + DNS []string `json:"Dns"` // List of DNS server to lookup + DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for + DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for ExtraHosts []string // List of extra hosts GroupAdd []string // List of additional groups that the container process will run as IpcMode IpcMode // IPC namespace to use for the container diff --git a/vendor/github.com/docker/docker/api/types/types.go b/vendor/github.com/docker/docker/api/types/types.go index 2accda9d0776..dabcd46056d2 100644 --- a/vendor/github.com/docker/docker/api/types/types.go +++ b/vendor/github.com/docker/docker/api/types/types.go @@ -146,6 +146,7 @@ type Commit struct { // GET "/info" type Info struct { ID string + Builder BuilderVersion Containers int ContainersRunning int ContainersPaused int diff --git a/vendor/github.com/docker/docker/client/README.md b/vendor/github.com/docker/docker/client/README.md index 059dfb3ce7b9..992f18117df5 100644 --- a/vendor/github.com/docker/docker/client/README.md +++ b/vendor/github.com/docker/docker/client/README.md @@ -16,7 +16,7 @@ import ( ) func main() { - cli, err := client.NewEnvClient() + cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { panic(err) } diff --git a/vendor/github.com/docker/docker/client/client.go b/vendor/github.com/docker/docker/client/client.go index fa66850c39a9..f174d3ce71fb 100644 --- a/vendor/github.com/docker/docker/client/client.go +++ b/vendor/github.com/docker/docker/client/client.go @@ -23,7 +23,7 @@ For example, to list running containers (the equivalent of "docker ps"): ) func main() { - cli, err := client.NewEnvClient() + cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { panic(err) } @@ -47,16 +47,13 @@ import ( "net" "net/http" "net/url" - "os" "path" - "path/filepath" "strings" "github.com/docker/docker/api" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/versions" "github.com/docker/go-connections/sockets" - "github.com/docker/go-connections/tlsconfig" "github.com/pkg/errors" ) @@ -103,145 +100,6 @@ func CheckRedirect(req *http.Request, via []*http.Request) error { return ErrRedirect } -// NewEnvClient initializes a new API client based on environment variables. -// See FromEnv for a list of support environment variables. -// -// Deprecated: use NewClientWithOpts(FromEnv) -func NewEnvClient() (*Client, error) { - return NewClientWithOpts(FromEnv) -} - -// FromEnv configures the client with values from environment variables. -// -// Supported environment variables: -// DOCKER_HOST to set the url to the docker server. -// DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest. -// DOCKER_CERT_PATH to load the TLS certificates from. -// DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default. -func FromEnv(c *Client) error { - if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" { - options := tlsconfig.Options{ - CAFile: filepath.Join(dockerCertPath, "ca.pem"), - CertFile: filepath.Join(dockerCertPath, "cert.pem"), - KeyFile: filepath.Join(dockerCertPath, "key.pem"), - InsecureSkipVerify: os.Getenv("DOCKER_TLS_VERIFY") == "", - } - tlsc, err := tlsconfig.Client(options) - if err != nil { - return err - } - - c.client = &http.Client{ - Transport: &http.Transport{TLSClientConfig: tlsc}, - CheckRedirect: CheckRedirect, - } - } - - if host := os.Getenv("DOCKER_HOST"); host != "" { - if err := WithHost(host)(c); err != nil { - return err - } - } - - if version := os.Getenv("DOCKER_API_VERSION"); version != "" { - c.version = version - c.manualOverride = true - } - return nil -} - -// WithTLSClientConfig applies a tls config to the client transport. -func WithTLSClientConfig(cacertPath, certPath, keyPath string) func(*Client) error { - return func(c *Client) error { - opts := tlsconfig.Options{ - CAFile: cacertPath, - CertFile: certPath, - KeyFile: keyPath, - ExclusiveRootPools: true, - } - config, err := tlsconfig.Client(opts) - if err != nil { - return errors.Wrap(err, "failed to create tls config") - } - if transport, ok := c.client.Transport.(*http.Transport); ok { - transport.TLSClientConfig = config - return nil - } - return errors.Errorf("cannot apply tls config to transport: %T", c.client.Transport) - } -} - -// WithDialer applies the dialer.DialContext to the client transport. This can be -// used to set the Timeout and KeepAlive settings of the client. -// Deprecated: use WithDialContext -func WithDialer(dialer *net.Dialer) func(*Client) error { - return WithDialContext(dialer.DialContext) -} - -// WithDialContext applies the dialer to the client transport. This can be -// used to set the Timeout and KeepAlive settings of the client. -func WithDialContext(dialContext func(ctx context.Context, network, addr string) (net.Conn, error)) func(*Client) error { - return func(c *Client) error { - if transport, ok := c.client.Transport.(*http.Transport); ok { - transport.DialContext = dialContext - return nil - } - return errors.Errorf("cannot apply dialer to transport: %T", c.client.Transport) - } -} - -// WithVersion overrides the client version with the specified one -func WithVersion(version string) func(*Client) error { - return func(c *Client) error { - c.version = version - return nil - } -} - -// WithHost overrides the client host with the specified one. -func WithHost(host string) func(*Client) error { - return func(c *Client) error { - hostURL, err := ParseHostURL(host) - if err != nil { - return err - } - c.host = host - c.proto = hostURL.Scheme - c.addr = hostURL.Host - c.basePath = hostURL.Path - if transport, ok := c.client.Transport.(*http.Transport); ok { - return sockets.ConfigureTransport(transport, c.proto, c.addr) - } - return errors.Errorf("cannot apply host to transport: %T", c.client.Transport) - } -} - -// WithHTTPClient overrides the client http client with the specified one -func WithHTTPClient(client *http.Client) func(*Client) error { - return func(c *Client) error { - if client != nil { - c.client = client - } - return nil - } -} - -// WithHTTPHeaders overrides the client default http headers -func WithHTTPHeaders(headers map[string]string) func(*Client) error { - return func(c *Client) error { - c.customHTTPHeaders = headers - return nil - } -} - -// WithScheme overrides the client scheme with the specified one -func WithScheme(scheme string) func(*Client) error { - return func(c *Client) error { - c.scheme = scheme - return nil - } -} - // NewClientWithOpts initializes a new API client with default values. It takes functors // to modify values when creating it, like `NewClientWithOpts(WithVersion(…))` // It also initializes the custom http headers to add to each request. @@ -301,18 +159,6 @@ func defaultHTTPClient(host string) (*http.Client, error) { }, nil } -// NewClient initializes a new API client for the given host and API version. -// It uses the given http client as transport. -// It also initializes the custom http headers to add to each request. -// -// It won't send any version information if the version number is empty. It is -// highly recommended that you set a version or your client may break if the -// server is upgraded. -// Deprecated: use NewClientWithOpts -func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error) { - return NewClientWithOpts(WithHost(host), WithVersion(version), WithHTTPClient(client), WithHTTPHeaders(httpHeaders)) -} - // Close the transport used by the client func (cli *Client) Close() error { if t, ok := cli.client.Transport.(*http.Transport); ok { diff --git a/vendor/github.com/docker/docker/client/client_deprecated.go b/vendor/github.com/docker/docker/client/client_deprecated.go new file mode 100644 index 000000000000..54cdfc29a84b --- /dev/null +++ b/vendor/github.com/docker/docker/client/client_deprecated.go @@ -0,0 +1,23 @@ +package client + +import "net/http" + +// NewClient initializes a new API client for the given host and API version. +// It uses the given http client as transport. +// It also initializes the custom http headers to add to each request. +// +// It won't send any version information if the version number is empty. It is +// highly recommended that you set a version or your client may break if the +// server is upgraded. +// Deprecated: use NewClientWithOpts +func NewClient(host string, version string, client *http.Client, httpHeaders map[string]string) (*Client, error) { + return NewClientWithOpts(WithHost(host), WithVersion(version), WithHTTPClient(client), WithHTTPHeaders(httpHeaders)) +} + +// NewEnvClient initializes a new API client based on environment variables. +// See FromEnv for a list of support environment variables. +// +// Deprecated: use NewClientWithOpts(FromEnv) +func NewEnvClient() (*Client, error) { + return NewClientWithOpts(FromEnv) +} diff --git a/vendor/github.com/docker/docker/client/options.go b/vendor/github.com/docker/docker/client/options.go new file mode 100644 index 000000000000..12eb25b18ee2 --- /dev/null +++ b/vendor/github.com/docker/docker/client/options.go @@ -0,0 +1,144 @@ +package client + +import ( + "context" + "net" + "net/http" + "os" + "path/filepath" + + "github.com/docker/go-connections/sockets" + "github.com/docker/go-connections/tlsconfig" + "github.com/pkg/errors" +) + +// FromEnv configures the client with values from environment variables. +// +// Supported environment variables: +// DOCKER_HOST to set the url to the docker server. +// DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest. +// DOCKER_CERT_PATH to load the TLS certificates from. +// DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default. +func FromEnv(c *Client) error { + if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" { + options := tlsconfig.Options{ + CAFile: filepath.Join(dockerCertPath, "ca.pem"), + CertFile: filepath.Join(dockerCertPath, "cert.pem"), + KeyFile: filepath.Join(dockerCertPath, "key.pem"), + InsecureSkipVerify: os.Getenv("DOCKER_TLS_VERIFY") == "", + } + tlsc, err := tlsconfig.Client(options) + if err != nil { + return err + } + + c.client = &http.Client{ + Transport: &http.Transport{TLSClientConfig: tlsc}, + CheckRedirect: CheckRedirect, + } + } + + if host := os.Getenv("DOCKER_HOST"); host != "" { + if err := WithHost(host)(c); err != nil { + return err + } + } + + if version := os.Getenv("DOCKER_API_VERSION"); version != "" { + c.version = version + c.manualOverride = true + } + return nil +} + +// WithDialer applies the dialer.DialContext to the client transport. This can be +// used to set the Timeout and KeepAlive settings of the client. +// Deprecated: use WithDialContext +func WithDialer(dialer *net.Dialer) func(*Client) error { + return WithDialContext(dialer.DialContext) +} + +// WithDialContext applies the dialer to the client transport. This can be +// used to set the Timeout and KeepAlive settings of the client. +func WithDialContext(dialContext func(ctx context.Context, network, addr string) (net.Conn, error)) func(*Client) error { + return func(c *Client) error { + if transport, ok := c.client.Transport.(*http.Transport); ok { + transport.DialContext = dialContext + return nil + } + return errors.Errorf("cannot apply dialer to transport: %T", c.client.Transport) + } +} + +// WithHost overrides the client host with the specified one. +func WithHost(host string) func(*Client) error { + return func(c *Client) error { + hostURL, err := ParseHostURL(host) + if err != nil { + return err + } + c.host = host + c.proto = hostURL.Scheme + c.addr = hostURL.Host + c.basePath = hostURL.Path + if transport, ok := c.client.Transport.(*http.Transport); ok { + return sockets.ConfigureTransport(transport, c.proto, c.addr) + } + return errors.Errorf("cannot apply host to transport: %T", c.client.Transport) + } +} + +// WithHTTPClient overrides the client http client with the specified one +func WithHTTPClient(client *http.Client) func(*Client) error { + return func(c *Client) error { + if client != nil { + c.client = client + } + return nil + } +} + +// WithHTTPHeaders overrides the client default http headers +func WithHTTPHeaders(headers map[string]string) func(*Client) error { + return func(c *Client) error { + c.customHTTPHeaders = headers + return nil + } +} + +// WithScheme overrides the client scheme with the specified one +func WithScheme(scheme string) func(*Client) error { + return func(c *Client) error { + c.scheme = scheme + return nil + } +} + +// WithTLSClientConfig applies a tls config to the client transport. +func WithTLSClientConfig(cacertPath, certPath, keyPath string) func(*Client) error { + return func(c *Client) error { + opts := tlsconfig.Options{ + CAFile: cacertPath, + CertFile: certPath, + KeyFile: keyPath, + ExclusiveRootPools: true, + } + config, err := tlsconfig.Client(opts) + if err != nil { + return errors.Wrap(err, "failed to create tls config") + } + if transport, ok := c.client.Transport.(*http.Transport); ok { + transport.TLSClientConfig = config + return nil + } + return errors.Errorf("cannot apply tls config to transport: %T", c.client.Transport) + } +} + +// WithVersion overrides the client version with the specified one +func WithVersion(version string) func(*Client) error { + return func(c *Client) error { + c.version = version + return nil + } +} diff --git a/vendor/github.com/docker/docker/client/ping.go b/vendor/github.com/docker/docker/client/ping.go index dec1423e3891..0ebb6b752bb2 100644 --- a/vendor/github.com/docker/docker/client/ping.go +++ b/vendor/github.com/docker/docker/client/ping.go @@ -2,34 +2,56 @@ package client // import "github.com/docker/docker/client" import ( "context" + "net/http" "path" "github.com/docker/docker/api/types" ) -// Ping pings the server and returns the value of the "Docker-Experimental", "Builder-Version", "OS-Type" & "API-Version" headers +// Ping pings the server and returns the value of the "Docker-Experimental", +// "Builder-Version", "OS-Type" & "API-Version" headers. It attempts to use +// a HEAD request on the endpoint, but falls back to GET if HEAD is not supported +// by the daemon. func (cli *Client) Ping(ctx context.Context) (types.Ping, error) { var ping types.Ping - req, err := cli.buildRequest("GET", path.Join(cli.basePath, "/_ping"), nil, nil) + req, err := cli.buildRequest("HEAD", path.Join(cli.basePath, "/_ping"), nil, nil) if err != nil { return ping, err } serverResp, err := cli.doRequest(ctx, req) + if err == nil { + defer ensureReaderClosed(serverResp) + switch serverResp.statusCode { + case http.StatusOK, http.StatusInternalServerError: + // Server handled the request, so parse the response + return parsePingResponse(cli, serverResp) + } + } + + req, err = cli.buildRequest("GET", path.Join(cli.basePath, "/_ping"), nil, nil) + if err != nil { + return ping, err + } + serverResp, err = cli.doRequest(ctx, req) if err != nil { return ping, err } defer ensureReaderClosed(serverResp) + return parsePingResponse(cli, serverResp) +} - if serverResp.header != nil { - ping.APIVersion = serverResp.header.Get("API-Version") - - if serverResp.header.Get("Docker-Experimental") == "true" { - ping.Experimental = true - } - ping.OSType = serverResp.header.Get("OSType") - if bv := serverResp.header.Get("Builder-Version"); bv != "" { - ping.BuilderVersion = types.BuilderVersion(bv) - } +func parsePingResponse(cli *Client, resp serverResponse) (types.Ping, error) { + var ping types.Ping + if resp.header == nil { + return ping, cli.checkResponseErr(resp) + } + ping.APIVersion = resp.header.Get("API-Version") + ping.OSType = resp.header.Get("OSType") + if resp.header.Get("Docker-Experimental") == "true" { + ping.Experimental = true + } + if bv := resp.header.Get("Builder-Version"); bv != "" { + ping.BuilderVersion = types.BuilderVersion(bv) } - return ping, cli.checkResponseErr(serverResp) + return ping, cli.checkResponseErr(resp) } diff --git a/vendor/github.com/docker/docker/client/request.go b/vendor/github.com/docker/docker/client/request.go index bf98962fcbac..52ed12446da2 100644 --- a/vendor/github.com/docker/docker/client/request.go +++ b/vendor/github.com/docker/docker/client/request.go @@ -136,7 +136,7 @@ func (cli *Client) doRequest(ctx context.Context, req *http.Request) (serverResp } if cli.scheme == "https" && strings.Contains(err.Error(), "bad certificate") { - return serverResp, fmt.Errorf("The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings: %v", err) + return serverResp, errors.Wrap(err, "The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings") } // Don't decorate context sentinel errors; users may be comparing to @@ -195,17 +195,21 @@ func (cli *Client) checkResponseErr(serverResp serverResponse) error { return nil } - bodyMax := 1 * 1024 * 1024 // 1 MiB - bodyR := &io.LimitedReader{ - R: serverResp.body, - N: int64(bodyMax), - } - body, err := ioutil.ReadAll(bodyR) - if err != nil { - return err - } - if bodyR.N == 0 { - return fmt.Errorf("request returned %s with a message (> %d bytes) for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), bodyMax, serverResp.reqURL) + var body []byte + var err error + if serverResp.body != nil { + bodyMax := 1 * 1024 * 1024 // 1 MiB + bodyR := &io.LimitedReader{ + R: serverResp.body, + N: int64(bodyMax), + } + body, err = ioutil.ReadAll(bodyR) + if err != nil { + return err + } + if bodyR.N == 0 { + return fmt.Errorf("request returned %s with a message (> %d bytes) for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), bodyMax, serverResp.reqURL) + } } if len(body) == 0 { return fmt.Errorf("request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), serverResp.reqURL) @@ -220,14 +224,14 @@ func (cli *Client) checkResponseErr(serverResp serverResponse) error { if (cli.version == "" || versions.GreaterThan(cli.version, "1.23")) && ct == "application/json" { var errorResponse types.ErrorResponse if err := json.Unmarshal(body, &errorResponse); err != nil { - return fmt.Errorf("Error reading JSON: %v", err) + return errors.Wrap(err, "Error reading JSON") } - errorMessage = errorResponse.Message + errorMessage = strings.TrimSpace(errorResponse.Message) } else { - errorMessage = string(body) + errorMessage = strings.TrimSpace(string(body)) } - return fmt.Errorf("Error response from daemon: %s", strings.TrimSpace(errorMessage)) + return errors.Wrap(errors.New(errorMessage), "Error response from daemon") } func (cli *Client) addHeaders(req *http.Request, headers headers) *http.Request { diff --git a/vendor/github.com/docker/docker/errdefs/helpers.go b/vendor/github.com/docker/docker/errdefs/helpers.go index 6169c2bc62a5..a28881caf5be 100644 --- a/vendor/github.com/docker/docker/errdefs/helpers.go +++ b/vendor/github.com/docker/docker/errdefs/helpers.go @@ -12,8 +12,8 @@ func (e errNotFound) Cause() error { // NotFound is a helper to create an error of the class with the same name from any error type func NotFound(err error) error { - if err == nil { - return nil + if err == nil || IsNotFound(err) { + return err } return errNotFound{err} } @@ -28,8 +28,8 @@ func (e errInvalidParameter) Cause() error { // InvalidParameter is a helper to create an error of the class with the same name from any error type func InvalidParameter(err error) error { - if err == nil { - return nil + if err == nil || IsInvalidParameter(err) { + return err } return errInvalidParameter{err} } @@ -44,8 +44,8 @@ func (e errConflict) Cause() error { // Conflict is a helper to create an error of the class with the same name from any error type func Conflict(err error) error { - if err == nil { - return nil + if err == nil || IsConflict(err) { + return err } return errConflict{err} } @@ -60,8 +60,8 @@ func (e errUnauthorized) Cause() error { // Unauthorized is a helper to create an error of the class with the same name from any error type func Unauthorized(err error) error { - if err == nil { - return nil + if err == nil || IsUnauthorized(err) { + return err } return errUnauthorized{err} } @@ -76,6 +76,9 @@ func (e errUnavailable) Cause() error { // Unavailable is a helper to create an error of the class with the same name from any error type func Unavailable(err error) error { + if err == nil || IsUnavailable(err) { + return err + } return errUnavailable{err} } @@ -89,8 +92,8 @@ func (e errForbidden) Cause() error { // Forbidden is a helper to create an error of the class with the same name from any error type func Forbidden(err error) error { - if err == nil { - return nil + if err == nil || IsForbidden(err) { + return err } return errForbidden{err} } @@ -105,8 +108,8 @@ func (e errSystem) Cause() error { // System is a helper to create an error of the class with the same name from any error type func System(err error) error { - if err == nil { - return nil + if err == nil || IsSystem(err) { + return err } return errSystem{err} } @@ -121,8 +124,8 @@ func (e errNotModified) Cause() error { // NotModified is a helper to create an error of the class with the same name from any error type func NotModified(err error) error { - if err == nil { - return nil + if err == nil || IsNotModified(err) { + return err } return errNotModified{err} } @@ -137,8 +140,8 @@ func (e errAlreadyExists) Cause() error { // AlreadyExists is a helper to create an error of the class with the same name from any error type func AlreadyExists(err error) error { - if err == nil { - return nil + if err == nil || IsAlreadyExists(err) { + return err } return errAlreadyExists{err} } @@ -153,8 +156,8 @@ func (e errNotImplemented) Cause() error { // NotImplemented is a helper to create an error of the class with the same name from any error type func NotImplemented(err error) error { - if err == nil { - return nil + if err == nil || IsNotImplemented(err) { + return err } return errNotImplemented{err} } @@ -169,8 +172,8 @@ func (e errUnknown) Cause() error { // Unknown is a helper to create an error of the class with the same name from any error type func Unknown(err error) error { - if err == nil { - return nil + if err == nil || IsUnknown(err) { + return err } return errUnknown{err} } @@ -185,8 +188,8 @@ func (e errCancelled) Cause() error { // Cancelled is a helper to create an error of the class with the same name from any error type func Cancelled(err error) error { - if err == nil { - return nil + if err == nil || IsCancelled(err) { + return err } return errCancelled{err} } @@ -201,8 +204,8 @@ func (e errDeadline) Cause() error { // Deadline is a helper to create an error of the class with the same name from any error type func Deadline(err error) error { - if err == nil { - return nil + if err == nil || IsDeadline(err) { + return err } return errDeadline{err} } @@ -217,8 +220,8 @@ func (e errDataLoss) Cause() error { // DataLoss is a helper to create an error of the class with the same name from any error type func DataLoss(err error) error { - if err == nil { - return nil + if err == nil || IsDataLoss(err) { + return err } return errDataLoss{err} } diff --git a/vendor/github.com/docker/docker/pkg/archive/archive.go b/vendor/github.com/docker/docker/pkg/archive/archive.go index 32132fcfed81..bb623fa85648 100644 --- a/vendor/github.com/docker/docker/pkg/archive/archive.go +++ b/vendor/github.com/docker/docker/pkg/archive/archive.go @@ -745,7 +745,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) compressWriter, options.ChownOpts, ) - ta.WhiteoutConverter = getWhiteoutConverter(options.WhiteoutFormat) + ta.WhiteoutConverter = getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS) defer func() { // Make sure to check the error on Close. @@ -903,7 +903,7 @@ func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) err var dirs []*tar.Header idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps) rootIDs := idMapping.RootPair() - whiteoutConverter := getWhiteoutConverter(options.WhiteoutFormat) + whiteoutConverter := getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS) // Iterate through the files in the archive. loop: diff --git a/vendor/github.com/docker/docker/pkg/archive/archive_linux.go b/vendor/github.com/docker/docker/pkg/archive/archive_linux.go index 970d4d06800d..0601f7b0d1f5 100644 --- a/vendor/github.com/docker/docker/pkg/archive/archive_linux.go +++ b/vendor/github.com/docker/docker/pkg/archive/archive_linux.go @@ -2,22 +2,29 @@ package archive // import "github.com/docker/docker/pkg/archive" import ( "archive/tar" + "fmt" + "io/ioutil" "os" "path/filepath" "strings" + "syscall" + "github.com/containerd/continuity/fs" "github.com/docker/docker/pkg/system" + "github.com/pkg/errors" "golang.org/x/sys/unix" ) -func getWhiteoutConverter(format WhiteoutFormat) tarWhiteoutConverter { +func getWhiteoutConverter(format WhiteoutFormat, inUserNS bool) tarWhiteoutConverter { if format == OverlayWhiteoutFormat { - return overlayWhiteoutConverter{} + return overlayWhiteoutConverter{inUserNS: inUserNS} } return nil } -type overlayWhiteoutConverter struct{} +type overlayWhiteoutConverter struct { + inUserNS bool +} func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, err error) { // convert whiteouts to AUFS format @@ -61,13 +68,22 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os return } -func (overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, error) { +func (c overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, error) { base := filepath.Base(path) dir := filepath.Dir(path) // if a directory is marked as opaque by the AUFS special file, we need to translate that to overlay if base == WhiteoutOpaqueDir { err := unix.Setxattr(dir, "trusted.overlay.opaque", []byte{'y'}, 0) + if err != nil { + if c.inUserNS { + if err = replaceDirWithOverlayOpaque(dir); err != nil { + return false, errors.Wrapf(err, "replaceDirWithOverlayOpaque(%q) failed", dir) + } + } else { + return false, errors.Wrapf(err, "setxattr(%q, trusted.overlay.opaque=y)", dir) + } + } // don't write the file itself return false, err } @@ -78,7 +94,19 @@ func (overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, originalPath := filepath.Join(dir, originalBase) if err := unix.Mknod(originalPath, unix.S_IFCHR, 0); err != nil { - return false, err + if c.inUserNS { + // Ubuntu and a few distros support overlayfs in userns. + // + // Although we can't call mknod directly in userns (at least on bionic kernel 4.15), + // we can still create 0,0 char device using mknodChar0Overlay(). + // + // NOTE: we don't need this hack for the containerd snapshotter+unpack model. + if err := mknodChar0Overlay(originalPath); err != nil { + return false, errors.Wrapf(err, "failed to mknodChar0UserNS(%q)", originalPath) + } + } else { + return false, errors.Wrapf(err, "failed to mknod(%q, S_IFCHR, 0)", originalPath) + } } if err := os.Chown(originalPath, hdr.Uid, hdr.Gid); err != nil { return false, err @@ -90,3 +118,144 @@ func (overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, return true, nil } + +// mknodChar0Overlay creates 0,0 char device by mounting overlayfs and unlinking. +// This function can be used for creating 0,0 char device in userns on Ubuntu. +// +// Steps: +// * Mkdir lower,upper,merged,work +// * Create lower/dummy +// * Mount overlayfs +// * Unlink merged/dummy +// * Unmount overlayfs +// * Make sure a 0,0 char device is created as upper/dummy +// * Rename upper/dummy to cleansedOriginalPath +func mknodChar0Overlay(cleansedOriginalPath string) error { + dir := filepath.Dir(cleansedOriginalPath) + tmp, err := ioutil.TempDir(dir, "mc0o") + if err != nil { + return errors.Wrapf(err, "failed to create a tmp directory under %s", dir) + } + defer os.RemoveAll(tmp) + lower := filepath.Join(tmp, "l") + upper := filepath.Join(tmp, "u") + work := filepath.Join(tmp, "w") + merged := filepath.Join(tmp, "m") + for _, s := range []string{lower, upper, work, merged} { + if err := os.MkdirAll(s, 0700); err != nil { + return errors.Wrapf(err, "failed to mkdir %s", s) + } + } + dummyBase := "d" + lowerDummy := filepath.Join(lower, dummyBase) + if err := ioutil.WriteFile(lowerDummy, []byte{}, 0600); err != nil { + return errors.Wrapf(err, "failed to create a dummy lower file %s", lowerDummy) + } + mOpts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lower, upper, work) + // docker/pkg/mount.Mount() requires procfs to be mounted. So we use syscall.Mount() directly instead. + if err := syscall.Mount("overlay", merged, "overlay", uintptr(0), mOpts); err != nil { + return errors.Wrapf(err, "failed to mount overlay (%s) on %s", mOpts, merged) + } + mergedDummy := filepath.Join(merged, dummyBase) + if err := os.Remove(mergedDummy); err != nil { + syscall.Unmount(merged, 0) + return errors.Wrapf(err, "failed to unlink %s", mergedDummy) + } + if err := syscall.Unmount(merged, 0); err != nil { + return errors.Wrapf(err, "failed to unmount %s", merged) + } + upperDummy := filepath.Join(upper, dummyBase) + if err := isChar0(upperDummy); err != nil { + return err + } + if err := os.Rename(upperDummy, cleansedOriginalPath); err != nil { + return errors.Wrapf(err, "failed to rename %s to %s", upperDummy, cleansedOriginalPath) + } + return nil +} + +func isChar0(path string) error { + osStat, err := os.Stat(path) + if err != nil { + return errors.Wrapf(err, "failed to stat %s", path) + } + st, ok := osStat.Sys().(*syscall.Stat_t) + if !ok { + return errors.Errorf("got unsupported stat for %s", path) + } + if os.FileMode(st.Mode)&syscall.S_IFMT != syscall.S_IFCHR { + return errors.Errorf("%s is not a character device, got mode=%d", path, st.Mode) + } + if st.Rdev != 0 { + return errors.Errorf("%s is not a 0,0 character device, got Rdev=%d", path, st.Rdev) + } + return nil +} + +// replaceDirWithOverlayOpaque replaces path with a new directory with trusted.overlay.opaque +// xattr. The contents of the directory are preserved. +func replaceDirWithOverlayOpaque(path string) error { + if path == "/" { + return errors.New("replaceDirWithOverlayOpaque: path must not be \"/\"") + } + dir := filepath.Dir(path) + tmp, err := ioutil.TempDir(dir, "rdwoo") + if err != nil { + return errors.Wrapf(err, "failed to create a tmp directory under %s", dir) + } + defer os.RemoveAll(tmp) + // newPath is a new empty directory crafted with trusted.overlay.opaque xattr. + // we copy the content of path into newPath, remove path, and rename newPath to path. + newPath, err := createDirWithOverlayOpaque(tmp) + if err != nil { + return errors.Wrapf(err, "createDirWithOverlayOpaque(%q) failed", tmp) + } + if err := fs.CopyDir(newPath, path); err != nil { + return errors.Wrapf(err, "CopyDir(%q, %q) failed", newPath, path) + } + if err := os.RemoveAll(path); err != nil { + return err + } + return os.Rename(newPath, path) +} + +// createDirWithOverlayOpaque creates a directory with trusted.overlay.opaque xattr, +// without calling setxattr, so as to allow creating opaque dir in userns on Ubuntu. +func createDirWithOverlayOpaque(tmp string) (string, error) { + lower := filepath.Join(tmp, "l") + upper := filepath.Join(tmp, "u") + work := filepath.Join(tmp, "w") + merged := filepath.Join(tmp, "m") + for _, s := range []string{lower, upper, work, merged} { + if err := os.MkdirAll(s, 0700); err != nil { + return "", errors.Wrapf(err, "failed to mkdir %s", s) + } + } + dummyBase := "d" + lowerDummy := filepath.Join(lower, dummyBase) + if err := os.MkdirAll(lowerDummy, 0700); err != nil { + return "", errors.Wrapf(err, "failed to create a dummy lower directory %s", lowerDummy) + } + mOpts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lower, upper, work) + // docker/pkg/mount.Mount() requires procfs to be mounted. So we use syscall.Mount() directly instead. + if err := syscall.Mount("overlay", merged, "overlay", uintptr(0), mOpts); err != nil { + return "", errors.Wrapf(err, "failed to mount overlay (%s) on %s", mOpts, merged) + } + mergedDummy := filepath.Join(merged, dummyBase) + if err := os.Remove(mergedDummy); err != nil { + syscall.Unmount(merged, 0) + return "", errors.Wrapf(err, "failed to rmdir %s", mergedDummy) + } + // upperDummy becomes a 0,0-char device file here + if err := os.Mkdir(mergedDummy, 0700); err != nil { + syscall.Unmount(merged, 0) + return "", errors.Wrapf(err, "failed to mkdir %s", mergedDummy) + } + // upperDummy becomes a directory with trusted.overlay.opaque xattr + // (but can't be verified in userns) + if err := syscall.Unmount(merged, 0); err != nil { + return "", errors.Wrapf(err, "failed to unmount %s", merged) + } + upperDummy := filepath.Join(upper, dummyBase) + return upperDummy, nil +} diff --git a/vendor/github.com/docker/docker/pkg/archive/archive_other.go b/vendor/github.com/docker/docker/pkg/archive/archive_other.go index 462dfc632325..65a73354c429 100644 --- a/vendor/github.com/docker/docker/pkg/archive/archive_other.go +++ b/vendor/github.com/docker/docker/pkg/archive/archive_other.go @@ -2,6 +2,6 @@ package archive // import "github.com/docker/docker/pkg/archive" -func getWhiteoutConverter(format WhiteoutFormat) tarWhiteoutConverter { +func getWhiteoutConverter(format WhiteoutFormat, inUserNS bool) tarWhiteoutConverter { return nil } diff --git a/vendor/github.com/docker/docker/pkg/archive/copy.go b/vendor/github.com/docker/docker/pkg/archive/copy.go index d0f13ca79beb..57fddac078da 100644 --- a/vendor/github.com/docker/docker/pkg/archive/copy.go +++ b/vendor/github.com/docker/docker/pkg/archive/copy.go @@ -336,6 +336,14 @@ func RebaseArchiveEntries(srcContent io.Reader, oldBase, newBase string) io.Read return } + // srcContent tar stream, as served by TarWithOptions(), is + // definitely in PAX format, but tar.Next() mistakenly guesses it + // as USTAR, which creates a problem: if the newBase is >100 + // characters long, WriteHeader() returns an error like + // "archive/tar: cannot encode header: Format specifies USTAR; and USTAR cannot encode Name=...". + // + // To fix, set the format to PAX here. See docker/for-linux issue #484. + hdr.Format = tar.FormatPAX hdr.Name = strings.Replace(hdr.Name, oldBase, newBase, 1) if hdr.Typeflag == tar.TypeLink { hdr.Linkname = strings.Replace(hdr.Linkname, oldBase, newBase, 1) diff --git a/vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go b/vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go index dd95f367041e..a68b566cea2c 100644 --- a/vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go +++ b/vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go @@ -4,13 +4,12 @@ import ( "encoding/json" "fmt" "io" - "os" "strings" "time" - "github.com/Nvveen/Gotty" "github.com/docker/docker/pkg/term" "github.com/docker/go-units" + "github.com/morikuni/aec" ) // RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to @@ -151,60 +150,23 @@ type JSONMessage struct { Aux *json.RawMessage `json:"aux,omitempty"` } -/* Satisfied by gotty.TermInfo as well as noTermInfo from below */ -type termInfo interface { - Parse(attr string, params ...interface{}) (string, error) +func clearLine(out io.Writer) { + eraseMode := aec.EraseModes.All + cl := aec.EraseLine(eraseMode) + fmt.Fprint(out, cl) } -type noTermInfo struct{} // canary used when no terminfo. - -func (ti *noTermInfo) Parse(attr string, params ...interface{}) (string, error) { - return "", fmt.Errorf("noTermInfo") -} - -func clearLine(out io.Writer, ti termInfo) { - // el2 (clear whole line) is not exposed by terminfo. - - // First clear line from beginning to cursor - if attr, err := ti.Parse("el1"); err == nil { - fmt.Fprintf(out, "%s", attr) - } else { - fmt.Fprintf(out, "\x1b[1K") - } - // Then clear line from cursor to end - if attr, err := ti.Parse("el"); err == nil { - fmt.Fprintf(out, "%s", attr) - } else { - fmt.Fprintf(out, "\x1b[K") - } -} - -func cursorUp(out io.Writer, ti termInfo, l int) { - if l == 0 { // Should never be the case, but be tolerant - return - } - if attr, err := ti.Parse("cuu", l); err == nil { - fmt.Fprintf(out, "%s", attr) - } else { - fmt.Fprintf(out, "\x1b[%dA", l) - } +func cursorUp(out io.Writer, l uint) { + fmt.Fprint(out, aec.Up(l)) } -func cursorDown(out io.Writer, ti termInfo, l int) { - if l == 0 { // Should never be the case, but be tolerant - return - } - if attr, err := ti.Parse("cud", l); err == nil { - fmt.Fprintf(out, "%s", attr) - } else { - fmt.Fprintf(out, "\x1b[%dB", l) - } +func cursorDown(out io.Writer, l uint) { + fmt.Fprint(out, aec.Down(l)) } -// Display displays the JSONMessage to `out`. `termInfo` is non-nil if `out` -// is a terminal. If this is the case, it will erase the entire current line -// when displaying the progressbar. -func (jm *JSONMessage) Display(out io.Writer, termInfo termInfo) error { +// Display displays the JSONMessage to `out`. If `isTerminal` is true, it will erase the +// entire current line when displaying the progressbar. +func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error { if jm.Error != nil { if jm.Error.Code == 401 { return fmt.Errorf("authentication is required") @@ -212,8 +174,8 @@ func (jm *JSONMessage) Display(out io.Writer, termInfo termInfo) error { return jm.Error } var endl string - if termInfo != nil && jm.Stream == "" && jm.Progress != nil { - clearLine(out, termInfo) + if isTerminal && jm.Stream == "" && jm.Progress != nil { + clearLine(out) endl = "\r" fmt.Fprintf(out, endl) } else if jm.Progress != nil && jm.Progress.String() != "" { //disable progressbar in non-terminal @@ -230,7 +192,7 @@ func (jm *JSONMessage) Display(out io.Writer, termInfo termInfo) error { if jm.From != "" { fmt.Fprintf(out, "(from %s) ", jm.From) } - if jm.Progress != nil && termInfo != nil { + if jm.Progress != nil && isTerminal { fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl) } else if jm.ProgressMessage != "" { //deprecated fmt.Fprintf(out, "%s %s%s", jm.Status, jm.ProgressMessage, endl) @@ -248,25 +210,11 @@ func (jm *JSONMessage) Display(out io.Writer, termInfo termInfo) error { func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(JSONMessage)) error { var ( dec = json.NewDecoder(in) - ids = make(map[string]int) + ids = make(map[string]uint) ) - var termInfo termInfo - - if isTerminal { - term := os.Getenv("TERM") - if term == "" { - term = "vt102" - } - - var err error - if termInfo, err = gotty.OpenTermInfo(term); err != nil { - termInfo = &noTermInfo{} - } - } - for { - diff := 0 + var diff uint var jm JSONMessage if err := dec.Decode(&jm); err != nil { if err == io.EOF { @@ -294,15 +242,15 @@ func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, // when we output something that's not // accounted for in the map, such as a line // with no ID. - line = len(ids) + line = uint(len(ids)) ids[jm.ID] = line - if termInfo != nil { + if isTerminal { fmt.Fprintf(out, "\n") } } - diff = len(ids) - line - if termInfo != nil { - cursorUp(out, termInfo, diff) + diff = uint(len(ids)) - line + if isTerminal { + cursorUp(out, diff) } } else { // When outputting something that isn't progress @@ -310,11 +258,11 @@ func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, // don't want progress entries from some previous // operation to be updated (for example, pull -a // with multiple tags). - ids = make(map[string]int) + ids = make(map[string]uint) } - err := jm.Display(out, termInfo) - if jm.ID != "" && termInfo != nil { - cursorDown(out, termInfo, diff) + err := jm.Display(out, isTerminal) + if jm.ID != "" && isTerminal { + cursorDown(out, diff) } if err != nil { return err diff --git a/vendor/github.com/docker/docker/vendor.conf b/vendor/github.com/docker/docker/vendor.conf index 2d3ca977da05..3ee53cf556a3 100644 --- a/vendor/github.com/docker/docker/vendor.conf +++ b/vendor/github.com/docker/docker/vendor.conf @@ -26,7 +26,7 @@ github.com/imdario/mergo v0.3.6 golang.org/x/sync 1d60e4601c6fd243af51cc01ddf169918a5407ca # buildkit -github.com/moby/buildkit d9f75920678e35090025bb89344c5370e2efc8e7 +github.com/moby/buildkit 34ff9c2366a878ada7938d2f9ede71741b0a220c github.com/tonistiigi/fsutil 2862f6bc5ac9b97124e552a5c108230b38a1b0ca github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746 github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7 @@ -118,19 +118,19 @@ github.com/googleapis/gax-go v2.0.0 google.golang.org/genproto 694d95ba50e67b2e363f3483057db5d4910c18f9 # containerd -github.com/containerd/containerd aa5e000c963756778ab3ebd1a12c67449c503a34 # v1.2.1+ +github.com/containerd/containerd 9754871865f7fe2f4e74d43e2fc7ccd237edcbce # v1.2.2 github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c github.com/containerd/continuity 004b46473808b3e7a4a3049c20e4376c91eb966d github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2 github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 -github.com/containerd/cri 0ca1e3c2b73b5c38e72f29bb76338d0078b23d6c # release/1.2 branch +github.com/containerd/cri 0d5cabd006cb5319dc965046067b8432d9fa5ef8 # release/1.2 branch github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3 github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 github.com/containerd/ttrpc 2a805f71863501300ae1976d29f0454ae003e85a github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef # cluster -github.com/docker/swarmkit 8af8c420f491f006ab1730e08d446a795b1667d7 +github.com/docker/swarmkit ebfb0aa1118ebfd35a224d72a5d337ce0addd907 github.com/gogo/protobuf v1.0.0 github.com/cloudflare/cfssl 1.3.2 github.com/fernet/fernet-go 1b2437bc582b3cfbb341ee5a29f8ef5b42912ff2 @@ -155,7 +155,7 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.1 github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 # v1.0 -github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github.com/ijc25/Gotty +github.com/morikuni/aec 39771216ff4c63d11f5e604076f9c45e8be1067b # metrics github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18 diff --git a/vendor/github.com/docker/swarmkit/api/ca.pb.go b/vendor/github.com/docker/swarmkit/api/ca.pb.go index 23d375f95ec9..206ba213a5e8 100644 --- a/vendor/github.com/docker/swarmkit/api/ca.pb.go +++ b/vendor/github.com/docker/swarmkit/api/ca.pb.go @@ -205,6 +205,7 @@ EncryptionKey ManagerStatus FileTarget + RuntimeTarget SecretReference ConfigReference BlacklistedCertificate diff --git a/vendor/github.com/docker/swarmkit/api/types.pb.go b/vendor/github.com/docker/swarmkit/api/types.pb.go index 0fef5d78534c..124f64dbe64f 100644 --- a/vendor/github.com/docker/swarmkit/api/types.pb.go +++ b/vendor/github.com/docker/swarmkit/api/types.pb.go @@ -603,7 +603,7 @@ func (x MaybeEncryptedRecord_Algorithm) String() string { return proto.EnumName(MaybeEncryptedRecord_Algorithm_name, int32(x)) } func (MaybeEncryptedRecord_Algorithm) EnumDescriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{52, 0} + return fileDescriptorTypes, []int{53, 0} } // Version tracks the last time an object in the store was updated. @@ -952,6 +952,8 @@ func (*Mount) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{1 type Mount_BindOptions struct { // Propagation mode of mount. Propagation Mount_BindOptions_MountPropagation `protobuf:"varint,1,opt,name=propagation,proto3,enum=docker.swarmkit.v1.Mount_BindOptions_MountPropagation" json:"propagation,omitempty"` + // allows non-recursive bind-mount, i.e. mount(2) with "bind" rather than "rbind". + NonRecursive bool `protobuf:"varint,2,opt,name=nonrecursive,proto3" json:"nonrecursive,omitempty"` } func (m *Mount_BindOptions) Reset() { *m = Mount_BindOptions{} } @@ -1717,6 +1719,17 @@ func (m *FileTarget) Reset() { *m = FileTarget{} } func (*FileTarget) ProtoMessage() {} func (*FileTarget) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{47} } +// RuntimeTarget represents that this secret is _not_ mounted into the +// container, but is used for some other purpose by the container runtime. +// +// Currently, RuntimeTarget has no fields; it's just a placeholder. +type RuntimeTarget struct { +} + +func (m *RuntimeTarget) Reset() { *m = RuntimeTarget{} } +func (*RuntimeTarget) ProtoMessage() {} +func (*RuntimeTarget) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{48} } + // SecretReference is the linkage between a service and a secret that it uses. type SecretReference struct { // SecretID represents the ID of the specific Secret that we're @@ -1735,7 +1748,7 @@ type SecretReference struct { func (m *SecretReference) Reset() { *m = SecretReference{} } func (*SecretReference) ProtoMessage() {} -func (*SecretReference) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{48} } +func (*SecretReference) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{49} } type isSecretReference_Target interface { isSecretReference_Target() @@ -1826,16 +1839,17 @@ type ConfigReference struct { // ConfigName is the name of the config that this references, but this is just provided for // lookup/display purposes. The config in the reference will be identified by its ID. ConfigName string `protobuf:"bytes,2,opt,name=config_name,json=configName,proto3" json:"config_name,omitempty"` - // Target specifies how this secret should be exposed to the task. + // Target specifies how this config should be exposed to the task. // // Types that are valid to be assigned to Target: // *ConfigReference_File + // *ConfigReference_Runtime Target isConfigReference_Target `protobuf_oneof:"target"` } func (m *ConfigReference) Reset() { *m = ConfigReference{} } func (*ConfigReference) ProtoMessage() {} -func (*ConfigReference) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{49} } +func (*ConfigReference) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{50} } type isConfigReference_Target interface { isConfigReference_Target() @@ -1846,8 +1860,12 @@ type isConfigReference_Target interface { type ConfigReference_File struct { File *FileTarget `protobuf:"bytes,3,opt,name=file,oneof"` } +type ConfigReference_Runtime struct { + Runtime *RuntimeTarget `protobuf:"bytes,4,opt,name=runtime,oneof"` +} -func (*ConfigReference_File) isConfigReference_Target() {} +func (*ConfigReference_File) isConfigReference_Target() {} +func (*ConfigReference_Runtime) isConfigReference_Target() {} func (m *ConfigReference) GetTarget() isConfigReference_Target { if m != nil { @@ -1863,10 +1881,18 @@ func (m *ConfigReference) GetFile() *FileTarget { return nil } +func (m *ConfigReference) GetRuntime() *RuntimeTarget { + if x, ok := m.GetTarget().(*ConfigReference_Runtime); ok { + return x.Runtime + } + return nil +} + // XXX_OneofFuncs is for the internal use of the proto package. func (*ConfigReference) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { return _ConfigReference_OneofMarshaler, _ConfigReference_OneofUnmarshaler, _ConfigReference_OneofSizer, []interface{}{ (*ConfigReference_File)(nil), + (*ConfigReference_Runtime)(nil), } } @@ -1879,6 +1905,11 @@ func _ConfigReference_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { if err := b.EncodeMessage(x.File); err != nil { return err } + case *ConfigReference_Runtime: + _ = b.EncodeVarint(4<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.Runtime); err != nil { + return err + } case nil: default: return fmt.Errorf("ConfigReference.Target has unexpected type %T", x) @@ -1897,6 +1928,14 @@ func _ConfigReference_OneofUnmarshaler(msg proto.Message, tag, wire int, b *prot err := b.DecodeMessage(msg) m.Target = &ConfigReference_File{msg} return true, err + case 4: // target.runtime + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(RuntimeTarget) + err := b.DecodeMessage(msg) + m.Target = &ConfigReference_Runtime{msg} + return true, err default: return false, nil } @@ -1911,6 +1950,11 @@ func _ConfigReference_OneofSizer(msg proto.Message) (n int) { n += proto.SizeVarint(3<<3 | proto.WireBytes) n += proto.SizeVarint(uint64(s)) n += s + case *ConfigReference_Runtime: + s := proto.Size(x.Runtime) + n += proto.SizeVarint(4<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s case nil: default: panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) @@ -1929,7 +1973,7 @@ type BlacklistedCertificate struct { func (m *BlacklistedCertificate) Reset() { *m = BlacklistedCertificate{} } func (*BlacklistedCertificate) ProtoMessage() {} -func (*BlacklistedCertificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{50} } +func (*BlacklistedCertificate) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{51} } // HealthConfig holds configuration settings for the HEALTHCHECK feature. type HealthConfig struct { @@ -1959,7 +2003,7 @@ type HealthConfig struct { func (m *HealthConfig) Reset() { *m = HealthConfig{} } func (*HealthConfig) ProtoMessage() {} -func (*HealthConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{51} } +func (*HealthConfig) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{52} } type MaybeEncryptedRecord struct { Algorithm MaybeEncryptedRecord_Algorithm `protobuf:"varint,1,opt,name=algorithm,proto3,enum=docker.swarmkit.v1.MaybeEncryptedRecord_Algorithm" json:"algorithm,omitempty"` @@ -1969,7 +2013,7 @@ type MaybeEncryptedRecord struct { func (m *MaybeEncryptedRecord) Reset() { *m = MaybeEncryptedRecord{} } func (*MaybeEncryptedRecord) ProtoMessage() {} -func (*MaybeEncryptedRecord) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{52} } +func (*MaybeEncryptedRecord) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{53} } type RootRotation struct { CACert []byte `protobuf:"bytes,1,opt,name=ca_cert,json=caCert,proto3" json:"ca_cert,omitempty"` @@ -1980,7 +2024,7 @@ type RootRotation struct { func (m *RootRotation) Reset() { *m = RootRotation{} } func (*RootRotation) ProtoMessage() {} -func (*RootRotation) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{53} } +func (*RootRotation) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{54} } // Privileges specifies security configuration/permissions. type Privileges struct { @@ -1990,20 +2034,21 @@ type Privileges struct { func (m *Privileges) Reset() { *m = Privileges{} } func (*Privileges) ProtoMessage() {} -func (*Privileges) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{54} } +func (*Privileges) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{55} } // CredentialSpec for managed service account (Windows only). type Privileges_CredentialSpec struct { // Types that are valid to be assigned to Source: // *Privileges_CredentialSpec_File // *Privileges_CredentialSpec_Registry + // *Privileges_CredentialSpec_Config Source isPrivileges_CredentialSpec_Source `protobuf_oneof:"source"` } func (m *Privileges_CredentialSpec) Reset() { *m = Privileges_CredentialSpec{} } func (*Privileges_CredentialSpec) ProtoMessage() {} func (*Privileges_CredentialSpec) Descriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{54, 0} + return fileDescriptorTypes, []int{55, 0} } type isPrivileges_CredentialSpec_Source interface { @@ -2018,9 +2063,13 @@ type Privileges_CredentialSpec_File struct { type Privileges_CredentialSpec_Registry struct { Registry string `protobuf:"bytes,2,opt,name=registry,proto3,oneof"` } +type Privileges_CredentialSpec_Config struct { + Config string `protobuf:"bytes,3,opt,name=config,proto3,oneof"` +} func (*Privileges_CredentialSpec_File) isPrivileges_CredentialSpec_Source() {} func (*Privileges_CredentialSpec_Registry) isPrivileges_CredentialSpec_Source() {} +func (*Privileges_CredentialSpec_Config) isPrivileges_CredentialSpec_Source() {} func (m *Privileges_CredentialSpec) GetSource() isPrivileges_CredentialSpec_Source { if m != nil { @@ -2043,11 +2092,19 @@ func (m *Privileges_CredentialSpec) GetRegistry() string { return "" } +func (m *Privileges_CredentialSpec) GetConfig() string { + if x, ok := m.GetSource().(*Privileges_CredentialSpec_Config); ok { + return x.Config + } + return "" +} + // XXX_OneofFuncs is for the internal use of the proto package. func (*Privileges_CredentialSpec) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { return _Privileges_CredentialSpec_OneofMarshaler, _Privileges_CredentialSpec_OneofUnmarshaler, _Privileges_CredentialSpec_OneofSizer, []interface{}{ (*Privileges_CredentialSpec_File)(nil), (*Privileges_CredentialSpec_Registry)(nil), + (*Privileges_CredentialSpec_Config)(nil), } } @@ -2061,6 +2118,9 @@ func _Privileges_CredentialSpec_OneofMarshaler(msg proto.Message, b *proto.Buffe case *Privileges_CredentialSpec_Registry: _ = b.EncodeVarint(2<<3 | proto.WireBytes) _ = b.EncodeStringBytes(x.Registry) + case *Privileges_CredentialSpec_Config: + _ = b.EncodeVarint(3<<3 | proto.WireBytes) + _ = b.EncodeStringBytes(x.Config) case nil: default: return fmt.Errorf("Privileges_CredentialSpec.Source has unexpected type %T", x) @@ -2085,6 +2145,13 @@ func _Privileges_CredentialSpec_OneofUnmarshaler(msg proto.Message, tag, wire in x, err := b.DecodeStringBytes() m.Source = &Privileges_CredentialSpec_Registry{x} return true, err + case 3: // source.config + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeStringBytes() + m.Source = &Privileges_CredentialSpec_Config{x} + return true, err default: return false, nil } @@ -2102,6 +2169,10 @@ func _Privileges_CredentialSpec_OneofSizer(msg proto.Message) (n int) { n += proto.SizeVarint(2<<3 | proto.WireBytes) n += proto.SizeVarint(uint64(len(x.Registry))) n += len(x.Registry) + case *Privileges_CredentialSpec_Config: + n += proto.SizeVarint(3<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(len(x.Config))) + n += len(x.Config) case nil: default: panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) @@ -2121,7 +2192,7 @@ type Privileges_SELinuxContext struct { func (m *Privileges_SELinuxContext) Reset() { *m = Privileges_SELinuxContext{} } func (*Privileges_SELinuxContext) ProtoMessage() {} func (*Privileges_SELinuxContext) Descriptor() ([]byte, []int) { - return fileDescriptorTypes, []int{54, 1} + return fileDescriptorTypes, []int{55, 1} } func init() { @@ -2178,6 +2249,7 @@ func init() { proto.RegisterType((*EncryptionKey)(nil), "docker.swarmkit.v1.EncryptionKey") proto.RegisterType((*ManagerStatus)(nil), "docker.swarmkit.v1.ManagerStatus") proto.RegisterType((*FileTarget)(nil), "docker.swarmkit.v1.FileTarget") + proto.RegisterType((*RuntimeTarget)(nil), "docker.swarmkit.v1.RuntimeTarget") proto.RegisterType((*SecretReference)(nil), "docker.swarmkit.v1.SecretReference") proto.RegisterType((*ConfigReference)(nil), "docker.swarmkit.v1.ConfigReference") proto.RegisterType((*BlacklistedCertificate)(nil), "docker.swarmkit.v1.BlacklistedCertificate") @@ -3327,6 +3399,16 @@ func (m *FileTarget) CopyFrom(src interface{}) { *m = *o } +func (m *RuntimeTarget) Copy() *RuntimeTarget { + if m == nil { + return nil + } + o := &RuntimeTarget{} + o.CopyFrom(m) + return o +} + +func (m *RuntimeTarget) CopyFrom(src interface{}) {} func (m *SecretReference) Copy() *SecretReference { if m == nil { return nil @@ -3374,6 +3456,12 @@ func (m *ConfigReference) CopyFrom(src interface{}) { } deepcopy.Copy(v.File, o.GetFile()) m.Target = &v + case *ConfigReference_Runtime: + v := ConfigReference_Runtime{ + Runtime: &RuntimeTarget{}, + } + deepcopy.Copy(v.Runtime, o.GetRuntime()) + m.Target = &v } } @@ -3528,6 +3616,11 @@ func (m *Privileges_CredentialSpec) CopyFrom(src interface{}) { Registry: o.GetRegistry(), } m.Source = &v + case *Privileges_CredentialSpec_Config: + v := Privileges_CredentialSpec_Config{ + Config: o.GetConfig(), + } + m.Source = &v } } @@ -4265,6 +4358,16 @@ func (m *Mount_BindOptions) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintTypes(dAtA, i, uint64(m.Propagation)) } + if m.NonRecursive { + dAtA[i] = 0x10 + i++ + if m.NonRecursive { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } return i, nil } @@ -5763,6 +5866,24 @@ func (m *FileTarget) MarshalTo(dAtA []byte) (int, error) { return i, nil } +func (m *RuntimeTarget) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RuntimeTarget) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + return i, nil +} + func (m *SecretReference) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -5865,6 +5986,20 @@ func (m *ConfigReference_File) MarshalTo(dAtA []byte) (int, error) { } return i, nil } +func (m *ConfigReference_Runtime) MarshalTo(dAtA []byte) (int, error) { + i := 0 + if m.Runtime != nil { + dAtA[i] = 0x22 + i++ + i = encodeVarintTypes(dAtA, i, uint64(m.Runtime.Size())) + n40, err := m.Runtime.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n40 + } + return i, nil +} func (m *BlacklistedCertificate) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -5884,11 +6019,11 @@ func (m *BlacklistedCertificate) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.Expiry.Size())) - n40, err := m.Expiry.MarshalTo(dAtA[i:]) + n41, err := m.Expiry.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n40 + i += n41 } return i, nil } @@ -5927,21 +6062,21 @@ func (m *HealthConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x12 i++ i = encodeVarintTypes(dAtA, i, uint64(m.Interval.Size())) - n41, err := m.Interval.MarshalTo(dAtA[i:]) + n42, err := m.Interval.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n41 + i += n42 } if m.Timeout != nil { dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.Timeout.Size())) - n42, err := m.Timeout.MarshalTo(dAtA[i:]) + n43, err := m.Timeout.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n42 + i += n43 } if m.Retries != 0 { dAtA[i] = 0x20 @@ -5952,11 +6087,11 @@ func (m *HealthConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x2a i++ i = encodeVarintTypes(dAtA, i, uint64(m.StartPeriod.Size())) - n43, err := m.StartPeriod.MarshalTo(dAtA[i:]) + n44, err := m.StartPeriod.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n43 + i += n44 } return i, nil } @@ -6051,21 +6186,21 @@ func (m *Privileges) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.CredentialSpec.Size())) - n44, err := m.CredentialSpec.MarshalTo(dAtA[i:]) + n45, err := m.CredentialSpec.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n44 + i += n45 } if m.SELinuxContext != nil { dAtA[i] = 0x12 i++ i = encodeVarintTypes(dAtA, i, uint64(m.SELinuxContext.Size())) - n45, err := m.SELinuxContext.MarshalTo(dAtA[i:]) + n46, err := m.SELinuxContext.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n45 + i += n46 } return i, nil } @@ -6086,11 +6221,11 @@ func (m *Privileges_CredentialSpec) MarshalTo(dAtA []byte) (int, error) { var l int _ = l if m.Source != nil { - nn46, err := m.Source.MarshalTo(dAtA[i:]) + nn47, err := m.Source.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += nn46 + i += nn47 } return i, nil } @@ -6111,6 +6246,14 @@ func (m *Privileges_CredentialSpec_Registry) MarshalTo(dAtA []byte) (int, error) i += copy(dAtA[i:], m.Registry) return i, nil } +func (m *Privileges_CredentialSpec_Config) MarshalTo(dAtA []byte) (int, error) { + i := 0 + dAtA[i] = 0x1a + i++ + i = encodeVarintTypes(dAtA, i, uint64(len(m.Config))) + i += copy(dAtA[i:], m.Config) + return i, nil +} func (m *Privileges_SELinuxContext) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -6489,6 +6632,9 @@ func (m *Mount_BindOptions) Size() (n int) { if m.Propagation != 0 { n += 1 + sovTypes(uint64(m.Propagation)) } + if m.NonRecursive { + n += 2 + } return n } @@ -7154,6 +7300,12 @@ func (m *FileTarget) Size() (n int) { return n } +func (m *RuntimeTarget) Size() (n int) { + var l int + _ = l + return n +} + func (m *SecretReference) Size() (n int) { var l int _ = l @@ -7206,6 +7358,15 @@ func (m *ConfigReference_File) Size() (n int) { } return n } +func (m *ConfigReference_Runtime) Size() (n int) { + var l int + _ = l + if m.Runtime != nil { + l = m.Runtime.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} func (m *BlacklistedCertificate) Size() (n int) { var l int _ = l @@ -7315,6 +7476,13 @@ func (m *Privileges_CredentialSpec_Registry) Size() (n int) { n += 1 + l + sovTypes(uint64(l)) return n } +func (m *Privileges_CredentialSpec_Config) Size() (n int) { + var l int + _ = l + l = len(m.Config) + n += 1 + l + sovTypes(uint64(l)) + return n +} func (m *Privileges_SELinuxContext) Size() (n int) { var l int _ = l @@ -7599,6 +7767,7 @@ func (this *Mount_BindOptions) String() string { } s := strings.Join([]string{`&Mount_BindOptions{`, `Propagation:` + fmt.Sprintf("%v", this.Propagation) + `,`, + `NonRecursive:` + fmt.Sprintf("%v", this.NonRecursive) + `,`, `}`, }, "") return s @@ -8099,6 +8268,15 @@ func (this *FileTarget) String() string { }, "") return s } +func (this *RuntimeTarget) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RuntimeTarget{`, + `}`, + }, "") + return s +} func (this *SecretReference) String() string { if this == nil { return "nil" @@ -8143,6 +8321,16 @@ func (this *ConfigReference_File) String() string { }, "") return s } +func (this *ConfigReference_Runtime) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ConfigReference_Runtime{`, + `Runtime:` + strings.Replace(fmt.Sprintf("%v", this.Runtime), "RuntimeTarget", "RuntimeTarget", 1) + `,`, + `}`, + }, "") + return s +} func (this *BlacklistedCertificate) String() string { if this == nil { return "nil" @@ -8232,6 +8420,16 @@ func (this *Privileges_CredentialSpec_Registry) String() string { }, "") return s } +func (this *Privileges_CredentialSpec_Config) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Privileges_CredentialSpec_Config{`, + `Config:` + fmt.Sprintf("%v", this.Config) + `,`, + `}`, + }, "") + return s +} func (this *Privileges_SELinuxContext) String() string { if this == nil { return "nil" @@ -10669,6 +10867,26 @@ func (m *Mount_BindOptions) Unmarshal(dAtA []byte) error { break } } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NonRecursive", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.NonRecursive = bool(v != 0) default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -15803,6 +16021,56 @@ func (m *FileTarget) Unmarshal(dAtA []byte) error { } return nil } +func (m *RuntimeTarget) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RuntimeTarget: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RuntimeTarget: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *SecretReference) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -16062,6 +16330,38 @@ func (m *ConfigReference) Unmarshal(dAtA []byte) error { } m.Target = &ConfigReference_File{v} iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Runtime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &RuntimeTarget{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Target = &ConfigReference_Runtime{v} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -16840,6 +17140,35 @@ func (m *Privileges_CredentialSpec) Unmarshal(dAtA []byte) error { } m.Source = &Privileges_CredentialSpec_Registry{string(dAtA[iNdEx:postIndex])} iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Source = &Privileges_CredentialSpec_Config{string(dAtA[iNdEx:postIndex])} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -17155,327 +17484,332 @@ var ( func init() { proto.RegisterFile("github.com/docker/swarmkit/api/types.proto", fileDescriptorTypes) } var fileDescriptorTypes = []byte{ - // 5152 bytes of a gzipped FileDescriptorProto + // 5218 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x5a, 0x4d, 0x70, 0x23, 0x49, 0x56, 0xb6, 0x64, 0x49, 0x96, 0x9e, 0x64, 0xbb, 0x3a, 0xdb, 0xdb, 0xe3, 0xd6, 0xf6, 0xd8, 0x9a, - 0x9a, 0xe9, 0x9d, 0xd9, 0xde, 0x41, 0xfd, 0xb7, 0xbb, 0xd1, 0x33, 0xc3, 0xee, 0x8c, 0x7e, 0xca, + 0x9a, 0xe9, 0x9d, 0xd9, 0xde, 0x41, 0xfd, 0x37, 0xbb, 0xd1, 0x33, 0xc3, 0xec, 0x8c, 0x7e, 0xca, 0xb6, 0xb6, 0x6d, 0x49, 0x91, 0x92, 0xbb, 0x77, 0x89, 0x80, 0xa2, 0x5c, 0x95, 0x96, 0x6b, 0x5c, - 0xaa, 0x14, 0x55, 0x25, 0xbb, 0xc5, 0x42, 0x30, 0xc1, 0x01, 0x08, 0x9f, 0xe0, 0x02, 0xbb, 0x41, - 0x98, 0x20, 0x02, 0x6e, 0x7b, 0xe0, 0xc0, 0x81, 0x80, 0xd3, 0x10, 0x41, 0x10, 0x1b, 0x5c, 0x60, - 0x21, 0x02, 0x36, 0x20, 0xc2, 0xb0, 0x3e, 0x70, 0x23, 0xe0, 0x42, 0x70, 0xe1, 0x40, 0xe4, 0x4f, - 0x95, 0xca, 0xea, 0xb2, 0xdd, 0xc3, 0xec, 0xc5, 0x56, 0xbe, 0xf7, 0xbd, 0x97, 0x99, 0x2f, 0x33, - 0x5f, 0xbe, 0xf7, 0xb2, 0xe0, 0xde, 0xc0, 0x0e, 0x0e, 0xc6, 0x7b, 0x55, 0x93, 0x0e, 0xef, 0x5b, - 0xd4, 0x3c, 0x24, 0xde, 0x7d, 0xff, 0xd8, 0xf0, 0x86, 0x87, 0x76, 0x70, 0xdf, 0x18, 0xd9, 0xf7, - 0x83, 0xc9, 0x88, 0xf8, 0xd5, 0x91, 0x47, 0x03, 0x8a, 0x90, 0x00, 0x54, 0x43, 0x40, 0xf5, 0xe8, - 0x61, 0x79, 0x7d, 0x40, 0xe9, 0xc0, 0x21, 0xf7, 0x39, 0x62, 0x6f, 0xbc, 0x7f, 0x3f, 0xb0, 0x87, - 0xc4, 0x0f, 0x8c, 0xe1, 0x48, 0x08, 0x95, 0xd7, 0x66, 0x01, 0xd6, 0xd8, 0x33, 0x02, 0x9b, 0xba, - 0x92, 0xbf, 0x32, 0xa0, 0x03, 0xca, 0x7f, 0xde, 0x67, 0xbf, 0x04, 0x55, 0x5d, 0x87, 0x85, 0x67, - 0xc4, 0xf3, 0x6d, 0xea, 0xa2, 0x15, 0xc8, 0xda, 0xae, 0x45, 0x5e, 0xac, 0xa6, 0x2a, 0xa9, 0x77, - 0x32, 0x58, 0x34, 0xd4, 0x07, 0x00, 0x2d, 0xf6, 0x43, 0x73, 0x03, 0x6f, 0x82, 0x14, 0x98, 0x3f, - 0x24, 0x13, 0x8e, 0x28, 0x60, 0xf6, 0x93, 0x51, 0x8e, 0x0c, 0x67, 0x35, 0x2d, 0x28, 0x47, 0x86, - 0xa3, 0xfe, 0x24, 0x05, 0xc5, 0x9a, 0xeb, 0xd2, 0x80, 0xf7, 0xee, 0x23, 0x04, 0x19, 0xd7, 0x18, - 0x12, 0x29, 0xc4, 0x7f, 0xa3, 0x06, 0xe4, 0x1c, 0x63, 0x8f, 0x38, 0xfe, 0x6a, 0xba, 0x32, 0xff, - 0x4e, 0xf1, 0xd1, 0x57, 0xaa, 0x2f, 0x4f, 0xb9, 0x1a, 0x53, 0x52, 0xdd, 0xe6, 0x68, 0x3e, 0x08, - 0x2c, 0x45, 0xd1, 0x37, 0x61, 0xc1, 0x76, 0x2d, 0xdb, 0x24, 0xfe, 0x6a, 0x86, 0x6b, 0x59, 0x4b, - 0xd2, 0x32, 0x1d, 0x7d, 0x3d, 0xf3, 0xc3, 0xb3, 0xf5, 0x39, 0x1c, 0x0a, 0x95, 0xdf, 0x83, 0x62, - 0x4c, 0x6d, 0xc2, 0xdc, 0x56, 0x20, 0x7b, 0x64, 0x38, 0x63, 0x22, 0x67, 0x27, 0x1a, 0xef, 0xa7, - 0x9f, 0xa4, 0xd4, 0x8f, 0x60, 0xa5, 0x6d, 0x0c, 0x89, 0xb5, 0x49, 0x5c, 0xe2, 0xd9, 0x26, 0x26, - 0x3e, 0x1d, 0x7b, 0x26, 0x61, 0x73, 0x3d, 0xb4, 0x5d, 0x2b, 0x9c, 0x2b, 0xfb, 0x9d, 0xac, 0x45, - 0x6d, 0xc0, 0x6b, 0x4d, 0xdb, 0x37, 0x3d, 0x12, 0x90, 0xcf, 0xac, 0x64, 0x3e, 0x54, 0x72, 0x96, - 0x82, 0xe5, 0x59, 0xe9, 0x9f, 0x83, 0x9b, 0xcc, 0xc4, 0x96, 0xee, 0x49, 0x8a, 0xee, 0x8f, 0x88, - 0xc9, 0x95, 0x15, 0x1f, 0xbd, 0x93, 0x64, 0xa1, 0xa4, 0x99, 0x6c, 0xcd, 0xe1, 0x1b, 0x5c, 0x4d, - 0x48, 0xe8, 0x8d, 0x88, 0x89, 0x4c, 0xb8, 0x65, 0xc9, 0x41, 0xcf, 0xa8, 0x4f, 0x73, 0xf5, 0x89, - 0xcb, 0x78, 0xc9, 0x34, 0xb7, 0xe6, 0xf0, 0x4a, 0xa8, 0x2c, 0xde, 0x49, 0x1d, 0x20, 0x1f, 0xea, - 0x56, 0xbf, 0x97, 0x82, 0x42, 0xc8, 0xf4, 0xd1, 0x97, 0xa1, 0xe0, 0x1a, 0x2e, 0xd5, 0xcd, 0xd1, - 0xd8, 0xe7, 0x13, 0x9a, 0xaf, 0x97, 0xce, 0xcf, 0xd6, 0xf3, 0x6d, 0xc3, 0xa5, 0x8d, 0xee, 0xae, - 0x8f, 0xf3, 0x8c, 0xdd, 0x18, 0x8d, 0x7d, 0xf4, 0x06, 0x94, 0x86, 0x64, 0x48, 0xbd, 0x89, 0xbe, - 0x37, 0x09, 0x88, 0x2f, 0xcd, 0x56, 0x14, 0xb4, 0x3a, 0x23, 0xa1, 0x6f, 0xc0, 0xc2, 0x40, 0x0c, - 0x69, 0x75, 0x9e, 0x6f, 0x9f, 0x37, 0x93, 0x46, 0x3f, 0x33, 0x6a, 0x1c, 0xca, 0xa8, 0xbf, 0x9d, - 0x82, 0x95, 0x88, 0x4a, 0x7e, 0x69, 0x6c, 0x7b, 0x64, 0x48, 0xdc, 0xc0, 0x47, 0x5f, 0x83, 0x9c, - 0x63, 0x0f, 0xed, 0xc0, 0x97, 0x36, 0x7f, 0x3d, 0x49, 0x6d, 0x34, 0x29, 0x2c, 0xc1, 0xa8, 0x06, - 0x25, 0x8f, 0xf8, 0xc4, 0x3b, 0x12, 0x3b, 0x5e, 0x5a, 0xf4, 0x1a, 0xe1, 0x0b, 0x22, 0xea, 0x06, - 0xe4, 0xbb, 0x8e, 0x11, 0xec, 0x53, 0x6f, 0x88, 0x54, 0x28, 0x19, 0x9e, 0x79, 0x60, 0x07, 0xc4, - 0x0c, 0xc6, 0x5e, 0x78, 0xfa, 0x2e, 0xd0, 0xd0, 0x2d, 0x48, 0x53, 0xd1, 0x51, 0xa1, 0x9e, 0x3b, - 0x3f, 0x5b, 0x4f, 0x77, 0x7a, 0x38, 0x4d, 0x7d, 0xf5, 0x03, 0xb8, 0xd1, 0x75, 0xc6, 0x03, 0xdb, - 0x6d, 0x12, 0xdf, 0xf4, 0xec, 0x11, 0xd3, 0xce, 0x76, 0x25, 0xf3, 0x51, 0xe1, 0xae, 0x64, 0xbf, - 0xa3, 0xa3, 0x9d, 0x9e, 0x1e, 0x6d, 0xf5, 0x37, 0xd3, 0x70, 0x43, 0x73, 0x07, 0xb6, 0x4b, 0xe2, - 0xd2, 0x77, 0x61, 0x89, 0x70, 0xa2, 0x7e, 0x24, 0xdc, 0x8d, 0xd4, 0xb3, 0x28, 0xa8, 0xa1, 0x0f, - 0x6a, 0xcd, 0xf8, 0x85, 0x87, 0x49, 0xd3, 0x7f, 0x49, 0x7b, 0xa2, 0x77, 0xd0, 0x60, 0x61, 0xc4, - 0x27, 0xe1, 0xcb, 0xe5, 0xbd, 0x9b, 0xa4, 0xeb, 0xa5, 0x79, 0x86, 0x4e, 0x42, 0xca, 0x7e, 0x1e, - 0x27, 0xf1, 0x37, 0x69, 0x58, 0x6e, 0x53, 0xeb, 0x82, 0x1d, 0xca, 0x90, 0x3f, 0xa0, 0x7e, 0x10, - 0x73, 0x88, 0x51, 0x1b, 0x3d, 0x81, 0xfc, 0x48, 0x2e, 0x9f, 0x5c, 0xfd, 0x3b, 0xc9, 0x43, 0x16, - 0x18, 0x1c, 0xa1, 0xd1, 0x07, 0x50, 0x08, 0x8f, 0x0c, 0x9b, 0xed, 0x2b, 0x6c, 0x9c, 0x29, 0x1e, - 0x7d, 0x03, 0x72, 0x62, 0x11, 0x56, 0x33, 0x5c, 0xf2, 0xee, 0x2b, 0xd9, 0x1c, 0x4b, 0x21, 0xb4, - 0x09, 0xf9, 0xc0, 0xf1, 0x75, 0xdb, 0xdd, 0xa7, 0xab, 0x59, 0xae, 0x60, 0x3d, 0xd1, 0xc9, 0x50, - 0x8b, 0xf4, 0xb7, 0x7b, 0x2d, 0x77, 0x9f, 0xd6, 0x8b, 0xe7, 0x67, 0xeb, 0x0b, 0xb2, 0x81, 0x17, - 0x02, 0xc7, 0x67, 0x3f, 0xd0, 0x1d, 0xc8, 0xec, 0xdb, 0x23, 0x7f, 0x35, 0x57, 0x49, 0xbd, 0x93, - 0xaf, 0xe7, 0xcf, 0xcf, 0xd6, 0x33, 0x1b, 0xad, 0x6e, 0x0f, 0x73, 0xaa, 0xfa, 0x3b, 0x29, 0x28, - 0xc6, 0x74, 0xa0, 0xd7, 0x01, 0x02, 0x6f, 0xec, 0x07, 0xba, 0x47, 0x69, 0xc0, 0x4d, 0x59, 0xc2, - 0x05, 0x4e, 0xc1, 0x94, 0x06, 0xa8, 0x0a, 0x37, 0x4d, 0xe2, 0x05, 0xba, 0xed, 0xfb, 0x63, 0xe2, - 0xe9, 0xfe, 0x78, 0xef, 0x63, 0x62, 0x06, 0xdc, 0xac, 0x25, 0x7c, 0x83, 0xb1, 0x5a, 0x9c, 0xd3, - 0x13, 0x0c, 0xf4, 0x18, 0x6e, 0xc5, 0xf1, 0xa3, 0xf1, 0x9e, 0x63, 0x9b, 0x3a, 0x5b, 0xea, 0x79, - 0x2e, 0x72, 0x73, 0x2a, 0xd2, 0xe5, 0xbc, 0xa7, 0x64, 0xa2, 0xfe, 0x38, 0x05, 0x0a, 0x36, 0xf6, - 0x83, 0x1d, 0x32, 0xdc, 0x23, 0x5e, 0x2f, 0x30, 0x82, 0xb1, 0x8f, 0x6e, 0x41, 0xce, 0x21, 0x86, - 0x45, 0x3c, 0x3e, 0xa8, 0x3c, 0x96, 0x2d, 0xb4, 0xcb, 0xce, 0xb7, 0x61, 0x1e, 0x18, 0x7b, 0xb6, - 0x63, 0x07, 0x13, 0x3e, 0x94, 0xa5, 0xe4, 0x0d, 0x3e, 0xab, 0xb3, 0x8a, 0x63, 0x82, 0xf8, 0x82, - 0x1a, 0xb4, 0x0a, 0x0b, 0x43, 0xe2, 0xfb, 0xc6, 0x80, 0xf0, 0x91, 0x16, 0x70, 0xd8, 0x54, 0x3f, - 0x80, 0x52, 0x5c, 0x0e, 0x15, 0x61, 0x61, 0xb7, 0xfd, 0xb4, 0xdd, 0x79, 0xde, 0x56, 0xe6, 0xd0, - 0x32, 0x14, 0x77, 0xdb, 0x58, 0xab, 0x35, 0xb6, 0x6a, 0xf5, 0x6d, 0x4d, 0x49, 0xa1, 0x45, 0x28, - 0x4c, 0x9b, 0x69, 0xf5, 0x4f, 0x53, 0x00, 0xcc, 0xdc, 0x72, 0x52, 0xef, 0x43, 0xd6, 0x0f, 0x8c, - 0x40, 0xec, 0xd9, 0xa5, 0x47, 0x6f, 0x5d, 0xb6, 0xc2, 0x72, 0xbc, 0xec, 0x1f, 0xc1, 0x42, 0x24, - 0x3e, 0xc2, 0xf4, 0x85, 0x11, 0x32, 0xf7, 0x61, 0x58, 0x96, 0x27, 0x07, 0xce, 0x7f, 0xab, 0x1f, - 0x40, 0x96, 0x4b, 0x5f, 0x1c, 0x6e, 0x1e, 0x32, 0x4d, 0xf6, 0x2b, 0x85, 0x0a, 0x90, 0xc5, 0x5a, - 0xad, 0xf9, 0x1d, 0x25, 0x8d, 0x14, 0x28, 0x35, 0x5b, 0xbd, 0x46, 0xa7, 0xdd, 0xd6, 0x1a, 0x7d, - 0xad, 0xa9, 0xcc, 0xab, 0x77, 0x21, 0xdb, 0x1a, 0x32, 0xcd, 0x77, 0xd8, 0x81, 0xd8, 0x27, 0x1e, - 0x71, 0xcd, 0xf0, 0x9c, 0x4d, 0x09, 0xea, 0x0f, 0x4a, 0x90, 0xdd, 0xa1, 0x63, 0x37, 0x40, 0x8f, - 0x62, 0x4e, 0x6d, 0x29, 0x39, 0x7e, 0xe0, 0xc0, 0x6a, 0x7f, 0x32, 0x22, 0xd2, 0xe9, 0xdd, 0x82, - 0x9c, 0x38, 0x3a, 0x72, 0x3a, 0xb2, 0xc5, 0xe8, 0x81, 0xe1, 0x0d, 0x48, 0x20, 0xe7, 0x23, 0x5b, - 0xe8, 0x1d, 0x76, 0x9f, 0x19, 0x16, 0x75, 0x9d, 0x09, 0x3f, 0x61, 0x79, 0x71, 0x69, 0x61, 0x62, - 0x58, 0x1d, 0xd7, 0x99, 0xe0, 0x88, 0x8b, 0xb6, 0xa0, 0xb4, 0x67, 0xbb, 0x96, 0x4e, 0x47, 0xe2, - 0x0a, 0xc8, 0x5e, 0x7e, 0x1e, 0xc5, 0xa8, 0xea, 0xb6, 0x6b, 0x75, 0x04, 0x18, 0x17, 0xf7, 0xa6, - 0x0d, 0xd4, 0x86, 0xa5, 0x23, 0xea, 0x8c, 0x87, 0x24, 0xd2, 0x95, 0xe3, 0xba, 0xde, 0xbe, 0x5c, - 0xd7, 0x33, 0x8e, 0x0f, 0xb5, 0x2d, 0x1e, 0xc5, 0x9b, 0xe8, 0x29, 0x2c, 0x06, 0xc3, 0xd1, 0xbe, - 0x1f, 0xa9, 0x5b, 0xe0, 0xea, 0xbe, 0x74, 0x85, 0xc1, 0x18, 0x3c, 0xd4, 0x56, 0x0a, 0x62, 0x2d, - 0xb4, 0x09, 0x45, 0x93, 0xba, 0xbe, 0xed, 0x07, 0xc4, 0x35, 0x27, 0xab, 0x79, 0x6e, 0xfb, 0x2b, - 0x66, 0xd9, 0x98, 0x82, 0x71, 0x5c, 0xb2, 0xfc, 0xeb, 0xf3, 0x50, 0x8c, 0x99, 0x00, 0xf5, 0xa0, - 0x38, 0xf2, 0xe8, 0xc8, 0x18, 0xf0, 0xfb, 0x50, 0x2e, 0xea, 0xc3, 0x57, 0x32, 0x5f, 0xb5, 0x3b, - 0x15, 0xc4, 0x71, 0x2d, 0xea, 0x69, 0x1a, 0x8a, 0x31, 0x26, 0xba, 0x07, 0x79, 0xdc, 0xc5, 0xad, - 0x67, 0xb5, 0xbe, 0xa6, 0xcc, 0x95, 0xef, 0x9c, 0x9c, 0x56, 0x56, 0xb9, 0xb6, 0xb8, 0x82, 0xae, - 0x67, 0x1f, 0xb1, 0x3d, 0xfc, 0x0e, 0x2c, 0x84, 0xd0, 0x54, 0xf9, 0x8b, 0x27, 0xa7, 0x95, 0xd7, - 0x66, 0xa1, 0x31, 0x24, 0xee, 0x6d, 0xd5, 0xb0, 0xd6, 0x54, 0xd2, 0xc9, 0x48, 0xdc, 0x3b, 0x30, - 0x3c, 0x62, 0xa1, 0x2f, 0x41, 0x4e, 0x02, 0xe7, 0xcb, 0xe5, 0x93, 0xd3, 0xca, 0xad, 0x59, 0xe0, - 0x14, 0x87, 0x7b, 0xdb, 0xb5, 0x67, 0x9a, 0x92, 0x49, 0xc6, 0xe1, 0x9e, 0x63, 0x1c, 0x11, 0xf4, - 0x16, 0x64, 0x05, 0x2c, 0x5b, 0xbe, 0x7d, 0x72, 0x5a, 0xf9, 0xc2, 0x4b, 0xea, 0x18, 0xaa, 0xbc, - 0xfa, 0x5b, 0x7f, 0xb4, 0x36, 0xf7, 0x17, 0x7f, 0xbc, 0xa6, 0xcc, 0xb2, 0xcb, 0xff, 0x9b, 0x82, - 0xc5, 0x0b, 0x7b, 0x07, 0xa9, 0x90, 0x73, 0xa9, 0x49, 0x47, 0xe2, 0x9a, 0xcc, 0xd7, 0xe1, 0xfc, - 0x6c, 0x3d, 0xd7, 0xa6, 0x0d, 0x3a, 0x9a, 0x60, 0xc9, 0x41, 0x4f, 0x67, 0x2e, 0xfa, 0xc7, 0xaf, - 0xb8, 0x31, 0x13, 0xaf, 0xfa, 0x0f, 0x61, 0xd1, 0xf2, 0xec, 0x23, 0xe2, 0xe9, 0x26, 0x75, 0xf7, - 0xed, 0x81, 0xbc, 0x02, 0xcb, 0x89, 0xd1, 0x28, 0x07, 0xe2, 0x92, 0x10, 0x68, 0x70, 0xfc, 0xe7, - 0xb8, 0xe4, 0xcb, 0x23, 0x28, 0xc5, 0xb7, 0x3a, 0xbb, 0x97, 0x7c, 0xfb, 0x97, 0x89, 0x0c, 0x3b, - 0x79, 0x90, 0x8a, 0x0b, 0x8c, 0x22, 0x82, 0xce, 0xb7, 0x21, 0x33, 0xa4, 0x96, 0xd0, 0xb3, 0x58, - 0xbf, 0xc9, 0x62, 0x8d, 0x7f, 0x3e, 0x5b, 0x2f, 0x52, 0xbf, 0xba, 0x61, 0x3b, 0x64, 0x87, 0x5a, - 0x04, 0x73, 0x00, 0xf3, 0x9a, 0xe1, 0x59, 0x93, 0x7e, 0x5d, 0x36, 0xd5, 0xef, 0xa7, 0x20, 0xc3, - 0xdc, 0x11, 0xfa, 0x22, 0x64, 0xea, 0xad, 0x76, 0x53, 0x99, 0x2b, 0xdf, 0x38, 0x39, 0xad, 0x2c, - 0x72, 0x6b, 0x31, 0x06, 0xdb, 0xd6, 0x68, 0x1d, 0x72, 0xcf, 0x3a, 0xdb, 0xbb, 0x3b, 0x6c, 0xe7, - 0xdd, 0x3c, 0x39, 0xad, 0x2c, 0x47, 0x6c, 0x61, 0x4f, 0xf4, 0x3a, 0x64, 0xfb, 0x3b, 0xdd, 0x8d, - 0x9e, 0x92, 0x2e, 0xa3, 0x93, 0xd3, 0xca, 0x52, 0xc4, 0xe7, 0xd3, 0x41, 0x6f, 0x40, 0xb6, 0xdd, - 0x6d, 0x75, 0x35, 0x65, 0xbe, 0x7c, 0xeb, 0xe4, 0xb4, 0x82, 0x22, 0x36, 0xcf, 0x16, 0xba, 0xf6, - 0x88, 0x94, 0x6f, 0xc8, 0x3d, 0x51, 0x88, 0x78, 0xea, 0x8f, 0x52, 0x50, 0x8c, 0x1d, 0x57, 0xb6, - 0xad, 0x9b, 0xda, 0x46, 0x6d, 0x77, 0xbb, 0xaf, 0xcc, 0xc5, 0xb6, 0x75, 0x0c, 0xd2, 0x24, 0xfb, - 0xc6, 0xd8, 0x61, 0x5e, 0x12, 0x1a, 0x9d, 0x76, 0xaf, 0xd5, 0xeb, 0x6b, 0xed, 0xbe, 0x92, 0x2a, - 0xaf, 0x9e, 0x9c, 0x56, 0x56, 0x66, 0xc1, 0x1b, 0x63, 0xc7, 0x61, 0x1b, 0xbb, 0x51, 0x6b, 0x6c, - 0xf1, 0x93, 0x32, 0xdd, 0xd8, 0x31, 0x54, 0xc3, 0x30, 0x0f, 0x88, 0x85, 0xde, 0x85, 0x42, 0x53, - 0xdb, 0xd6, 0x36, 0x6b, 0xfc, 0x6e, 0x28, 0xbf, 0x7e, 0x72, 0x5a, 0xb9, 0xfd, 0x72, 0xef, 0x0e, - 0x19, 0x18, 0x01, 0xb1, 0x66, 0x36, 0x78, 0x0c, 0xa2, 0xfe, 0x77, 0x1a, 0x16, 0x31, 0x4b, 0xb5, - 0xbd, 0xa0, 0x4b, 0x1d, 0xdb, 0x9c, 0xa0, 0x2e, 0x14, 0x4c, 0xea, 0x5a, 0x76, 0xcc, 0xcb, 0x3c, - 0xba, 0x24, 0xdc, 0x9a, 0x4a, 0x85, 0xad, 0x46, 0x28, 0x89, 0xa7, 0x4a, 0xd0, 0x7d, 0xc8, 0x5a, - 0xc4, 0x31, 0x26, 0x32, 0xee, 0xbb, 0x5d, 0x15, 0xc9, 0x7c, 0x35, 0x4c, 0xe6, 0xab, 0x4d, 0x99, - 0xcc, 0x63, 0x81, 0xe3, 0xf9, 0x8d, 0xf1, 0x42, 0x37, 0x82, 0x80, 0x0c, 0x47, 0x81, 0xd8, 0x23, - 0x19, 0x5c, 0x1c, 0x1a, 0x2f, 0x6a, 0x92, 0x84, 0x1e, 0x42, 0xee, 0xd8, 0x76, 0x2d, 0x7a, 0x2c, - 0xe3, 0xba, 0x2b, 0x94, 0x4a, 0xa0, 0x7a, 0xc2, 0x02, 0x9a, 0x99, 0x61, 0xb2, 0x6d, 0xd6, 0xee, - 0xb4, 0xb5, 0x70, 0x9b, 0x49, 0x7e, 0xc7, 0x6d, 0x53, 0x97, 0x79, 0x0f, 0xe8, 0xb4, 0xf5, 0x8d, - 0x5a, 0x6b, 0x7b, 0x17, 0xb3, 0xad, 0xb6, 0x72, 0x72, 0x5a, 0x51, 0x22, 0xc8, 0x86, 0x61, 0x3b, - 0x2c, 0xd1, 0xb8, 0x0d, 0xf3, 0xb5, 0xf6, 0x77, 0x94, 0x74, 0x59, 0x39, 0x39, 0xad, 0x94, 0x22, - 0x76, 0xcd, 0x9d, 0x4c, 0xed, 0x3e, 0xdb, 0xaf, 0xfa, 0xb7, 0xf3, 0x50, 0xda, 0x1d, 0x59, 0x46, - 0x40, 0xc4, 0x29, 0x45, 0x15, 0x28, 0x8e, 0x0c, 0xcf, 0x70, 0x1c, 0xe2, 0xd8, 0xfe, 0x50, 0x96, - 0x29, 0xe2, 0x24, 0xf4, 0xde, 0xab, 0x9a, 0xb1, 0x9e, 0x67, 0x27, 0xef, 0x7b, 0xff, 0xba, 0x9e, - 0x0a, 0x0d, 0xba, 0x0b, 0x4b, 0xfb, 0x62, 0xb4, 0xba, 0x61, 0xf2, 0x85, 0x9d, 0xe7, 0x0b, 0x5b, - 0x4d, 0x5a, 0xd8, 0xf8, 0xb0, 0xaa, 0x72, 0x92, 0x35, 0x2e, 0x85, 0x17, 0xf7, 0xe3, 0x4d, 0xf4, - 0x18, 0x16, 0x86, 0xd4, 0xb5, 0x03, 0xea, 0x5d, 0xbf, 0x0a, 0x21, 0x12, 0xdd, 0x83, 0x1b, 0x6c, - 0x71, 0xc3, 0xf1, 0x70, 0x36, 0x0f, 0x06, 0xd2, 0x78, 0x79, 0x68, 0xbc, 0x90, 0x1d, 0x62, 0x46, - 0x46, 0x75, 0xc8, 0x52, 0x8f, 0x45, 0x9b, 0x39, 0x3e, 0xdc, 0x77, 0xaf, 0x1d, 0xae, 0x68, 0x74, - 0x98, 0x0c, 0x16, 0xa2, 0xea, 0xd7, 0x61, 0xf1, 0xc2, 0x24, 0x58, 0x90, 0xd5, 0xad, 0xed, 0xf6, - 0x34, 0x65, 0x0e, 0x95, 0x20, 0xdf, 0xe8, 0xb4, 0xfb, 0xad, 0xf6, 0x2e, 0x8b, 0x12, 0x4b, 0x90, - 0xc7, 0x9d, 0xed, 0xed, 0x7a, 0xad, 0xf1, 0x54, 0x49, 0xab, 0x55, 0x28, 0xc6, 0xb4, 0xa1, 0x25, - 0x80, 0x5e, 0xbf, 0xd3, 0xd5, 0x37, 0x5a, 0xb8, 0xd7, 0x17, 0x31, 0x66, 0xaf, 0x5f, 0xc3, 0x7d, - 0x49, 0x48, 0xa9, 0xff, 0x99, 0x0e, 0x57, 0x54, 0x86, 0x95, 0xf5, 0x8b, 0x61, 0xe5, 0x15, 0x83, - 0x97, 0x81, 0xe5, 0xb4, 0x11, 0x85, 0x97, 0xef, 0x01, 0xf0, 0x8d, 0x43, 0x2c, 0xdd, 0x08, 0xe4, - 0xc2, 0x97, 0x5f, 0x32, 0x72, 0x3f, 0xac, 0x96, 0xe1, 0x82, 0x44, 0xd7, 0x02, 0xf4, 0x0d, 0x28, - 0x99, 0x74, 0x38, 0x72, 0x88, 0x14, 0x9e, 0xbf, 0x56, 0xb8, 0x18, 0xe1, 0x6b, 0x41, 0x3c, 0xb0, - 0xcd, 0x5c, 0x0c, 0xbd, 0x7f, 0x23, 0x15, 0x5a, 0x26, 0x21, 0x96, 0x2d, 0x41, 0x7e, 0xb7, 0xdb, - 0xac, 0xf5, 0x5b, 0xed, 0x4d, 0x25, 0x85, 0x00, 0x72, 0xdc, 0xd4, 0x4d, 0x25, 0xcd, 0x62, 0xf0, - 0x46, 0x67, 0xa7, 0xbb, 0xad, 0x71, 0x8f, 0x85, 0x56, 0x40, 0x09, 0x8d, 0xad, 0x73, 0x43, 0x6a, - 0x4d, 0x25, 0x83, 0x6e, 0xc2, 0x72, 0x44, 0x95, 0x92, 0x59, 0x74, 0x0b, 0x50, 0x44, 0x9c, 0xaa, - 0xc8, 0xa9, 0xbf, 0x0a, 0xcb, 0x0d, 0xea, 0x06, 0x86, 0xed, 0x46, 0xf9, 0xc9, 0x23, 0x36, 0x69, - 0x49, 0xd2, 0x6d, 0x59, 0x65, 0xaa, 0x2f, 0x9f, 0x9f, 0xad, 0x17, 0x23, 0x68, 0xab, 0xc9, 0x03, - 0x2d, 0xd9, 0xb0, 0xd8, 0xf9, 0x1d, 0xd9, 0x16, 0x37, 0x6e, 0xb6, 0xbe, 0x70, 0x7e, 0xb6, 0x3e, - 0xdf, 0x6d, 0x35, 0x31, 0xa3, 0xa1, 0x2f, 0x42, 0x81, 0xbc, 0xb0, 0x03, 0xdd, 0x64, 0xb7, 0x1a, - 0x33, 0x60, 0x16, 0xe7, 0x19, 0xa1, 0x41, 0x2d, 0xa2, 0xd6, 0x01, 0xba, 0xd4, 0x0b, 0x64, 0xcf, - 0x5f, 0x85, 0xec, 0x88, 0x7a, 0xbc, 0x2e, 0x72, 0x69, 0xb5, 0x8e, 0xc1, 0xc5, 0x46, 0xc5, 0x02, - 0xac, 0x7e, 0x7f, 0x1e, 0xa0, 0x6f, 0xf8, 0x87, 0x52, 0xc9, 0x13, 0x28, 0x44, 0x95, 0x4f, 0x59, - 0x60, 0xb9, 0x72, 0xb5, 0x23, 0x30, 0x7a, 0x1c, 0x6e, 0x36, 0x91, 0x79, 0x25, 0x26, 0xc8, 0x61, - 0x47, 0x49, 0xc9, 0xcb, 0xc5, 0xf4, 0x8a, 0x05, 0x09, 0xc4, 0xf3, 0xe4, 0xca, 0xb3, 0x9f, 0xa8, - 0xc1, 0xaf, 0x05, 0x61, 0x34, 0x19, 0xbb, 0x27, 0x96, 0x94, 0x66, 0x56, 0x64, 0x6b, 0x0e, 0x4f, - 0xe5, 0xd0, 0x87, 0x50, 0x64, 0xf3, 0xd6, 0x7d, 0xce, 0x93, 0x61, 0xfb, 0xa5, 0xa6, 0x12, 0x1a, - 0x30, 0x8c, 0xa6, 0x56, 0x7e, 0x1d, 0xc0, 0x18, 0x8d, 0x1c, 0x9b, 0x58, 0xfa, 0xde, 0x84, 0xc7, - 0xe9, 0x05, 0x5c, 0x90, 0x94, 0xfa, 0x84, 0x1d, 0x97, 0x90, 0x6d, 0x04, 0x3c, 0xf6, 0xbe, 0xc6, - 0x80, 0x12, 0x5d, 0x0b, 0xea, 0x0a, 0x2c, 0x79, 0x63, 0x97, 0x19, 0x54, 0x8e, 0x4e, 0xfd, 0x93, - 0x34, 0xbc, 0xd6, 0x26, 0xc1, 0x31, 0xf5, 0x0e, 0x6b, 0x41, 0x60, 0x98, 0x07, 0x43, 0xe2, 0xca, - 0xe5, 0x8b, 0xa5, 0x43, 0xa9, 0x0b, 0xe9, 0xd0, 0x2a, 0x2c, 0x18, 0x8e, 0x6d, 0xf8, 0x44, 0x84, - 0x7e, 0x05, 0x1c, 0x36, 0x59, 0xd2, 0xc6, 0x52, 0x40, 0xe2, 0xfb, 0x44, 0xd4, 0x6c, 0xd8, 0xc0, - 0x43, 0x02, 0xfa, 0x2e, 0xdc, 0x92, 0x41, 0x9e, 0x11, 0x75, 0xc5, 0xd2, 0x91, 0xb0, 0xf8, 0xab, - 0x25, 0xe6, 0xa4, 0xc9, 0x83, 0x93, 0x51, 0xe0, 0x94, 0xdc, 0x19, 0x05, 0x32, 0xa6, 0x5c, 0xb1, - 0x12, 0x58, 0xe5, 0x4d, 0xb8, 0x7d, 0xa9, 0xc8, 0x67, 0xaa, 0x09, 0xfd, 0x43, 0x1a, 0xa0, 0xd5, - 0xad, 0xed, 0x48, 0x23, 0x35, 0x21, 0xb7, 0x6f, 0x0c, 0x6d, 0x67, 0x72, 0x95, 0x07, 0x9c, 0xe2, - 0xab, 0x35, 0x61, 0x8e, 0x0d, 0x2e, 0x83, 0xa5, 0x2c, 0xcf, 0x48, 0xc7, 0x7b, 0x2e, 0x09, 0xa2, - 0x8c, 0x94, 0xb7, 0xd8, 0x30, 0x3c, 0xc3, 0x8d, 0xb6, 0xae, 0x68, 0xb0, 0x05, 0x60, 0x21, 0xcf, - 0xb1, 0x31, 0x09, 0xdd, 0x96, 0x6c, 0xa2, 0x2d, 0x5e, 0x79, 0x25, 0xde, 0x11, 0xb1, 0x56, 0xb3, - 0xdc, 0xa8, 0xd7, 0x8d, 0x07, 0x4b, 0xb8, 0xb0, 0x5d, 0x24, 0x5d, 0xfe, 0x80, 0x87, 0x4c, 0x53, - 0xd6, 0x67, 0xb2, 0xd1, 0x03, 0x58, 0xbc, 0x30, 0xcf, 0x97, 0x4a, 0x01, 0xad, 0xee, 0xb3, 0xaf, - 0x2a, 0x19, 0xf9, 0xeb, 0xeb, 0x4a, 0x4e, 0xfd, 0xeb, 0x79, 0xe1, 0x68, 0xa4, 0x55, 0x93, 0x5f, - 0x1c, 0xf2, 0x7c, 0x77, 0x9b, 0xd4, 0x91, 0x0e, 0xe0, 0xed, 0xab, 0xfd, 0x0f, 0xcb, 0x08, 0x39, - 0x1c, 0x47, 0x82, 0x68, 0x1d, 0x8a, 0x62, 0x17, 0xeb, 0xec, 0xc0, 0x71, 0xb3, 0x2e, 0x62, 0x10, - 0x24, 0x26, 0x89, 0xee, 0xc2, 0x12, 0x2f, 0x1d, 0xf9, 0x07, 0xc4, 0x12, 0x98, 0x0c, 0xc7, 0x2c, - 0x46, 0x54, 0x0e, 0xdb, 0x81, 0x92, 0x24, 0xe8, 0x3c, 0x1b, 0xc8, 0xf2, 0x01, 0xdd, 0xbb, 0x6e, - 0x40, 0x42, 0x84, 0x27, 0x09, 0xc5, 0xd1, 0xb4, 0xa1, 0xfe, 0x22, 0xe4, 0xc3, 0xc1, 0xa2, 0x55, - 0x98, 0xef, 0x37, 0xba, 0xca, 0x5c, 0x79, 0xf9, 0xe4, 0xb4, 0x52, 0x0c, 0xc9, 0xfd, 0x46, 0x97, - 0x71, 0x76, 0x9b, 0x5d, 0x25, 0x75, 0x91, 0xb3, 0xdb, 0xec, 0xa2, 0x32, 0x64, 0x7a, 0x8d, 0x7e, - 0x37, 0x8c, 0xcf, 0x42, 0x16, 0xa3, 0x95, 0x33, 0x2c, 0x3e, 0x53, 0xf7, 0xa1, 0x18, 0xeb, 0x1d, - 0xbd, 0x09, 0x0b, 0xad, 0xf6, 0x26, 0xd6, 0x7a, 0x3d, 0x65, 0x4e, 0xa4, 0x07, 0x31, 0x6e, 0xcb, - 0x1d, 0xb0, 0xb5, 0x43, 0xaf, 0x43, 0x66, 0xab, 0xc3, 0xee, 0x7d, 0x91, 0x7f, 0xc4, 0x10, 0x5b, - 0xd4, 0x0f, 0xca, 0x37, 0x65, 0xe0, 0x17, 0x57, 0xac, 0xfe, 0x7e, 0x0a, 0x72, 0xe2, 0xa0, 0x25, - 0x2e, 0x62, 0x6d, 0x9a, 0x14, 0x89, 0xb4, 0xf1, 0xed, 0xcb, 0x53, 0xbc, 0xaa, 0xcc, 0xc8, 0xc4, - 0xd6, 0x0c, 0xe5, 0xca, 0xef, 0x43, 0x29, 0xce, 0xf8, 0x4c, 0x1b, 0xf3, 0xbb, 0x50, 0x64, 0x7b, - 0x3f, 0x4c, 0xf5, 0x1e, 0x41, 0x4e, 0x38, 0x8b, 0xe8, 0x1e, 0xba, 0x3c, 0xdf, 0x94, 0x48, 0xf4, - 0x04, 0x16, 0x44, 0x8e, 0x1a, 0x56, 0xa5, 0xd7, 0xae, 0x3e, 0x61, 0x38, 0x84, 0xab, 0x1f, 0x42, - 0xa6, 0x4b, 0x88, 0xc7, 0x6c, 0xef, 0x52, 0x8b, 0x4c, 0xaf, 0x6e, 0x99, 0x5e, 0x5b, 0xa4, 0xd5, - 0x64, 0xe9, 0xb5, 0x45, 0x5a, 0x56, 0x54, 0x59, 0x4b, 0xc7, 0x2a, 0x6b, 0x7d, 0x28, 0x3d, 0x27, - 0xf6, 0xe0, 0x20, 0x20, 0x16, 0x57, 0xf4, 0x2e, 0x64, 0x46, 0x24, 0x1a, 0xfc, 0x6a, 0xe2, 0xe6, - 0x23, 0xc4, 0xc3, 0x1c, 0xc5, 0x7c, 0xcc, 0x31, 0x97, 0x96, 0x4f, 0x29, 0xb2, 0xa5, 0xfe, 0x7d, - 0x1a, 0x96, 0x5a, 0xbe, 0x3f, 0x36, 0x5c, 0x33, 0x8c, 0xea, 0xbe, 0x79, 0x31, 0xaa, 0x4b, 0x7c, - 0x73, 0xba, 0x28, 0x72, 0xb1, 0x60, 0x28, 0x6f, 0xd6, 0x74, 0x74, 0xb3, 0xaa, 0xff, 0x91, 0x0a, - 0xab, 0x82, 0x77, 0x63, 0xae, 0x40, 0xe4, 0x88, 0x71, 0x4d, 0x64, 0xd7, 0x3d, 0x74, 0xe9, 0xb1, - 0xcb, 0xb2, 0x57, 0xac, 0xb5, 0xb5, 0xe7, 0x4a, 0x4a, 0x6c, 0xcf, 0x0b, 0x20, 0x4c, 0x5c, 0x72, - 0xcc, 0x34, 0x75, 0xb5, 0x76, 0x93, 0x45, 0x61, 0xe9, 0x04, 0x4d, 0x5d, 0xe2, 0x5a, 0xb6, 0x3b, - 0x40, 0x6f, 0x42, 0xae, 0xd5, 0xeb, 0xed, 0xf2, 0x14, 0xf2, 0xb5, 0x93, 0xd3, 0xca, 0xcd, 0x0b, - 0x28, 0x5e, 0x11, 0xb6, 0x18, 0x88, 0xa5, 0x40, 0x2c, 0x3e, 0x4b, 0x00, 0xb1, 0xd8, 0x5a, 0x80, - 0x70, 0xa7, 0x5f, 0xeb, 0x6b, 0x4a, 0x36, 0x01, 0x84, 0x29, 0xfb, 0x2b, 0x8f, 0xdb, 0xbf, 0xa4, - 0x41, 0xa9, 0x99, 0x26, 0x19, 0x05, 0x8c, 0x2f, 0xb3, 0xce, 0x3e, 0xe4, 0x47, 0xec, 0x97, 0x4d, - 0xc2, 0x08, 0xea, 0x49, 0xe2, 0xab, 0xe9, 0x8c, 0x5c, 0x15, 0x53, 0x87, 0xd4, 0xac, 0xa1, 0xed, - 0xfb, 0x36, 0x75, 0x05, 0x0d, 0x47, 0x9a, 0xca, 0xff, 0x95, 0x82, 0x9b, 0x09, 0x08, 0xf4, 0x00, - 0x32, 0x1e, 0x75, 0xc2, 0x35, 0xbc, 0x73, 0x59, 0xc1, 0x97, 0x89, 0x62, 0x8e, 0x44, 0x6b, 0x00, - 0xc6, 0x38, 0xa0, 0x06, 0xef, 0x9f, 0xaf, 0x5e, 0x1e, 0xc7, 0x28, 0xe8, 0x39, 0xe4, 0x7c, 0x62, - 0x7a, 0x24, 0x8c, 0xb3, 0x3f, 0xfc, 0xff, 0x8e, 0xbe, 0xda, 0xe3, 0x6a, 0xb0, 0x54, 0x57, 0xae, - 0x42, 0x4e, 0x50, 0xd8, 0xb6, 0xb7, 0x8c, 0xc0, 0x90, 0xcf, 0x01, 0xfc, 0x37, 0xdb, 0x4d, 0x86, - 0x33, 0x08, 0x77, 0x93, 0xe1, 0x0c, 0xd4, 0xbf, 0x4a, 0x03, 0x68, 0x2f, 0x02, 0xe2, 0xb9, 0x86, - 0xd3, 0xa8, 0x21, 0x2d, 0x76, 0x33, 0x88, 0xd9, 0x7e, 0x39, 0xf1, 0x05, 0x24, 0x92, 0xa8, 0x36, - 0x6a, 0x09, 0x77, 0xc3, 0x6d, 0x98, 0x1f, 0x7b, 0xf2, 0x21, 0x5c, 0xc4, 0xc8, 0xbb, 0x78, 0x1b, - 0x33, 0x1a, 0xd2, 0xe2, 0xb5, 0x9c, 0x4b, 0x9f, 0xbb, 0x63, 0x1d, 0x24, 0xba, 0x2e, 0x76, 0xf2, - 0x4d, 0x43, 0x37, 0x89, 0xbc, 0x55, 0x4a, 0xe2, 0xe4, 0x37, 0x6a, 0x0d, 0xe2, 0x05, 0x38, 0x67, - 0x1a, 0xec, 0xff, 0xe7, 0xf2, 0x6f, 0xef, 0x02, 0x4c, 0xa7, 0x86, 0xd6, 0x20, 0xdb, 0xd8, 0xe8, - 0xf5, 0xb6, 0x95, 0x39, 0xe1, 0xc0, 0xa7, 0x2c, 0x4e, 0x56, 0xff, 0x3c, 0x0d, 0xf9, 0x46, 0x4d, - 0x5e, 0xb9, 0x0d, 0x50, 0xb8, 0x57, 0xe2, 0x8f, 0x28, 0xe4, 0xc5, 0xc8, 0xf6, 0x26, 0xd2, 0xb1, - 0x5c, 0x91, 0xf0, 0x2e, 0x31, 0x11, 0x36, 0x6a, 0x8d, 0x0b, 0x20, 0x0c, 0x25, 0x22, 0x8d, 0xa0, - 0x9b, 0x46, 0xe8, 0xe3, 0xd7, 0xae, 0x36, 0x96, 0x48, 0x5d, 0xa6, 0x6d, 0x1f, 0x17, 0x43, 0x25, - 0x0d, 0xc3, 0x47, 0xef, 0xc1, 0xb2, 0x6f, 0x0f, 0x5c, 0xdb, 0x1d, 0xe8, 0xa1, 0xf1, 0xf8, 0x8b, - 0x4e, 0xfd, 0xc6, 0xf9, 0xd9, 0xfa, 0x62, 0x4f, 0xb0, 0xa4, 0x0d, 0x17, 0x25, 0xb2, 0xc1, 0x4d, - 0x89, 0xbe, 0x0e, 0x4b, 0x31, 0x51, 0x66, 0x45, 0x61, 0x76, 0xe5, 0xfc, 0x6c, 0xbd, 0x14, 0x49, - 0x3e, 0x25, 0x13, 0x5c, 0x8a, 0x04, 0x9f, 0x12, 0x5e, 0x9b, 0xd9, 0xa7, 0x9e, 0x49, 0x74, 0x8f, - 0x9f, 0x69, 0x7e, 0xbb, 0x67, 0x70, 0x91, 0xd3, 0xc4, 0x31, 0x57, 0x9f, 0xc1, 0xcd, 0x8e, 0x67, - 0x1e, 0x10, 0x3f, 0x10, 0xa6, 0x90, 0x56, 0xfc, 0x10, 0xee, 0x04, 0x86, 0x7f, 0xa8, 0x1f, 0xd8, - 0x7e, 0x40, 0xbd, 0x89, 0xee, 0x91, 0x80, 0xb8, 0x8c, 0xaf, 0xf3, 0x47, 0x62, 0x59, 0x4e, 0xbc, - 0xcd, 0x30, 0x5b, 0x02, 0x82, 0x43, 0xc4, 0x36, 0x03, 0xa8, 0x2d, 0x28, 0xb1, 0x14, 0x46, 0x16, - 0xd5, 0xd8, 0xec, 0xc1, 0xa1, 0x03, 0xfd, 0x95, 0xaf, 0xa9, 0x82, 0x43, 0x07, 0xe2, 0xa7, 0xfa, - 0x6d, 0x50, 0x9a, 0xb6, 0x3f, 0x32, 0x02, 0xf3, 0x20, 0xac, 0x93, 0xa2, 0x26, 0x28, 0x07, 0xc4, - 0xf0, 0x82, 0x3d, 0x62, 0x04, 0xfa, 0x88, 0x78, 0x36, 0xb5, 0xae, 0x5f, 0xe5, 0xe5, 0x48, 0xa4, - 0xcb, 0x25, 0xd4, 0xff, 0x49, 0x01, 0x60, 0x63, 0x3f, 0x8c, 0xd6, 0xbe, 0x02, 0x37, 0x7c, 0xd7, - 0x18, 0xf9, 0x07, 0x34, 0xd0, 0x6d, 0x37, 0x20, 0xde, 0x91, 0xe1, 0xc8, 0xe2, 0x8e, 0x12, 0x32, - 0x5a, 0x92, 0x8e, 0xde, 0x05, 0x74, 0x48, 0xc8, 0x48, 0xa7, 0x8e, 0xa5, 0x87, 0x4c, 0xf1, 0x84, - 0x9d, 0xc1, 0x0a, 0xe3, 0x74, 0x1c, 0xab, 0x17, 0xd2, 0x51, 0x1d, 0xd6, 0xd8, 0xf4, 0x89, 0x1b, - 0x78, 0x36, 0xf1, 0xf5, 0x7d, 0xea, 0xe9, 0xbe, 0x43, 0x8f, 0xf5, 0x7d, 0xea, 0x38, 0xf4, 0x98, - 0x78, 0x61, 0xdd, 0xac, 0xec, 0xd0, 0x81, 0x26, 0x40, 0x1b, 0xd4, 0xeb, 0x39, 0xf4, 0x78, 0x23, - 0x44, 0xb0, 0x90, 0x6e, 0x3a, 0xe7, 0xc0, 0x36, 0x0f, 0xc3, 0x90, 0x2e, 0xa2, 0xf6, 0x6d, 0xf3, - 0x10, 0xbd, 0x09, 0x8b, 0xc4, 0x21, 0xbc, 0x7c, 0x22, 0x50, 0x59, 0x8e, 0x2a, 0x85, 0x44, 0x06, - 0x52, 0x3f, 0x02, 0x45, 0x73, 0x4d, 0x6f, 0x32, 0x8a, 0xad, 0xf9, 0xbb, 0x80, 0x98, 0x93, 0xd4, - 0x1d, 0x6a, 0x1e, 0xea, 0x43, 0xc3, 0x35, 0x06, 0x6c, 0x5c, 0xe2, 0xed, 0x50, 0x61, 0x9c, 0x6d, - 0x6a, 0x1e, 0xee, 0x48, 0xba, 0xfa, 0x1e, 0x40, 0x6f, 0xe4, 0x11, 0xc3, 0xea, 0xb0, 0x68, 0x82, - 0x99, 0x8e, 0xb7, 0x74, 0x4b, 0xbe, 0xcc, 0x52, 0x4f, 0x1e, 0x75, 0x45, 0x30, 0x9a, 0x11, 0x5d, - 0xfd, 0x79, 0xb8, 0xd9, 0x75, 0x0c, 0x93, 0x7f, 0xa5, 0xd0, 0x8d, 0x1e, 0xc3, 0xd0, 0x13, 0xc8, - 0x09, 0xa8, 0x5c, 0xc9, 0xc4, 0xe3, 0x36, 0xed, 0x73, 0x6b, 0x0e, 0x4b, 0x7c, 0xbd, 0x04, 0x30, - 0xd5, 0xa3, 0xfe, 0x53, 0x0a, 0x0a, 0x91, 0x7e, 0x54, 0x11, 0x6f, 0x3c, 0x81, 0x67, 0xd8, 0xae, - 0xcc, 0xf8, 0x0b, 0x38, 0x4e, 0x42, 0x2d, 0x28, 0x8e, 0x22, 0xe9, 0x2b, 0xe3, 0xb9, 0x84, 0x51, - 0xe3, 0xb8, 0x2c, 0x7a, 0x1f, 0x0a, 0xe1, 0x53, 0x78, 0xe8, 0x61, 0xaf, 0x7e, 0x39, 0x9f, 0xc2, - 0xc3, 0x42, 0xaa, 0x47, 0x46, 0x8e, 0xcd, 0x7c, 0x4e, 0x26, 0x2a, 0xa4, 0x62, 0x49, 0x52, 0xbf, - 0x09, 0xf0, 0x2d, 0x6a, 0xbb, 0x7d, 0x7a, 0x48, 0x5c, 0xfe, 0xbe, 0xcb, 0x52, 0x4a, 0x12, 0x1a, - 0x5a, 0xb6, 0x78, 0xa5, 0x40, 0xac, 0x52, 0xf4, 0xcc, 0x29, 0x9a, 0xea, 0x5f, 0xa6, 0x21, 0x87, - 0x29, 0x0d, 0x1a, 0x35, 0x54, 0x81, 0x9c, 0x74, 0x25, 0xfc, 0x8a, 0xaa, 0x17, 0xce, 0xcf, 0xd6, - 0xb3, 0xc2, 0x87, 0x64, 0x4d, 0xee, 0x3c, 0x62, 0x4e, 0x3e, 0x7d, 0x99, 0x93, 0x47, 0x0f, 0xa0, - 0x24, 0x41, 0xfa, 0x81, 0xe1, 0x1f, 0x88, 0xfc, 0xae, 0xbe, 0x74, 0x7e, 0xb6, 0x0e, 0x02, 0xb9, - 0x65, 0xf8, 0x07, 0x18, 0x04, 0x9a, 0xfd, 0x46, 0x1a, 0x14, 0x3f, 0xa6, 0xb6, 0xab, 0x07, 0x7c, - 0x12, 0xb2, 0x16, 0x99, 0xb8, 0xd4, 0xd3, 0xa9, 0xca, 0x4f, 0x21, 0xe0, 0xe3, 0xe9, 0xe4, 0x35, - 0x58, 0xf4, 0x28, 0x0d, 0x84, 0x67, 0xb3, 0xa9, 0x2b, 0xcb, 0x1c, 0x95, 0xc4, 0xea, 0x37, 0xa5, - 0x01, 0x96, 0x38, 0x5c, 0xf2, 0x62, 0x2d, 0xf4, 0x00, 0x56, 0x1c, 0xc3, 0x0f, 0x74, 0xee, 0x12, - 0xad, 0xa9, 0xb6, 0x1c, 0x37, 0x3e, 0x62, 0xbc, 0x0d, 0xce, 0x0a, 0x25, 0xd4, 0x7f, 0x4c, 0x41, - 0x91, 0x4d, 0xc6, 0xde, 0xb7, 0x4d, 0x16, 0x07, 0x7e, 0xf6, 0xf0, 0xe4, 0x36, 0xcc, 0x9b, 0xbe, - 0x27, 0x8d, 0xca, 0xef, 0xe7, 0x46, 0x0f, 0x63, 0x46, 0x43, 0x1f, 0x41, 0x4e, 0x96, 0x5b, 0x44, - 0x64, 0xa2, 0x5e, 0x1f, 0xb1, 0x4a, 0xdb, 0x48, 0x39, 0xbe, 0xdd, 0xa7, 0xa3, 0x13, 0xf7, 0x04, - 0x8e, 0x93, 0xd0, 0x2d, 0x48, 0x9b, 0xc2, 0x5c, 0xf2, 0x5b, 0x9b, 0x46, 0x1b, 0xa7, 0x4d, 0x57, - 0xfd, 0x51, 0x0a, 0x16, 0xa7, 0x3e, 0x81, 0xed, 0x80, 0x3b, 0x50, 0xf0, 0xc7, 0x7b, 0xfe, 0xc4, - 0x0f, 0xc8, 0x30, 0x7c, 0xbb, 0x8e, 0x08, 0xa8, 0x05, 0x05, 0xc3, 0x19, 0x50, 0xcf, 0x0e, 0x0e, - 0x86, 0x32, 0x91, 0x4d, 0x8e, 0x26, 0xe2, 0x3a, 0xab, 0xb5, 0x50, 0x04, 0x4f, 0xa5, 0xc3, 0xd0, - 0x40, 0x7c, 0xe0, 0xc0, 0x43, 0x83, 0x37, 0xa0, 0xe4, 0x18, 0x43, 0x5e, 0x7f, 0x0a, 0xec, 0x21, - 0x09, 0x0f, 0x83, 0xa4, 0xf5, 0xed, 0x21, 0x51, 0x55, 0x28, 0x44, 0xca, 0xd0, 0x32, 0x14, 0x6b, - 0x5a, 0x4f, 0x7f, 0xf8, 0xe8, 0x89, 0xbe, 0xd9, 0xd8, 0x51, 0xe6, 0x64, 0xf8, 0xfa, 0x67, 0x29, - 0x58, 0x94, 0x1e, 0x4b, 0xa6, 0x04, 0x6f, 0xc2, 0x82, 0x67, 0xec, 0x07, 0x61, 0xd2, 0x92, 0x11, - 0xbb, 0x9a, 0x5d, 0x02, 0x2c, 0x69, 0x61, 0xac, 0xe4, 0xa4, 0x25, 0xf6, 0x35, 0xc5, 0xfc, 0x95, - 0x5f, 0x53, 0x64, 0x7e, 0x2a, 0x5f, 0x53, 0xa8, 0xbf, 0x06, 0xb0, 0x61, 0x3b, 0xa4, 0x2f, 0x4a, - 0x55, 0x49, 0x29, 0x28, 0x0b, 0xf3, 0x64, 0x29, 0x34, 0x0c, 0xf3, 0x5a, 0x4d, 0xcc, 0x68, 0x8c, - 0x35, 0xb0, 0x2d, 0x79, 0x18, 0x39, 0x6b, 0x93, 0xb1, 0x06, 0xb6, 0x15, 0x3d, 0xfb, 0x65, 0xae, - 0x79, 0xf6, 0x53, 0x4f, 0x53, 0xb0, 0x2c, 0xc3, 0xdb, 0xc8, 0x43, 0x7f, 0x19, 0x0a, 0x22, 0xd2, - 0x9d, 0xe6, 0x7c, 0xfc, 0x0b, 0x02, 0x81, 0x6b, 0x35, 0x71, 0x5e, 0xb0, 0x5b, 0x16, 0x5a, 0x87, - 0xa2, 0x84, 0xc6, 0xbe, 0xcb, 0x02, 0x41, 0x6a, 0xb3, 0xe1, 0x7f, 0x15, 0x32, 0xfb, 0xb6, 0x43, - 0xe4, 0x46, 0x4f, 0x74, 0x00, 0x53, 0x03, 0x6c, 0xcd, 0x61, 0x8e, 0xae, 0xe7, 0xc3, 0x5a, 0x1e, - 0x1f, 0x9f, 0xcc, 0x4c, 0xe3, 0xe3, 0x13, 0x49, 0xea, 0xcc, 0xf8, 0x04, 0x8e, 0x8d, 0x4f, 0xb0, - 0xc5, 0xf8, 0x24, 0x34, 0x3e, 0x3e, 0x41, 0xfa, 0xa9, 0x8c, 0x6f, 0x1b, 0x6e, 0xd5, 0x1d, 0xc3, - 0x3c, 0x74, 0x6c, 0x3f, 0x20, 0x56, 0xdc, 0x63, 0x3c, 0x82, 0xdc, 0x85, 0xb8, 0xf4, 0xaa, 0xa2, - 0xa7, 0x44, 0xaa, 0xff, 0x9e, 0x82, 0xd2, 0x16, 0x31, 0x9c, 0xe0, 0x60, 0x5a, 0x59, 0x0a, 0x88, - 0x1f, 0xc8, 0xfb, 0x8c, 0xff, 0x46, 0x5f, 0x83, 0x7c, 0x14, 0xb6, 0x5c, 0xfb, 0x7c, 0x17, 0x41, - 0xd1, 0x63, 0x58, 0x60, 0x67, 0x8c, 0x8e, 0xc3, 0x7c, 0xe8, 0xaa, 0x97, 0x21, 0x89, 0x64, 0x97, - 0x8c, 0x47, 0x78, 0x9c, 0xc2, 0xb7, 0x52, 0x16, 0x87, 0x4d, 0xf4, 0xb3, 0x50, 0xe2, 0x0f, 0x1b, - 0x61, 0x58, 0x96, 0xbd, 0x4e, 0x67, 0x51, 0xbc, 0x4d, 0x8a, 0x90, 0xec, 0x07, 0x69, 0x58, 0xd9, - 0x31, 0x26, 0x7b, 0x44, 0xba, 0x0d, 0x62, 0x61, 0x62, 0x52, 0xcf, 0x42, 0xdd, 0xb8, 0xbb, 0xb9, - 0xe2, 0xa9, 0x33, 0x49, 0x38, 0xd9, 0xeb, 0x84, 0x39, 0x5a, 0x3a, 0x96, 0xa3, 0xad, 0x40, 0xd6, - 0xa5, 0xae, 0x49, 0xa4, 0x2f, 0x12, 0x0d, 0xf5, 0x77, 0x53, 0x71, 0x5f, 0x53, 0x8e, 0x9e, 0x21, - 0x79, 0x91, 0xaa, 0x4d, 0x83, 0xa8, 0x3b, 0xf4, 0x11, 0x94, 0x7b, 0x5a, 0x03, 0x6b, 0xfd, 0x7a, - 0xe7, 0xdb, 0x7a, 0xaf, 0xb6, 0xdd, 0xab, 0x3d, 0x7a, 0xa0, 0x77, 0x3b, 0xdb, 0xdf, 0x79, 0xf8, - 0xf8, 0xc1, 0xd7, 0x94, 0x54, 0xb9, 0x72, 0x72, 0x5a, 0xb9, 0xd3, 0xae, 0x35, 0xb6, 0xc5, 0x91, - 0xd9, 0xa3, 0x2f, 0x7a, 0x86, 0xe3, 0x1b, 0x8f, 0x1e, 0x74, 0xa9, 0x33, 0x61, 0x18, 0xf4, 0x15, - 0x40, 0x1b, 0x1a, 0x6e, 0x6b, 0x7d, 0x3d, 0x74, 0x68, 0x8d, 0x7a, 0x43, 0x49, 0x8b, 0xcc, 0x67, - 0x83, 0x78, 0x2e, 0x09, 0x6a, 0x5a, 0xef, 0xe1, 0xa3, 0x27, 0x8d, 0x7a, 0x83, 0x1d, 0x82, 0x52, - 0xfc, 0x76, 0x8b, 0x5f, 0xda, 0xa9, 0x4b, 0x2f, 0xed, 0xe9, 0xdd, 0x9f, 0xbe, 0xe4, 0xee, 0xdf, - 0x80, 0x15, 0xd3, 0xa3, 0xbe, 0xaf, 0xb3, 0x74, 0x82, 0x58, 0x33, 0x09, 0xcb, 0x17, 0xce, 0xcf, - 0xd6, 0x6f, 0x34, 0x18, 0xbf, 0xc7, 0xd9, 0x52, 0xfd, 0x0d, 0x33, 0x46, 0xe2, 0x3d, 0xa9, 0x7f, - 0x30, 0xcf, 0x22, 0x33, 0xfb, 0xc8, 0x76, 0xc8, 0x80, 0xf8, 0xe8, 0x19, 0x2c, 0x9b, 0x1e, 0xb1, - 0x58, 0x9e, 0x60, 0x38, 0xf1, 0xaf, 0x81, 0x7f, 0x26, 0x31, 0x48, 0x8a, 0x04, 0xab, 0x8d, 0x48, - 0xaa, 0x37, 0x22, 0x26, 0x5e, 0x32, 0x2f, 0xb4, 0xd1, 0xc7, 0xb0, 0xec, 0x13, 0xc7, 0x76, 0xc7, - 0x2f, 0x74, 0x93, 0xba, 0x01, 0x79, 0x11, 0x3e, 0xbf, 0x5d, 0xa7, 0xb7, 0xa7, 0x6d, 0x33, 0xa9, - 0x86, 0x10, 0xaa, 0xa3, 0xf3, 0xb3, 0xf5, 0xa5, 0x8b, 0x34, 0xbc, 0x24, 0x35, 0xcb, 0x76, 0xb9, - 0x0d, 0x4b, 0x17, 0x47, 0x83, 0x56, 0xa4, 0xa7, 0xe0, 0x0e, 0x27, 0xf4, 0x04, 0xe8, 0x0e, 0xe4, - 0x3d, 0x32, 0xb0, 0xfd, 0xc0, 0x13, 0x66, 0x66, 0x9c, 0x88, 0xc2, 0xfc, 0x84, 0xf8, 0x58, 0xab, - 0xfc, 0x2b, 0x30, 0xd3, 0x23, 0x3b, 0x5a, 0x96, 0xed, 0x1b, 0x7b, 0x52, 0x65, 0x1e, 0x87, 0x4d, - 0xb6, 0x63, 0xc7, 0x7e, 0x14, 0xd6, 0xf1, 0xdf, 0x8c, 0xc6, 0xe3, 0x0f, 0xf9, 0xe9, 0x1a, 0x8f, - 0x30, 0xc2, 0x2f, 0x64, 0x33, 0xb1, 0x2f, 0x64, 0x57, 0x20, 0xeb, 0x90, 0x23, 0xe2, 0x88, 0x9b, - 0x1f, 0x8b, 0xc6, 0xbd, 0x07, 0x50, 0x0a, 0x3f, 0xc5, 0xe4, 0x5f, 0x72, 0xe4, 0x21, 0xd3, 0xaf, - 0xf5, 0x9e, 0x2a, 0x73, 0x08, 0x20, 0x27, 0x76, 0xb2, 0x78, 0x1a, 0x6c, 0x74, 0xda, 0x1b, 0xad, - 0x4d, 0x25, 0x7d, 0xef, 0xf7, 0x32, 0x50, 0x88, 0x1e, 0xa7, 0xd8, 0x4d, 0xd3, 0xd6, 0x9e, 0x87, - 0x47, 0x21, 0xa2, 0xb7, 0xc9, 0x31, 0x7a, 0x63, 0x5a, 0xd6, 0xfa, 0x48, 0xbc, 0xc6, 0x47, 0xec, - 0xb0, 0xa4, 0xf5, 0x16, 0xe4, 0x6b, 0xbd, 0x5e, 0x6b, 0xb3, 0xad, 0x35, 0x95, 0x4f, 0x53, 0xe5, - 0x2f, 0x9c, 0x9c, 0x56, 0x6e, 0x44, 0xa0, 0x9a, 0x2f, 0x36, 0x1f, 0x47, 0x35, 0x1a, 0x5a, 0xb7, - 0xaf, 0x35, 0x95, 0x4f, 0xd2, 0xb3, 0x28, 0x5e, 0xa6, 0xe1, 0x5f, 0x19, 0x15, 0xba, 0x58, 0xeb, - 0xd6, 0x30, 0xeb, 0xf0, 0xd3, 0xb4, 0xa8, 0xb6, 0x4d, 0x7b, 0xf4, 0xc8, 0xc8, 0xf0, 0x58, 0x9f, - 0x6b, 0xe1, 0x67, 0x7b, 0x9f, 0xcc, 0x8b, 0xcf, 0x4d, 0xa6, 0x2f, 0x6d, 0xc4, 0xb0, 0x26, 0xac, - 0x37, 0xfe, 0xc4, 0xc9, 0xd5, 0xcc, 0xcf, 0xf4, 0xd6, 0x63, 0x9e, 0x8a, 0x69, 0x51, 0x61, 0x01, - 0xef, 0xb6, 0xdb, 0x0c, 0xf4, 0x49, 0x66, 0x66, 0x76, 0x78, 0xec, 0xb2, 0x14, 0x1c, 0xdd, 0x85, - 0x7c, 0xf8, 0x02, 0xaa, 0x7c, 0x9a, 0x99, 0x19, 0x50, 0x23, 0x7c, 0xbe, 0xe5, 0x1d, 0x6e, 0xed, - 0xf6, 0xf9, 0x57, 0x85, 0x9f, 0x64, 0x67, 0x3b, 0x3c, 0x18, 0x07, 0x16, 0x3d, 0x76, 0xd9, 0x99, - 0x95, 0x85, 0xbd, 0x4f, 0xb3, 0xc2, 0x17, 0x44, 0x18, 0x59, 0xd5, 0x7b, 0x0b, 0xf2, 0x58, 0xfb, - 0x96, 0xf8, 0x00, 0xf1, 0x93, 0xdc, 0x8c, 0x1e, 0x4c, 0x3e, 0x26, 0x26, 0xeb, 0xad, 0x02, 0x39, - 0xac, 0xed, 0x74, 0x9e, 0x69, 0xca, 0x1f, 0xe6, 0x66, 0xf4, 0x60, 0x32, 0xa4, 0xfc, 0x33, 0xac, - 0x7c, 0x07, 0x77, 0xb7, 0x6a, 0x7c, 0x51, 0x66, 0xf5, 0x74, 0xbc, 0xd1, 0x81, 0xe1, 0x12, 0x6b, - 0xfa, 0xc9, 0x4d, 0xc4, 0xba, 0xf7, 0x0b, 0x90, 0x0f, 0x23, 0x5d, 0xb4, 0x06, 0xb9, 0xe7, 0x1d, - 0xfc, 0x54, 0xc3, 0xca, 0x9c, 0xb0, 0x72, 0xc8, 0x79, 0x2e, 0x72, 0x94, 0x0a, 0x2c, 0xec, 0xd4, - 0xda, 0xb5, 0x4d, 0x0d, 0x87, 0x55, 0xf9, 0x10, 0x20, 0xc3, 0xb5, 0xb2, 0x22, 0x3b, 0x88, 0x74, - 0xd6, 0x57, 0x7f, 0xf8, 0x93, 0xb5, 0xb9, 0x1f, 0xff, 0x64, 0x6d, 0xee, 0x93, 0xf3, 0xb5, 0xd4, - 0x0f, 0xcf, 0xd7, 0x52, 0x7f, 0x77, 0xbe, 0x96, 0xfa, 0xb7, 0xf3, 0xb5, 0xd4, 0x5e, 0x8e, 0x5f, - 0x2a, 0x8f, 0xff, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x5a, 0x0b, 0x70, 0x5e, 0xdb, 0x32, 0x00, 0x00, + 0xaa, 0x14, 0x55, 0x25, 0xbb, 0xc5, 0x42, 0x30, 0x27, 0x20, 0x7c, 0x82, 0x0b, 0xec, 0x06, 0xe1, + 0x08, 0x22, 0xe0, 0xc6, 0x81, 0x03, 0x07, 0x16, 0x4e, 0x43, 0x04, 0x41, 0x6c, 0x70, 0x81, 0x85, + 0x08, 0xd8, 0x00, 0xc2, 0x30, 0x3e, 0x70, 0x23, 0xe0, 0x42, 0x70, 0xe1, 0x40, 0xe4, 0x4f, 0x95, + 0xca, 0xea, 0xb2, 0x3d, 0xb3, 0xcb, 0xc5, 0x56, 0xbe, 0xf7, 0xbd, 0x97, 0x99, 0x2f, 0x33, 0x5f, + 0xbe, 0xf7, 0xb2, 0xe0, 0xde, 0xc0, 0x0e, 0x0e, 0xc6, 0x7b, 0x55, 0x93, 0x0e, 0xef, 0x5b, 0xd4, + 0x3c, 0x24, 0xde, 0x7d, 0xff, 0xd8, 0xf0, 0x86, 0x87, 0x76, 0x70, 0xdf, 0x18, 0xd9, 0xf7, 0x83, + 0xc9, 0x88, 0xf8, 0xd5, 0x91, 0x47, 0x03, 0x8a, 0x90, 0x00, 0x54, 0x43, 0x40, 0xf5, 0xe8, 0x61, + 0x79, 0x7d, 0x40, 0xe9, 0xc0, 0x21, 0xf7, 0x39, 0x62, 0x6f, 0xbc, 0x7f, 0x3f, 0xb0, 0x87, 0xc4, + 0x0f, 0x8c, 0xe1, 0x48, 0x08, 0x95, 0xd7, 0x66, 0x01, 0xd6, 0xd8, 0x33, 0x02, 0x9b, 0xba, 0x92, + 0xbf, 0x32, 0xa0, 0x03, 0xca, 0x7f, 0xde, 0x67, 0xbf, 0x04, 0x55, 0x5d, 0x87, 0x85, 0x67, 0xc4, + 0xf3, 0x6d, 0xea, 0xa2, 0x15, 0xc8, 0xda, 0xae, 0x45, 0x5e, 0xac, 0xa6, 0x2a, 0xa9, 0xb7, 0x32, + 0x58, 0x34, 0xd4, 0x07, 0x00, 0x2d, 0xf6, 0x43, 0x73, 0x03, 0x6f, 0x82, 0x14, 0x98, 0x3f, 0x24, + 0x13, 0x8e, 0x28, 0x60, 0xf6, 0x93, 0x51, 0x8e, 0x0c, 0x67, 0x35, 0x2d, 0x28, 0x47, 0x86, 0xa3, + 0x7e, 0x96, 0x82, 0x62, 0xcd, 0x75, 0x69, 0xc0, 0x7b, 0xf7, 0x11, 0x82, 0x8c, 0x6b, 0x0c, 0x89, + 0x14, 0xe2, 0xbf, 0x51, 0x03, 0x72, 0x8e, 0xb1, 0x47, 0x1c, 0x7f, 0x35, 0x5d, 0x99, 0x7f, 0xab, + 0xf8, 0xe8, 0x6b, 0xd5, 0x97, 0xa7, 0x5c, 0x8d, 0x29, 0xa9, 0x6e, 0x73, 0x34, 0x1f, 0x04, 0x96, + 0xa2, 0xe8, 0x9b, 0xb0, 0x60, 0xbb, 0x96, 0x6d, 0x12, 0x7f, 0x35, 0xc3, 0xb5, 0xac, 0x25, 0x69, + 0x99, 0x8e, 0xbe, 0x9e, 0xf9, 0xe1, 0xd9, 0xfa, 0x1c, 0x0e, 0x85, 0xca, 0xef, 0x42, 0x31, 0xa6, + 0x36, 0x61, 0x6e, 0x2b, 0x90, 0x3d, 0x32, 0x9c, 0x31, 0x91, 0xb3, 0x13, 0x8d, 0xf7, 0xd2, 0x4f, + 0x52, 0xea, 0x47, 0xb0, 0xd2, 0x36, 0x86, 0xc4, 0xda, 0x24, 0x2e, 0xf1, 0x6c, 0x13, 0x13, 0x9f, + 0x8e, 0x3d, 0x93, 0xb0, 0xb9, 0x1e, 0xda, 0xae, 0x15, 0xce, 0x95, 0xfd, 0x4e, 0xd6, 0xa2, 0x36, + 0xe0, 0x95, 0xa6, 0xed, 0x9b, 0x1e, 0x09, 0xc8, 0x17, 0x56, 0x32, 0x1f, 0x2a, 0x39, 0x4b, 0xc1, + 0xf2, 0xac, 0xf4, 0xcf, 0xc1, 0x4d, 0x66, 0x62, 0x4b, 0xf7, 0x24, 0x45, 0xf7, 0x47, 0xc4, 0xe4, + 0xca, 0x8a, 0x8f, 0xde, 0x4a, 0xb2, 0x50, 0xd2, 0x4c, 0xb6, 0xe6, 0xf0, 0x0d, 0xae, 0x26, 0x24, + 0xf4, 0x46, 0xc4, 0x44, 0x26, 0xdc, 0xb2, 0xe4, 0xa0, 0x67, 0xd4, 0xa7, 0xb9, 0xfa, 0xc4, 0x65, + 0xbc, 0x64, 0x9a, 0x5b, 0x73, 0x78, 0x25, 0x54, 0x16, 0xef, 0xa4, 0x0e, 0x90, 0x0f, 0x75, 0xab, + 0xdf, 0x4b, 0x41, 0x21, 0x64, 0xfa, 0xe8, 0xab, 0x50, 0x70, 0x0d, 0x97, 0xea, 0xe6, 0x68, 0xec, + 0xf3, 0x09, 0xcd, 0xd7, 0x4b, 0xe7, 0x67, 0xeb, 0xf9, 0xb6, 0xe1, 0xd2, 0x46, 0x77, 0xd7, 0xc7, + 0x79, 0xc6, 0x6e, 0x8c, 0xc6, 0x3e, 0x7a, 0x0d, 0x4a, 0x43, 0x32, 0xa4, 0xde, 0x44, 0xdf, 0x9b, + 0x04, 0xc4, 0x97, 0x66, 0x2b, 0x0a, 0x5a, 0x9d, 0x91, 0xd0, 0x07, 0xb0, 0x30, 0x10, 0x43, 0x5a, + 0x9d, 0xe7, 0xdb, 0xe7, 0xf5, 0xa4, 0xd1, 0xcf, 0x8c, 0x1a, 0x87, 0x32, 0xea, 0x6f, 0xa5, 0x60, + 0x25, 0xa2, 0x92, 0x5f, 0x1a, 0xdb, 0x1e, 0x19, 0x12, 0x37, 0xf0, 0xd1, 0xd7, 0x21, 0xe7, 0xd8, + 0x43, 0x3b, 0xf0, 0xa5, 0xcd, 0x5f, 0x4d, 0x52, 0x1b, 0x4d, 0x0a, 0x4b, 0x30, 0xaa, 0x41, 0xc9, + 0x23, 0x3e, 0xf1, 0x8e, 0xc4, 0x8e, 0x97, 0x16, 0xbd, 0x46, 0xf8, 0x82, 0x88, 0xba, 0x01, 0xf9, + 0xae, 0x63, 0x04, 0xfb, 0xd4, 0x1b, 0x22, 0x15, 0x4a, 0x86, 0x67, 0x1e, 0xd8, 0x01, 0x31, 0x83, + 0xb1, 0x17, 0x9e, 0xbe, 0x0b, 0x34, 0x74, 0x0b, 0xd2, 0x54, 0x74, 0x54, 0xa8, 0xe7, 0xce, 0xcf, + 0xd6, 0xd3, 0x9d, 0x1e, 0x4e, 0x53, 0x5f, 0x7d, 0x1f, 0x6e, 0x74, 0x9d, 0xf1, 0xc0, 0x76, 0x9b, + 0xc4, 0x37, 0x3d, 0x7b, 0xc4, 0xb4, 0xb3, 0x5d, 0xc9, 0x7c, 0x54, 0xb8, 0x2b, 0xd9, 0xef, 0xe8, + 0x68, 0xa7, 0xa7, 0x47, 0x5b, 0xfd, 0x8d, 0x34, 0xdc, 0xd0, 0xdc, 0x81, 0xed, 0x92, 0xb8, 0xf4, + 0x5d, 0x58, 0x22, 0x9c, 0xa8, 0x1f, 0x09, 0x77, 0x23, 0xf5, 0x2c, 0x0a, 0x6a, 0xe8, 0x83, 0x5a, + 0x33, 0x7e, 0xe1, 0x61, 0xd2, 0xf4, 0x5f, 0xd2, 0x9e, 0xe8, 0x1d, 0x34, 0x58, 0x18, 0xf1, 0x49, + 0xf8, 0x72, 0x79, 0xef, 0x26, 0xe9, 0x7a, 0x69, 0x9e, 0xa1, 0x93, 0x90, 0xb2, 0x3f, 0x8d, 0x93, + 0xf8, 0xeb, 0x34, 0x2c, 0xb7, 0xa9, 0x75, 0xc1, 0x0e, 0x65, 0xc8, 0x1f, 0x50, 0x3f, 0x88, 0x39, + 0xc4, 0xa8, 0x8d, 0x9e, 0x40, 0x7e, 0x24, 0x97, 0x4f, 0xae, 0xfe, 0x9d, 0xe4, 0x21, 0x0b, 0x0c, + 0x8e, 0xd0, 0xe8, 0x7d, 0x28, 0x84, 0x47, 0x86, 0xcd, 0xf6, 0x73, 0x6c, 0x9c, 0x29, 0x1e, 0x7d, + 0x00, 0x39, 0xb1, 0x08, 0xab, 0x19, 0x2e, 0x79, 0xf7, 0x73, 0xd9, 0x1c, 0x4b, 0x21, 0xb4, 0x09, + 0xf9, 0xc0, 0xf1, 0x75, 0xdb, 0xdd, 0xa7, 0xab, 0x59, 0xae, 0x60, 0x3d, 0xd1, 0xc9, 0x50, 0x8b, + 0xf4, 0xb7, 0x7b, 0x2d, 0x77, 0x9f, 0xd6, 0x8b, 0xe7, 0x67, 0xeb, 0x0b, 0xb2, 0x81, 0x17, 0x02, + 0xc7, 0x67, 0x3f, 0xd0, 0x1d, 0xc8, 0xec, 0xdb, 0x23, 0x7f, 0x35, 0x57, 0x49, 0xbd, 0x95, 0xaf, + 0xe7, 0xcf, 0xcf, 0xd6, 0x33, 0x1b, 0xad, 0x6e, 0x0f, 0x73, 0xaa, 0xfa, 0xdb, 0x29, 0x28, 0xc6, + 0x74, 0xa0, 0x57, 0x01, 0x02, 0x6f, 0xec, 0x07, 0xba, 0x47, 0x69, 0xc0, 0x4d, 0x59, 0xc2, 0x05, + 0x4e, 0xc1, 0x94, 0x06, 0xa8, 0x0a, 0x37, 0x4d, 0xe2, 0x05, 0xba, 0xed, 0xfb, 0x63, 0xe2, 0xe9, + 0xfe, 0x78, 0xef, 0x63, 0x62, 0x06, 0xdc, 0xac, 0x25, 0x7c, 0x83, 0xb1, 0x5a, 0x9c, 0xd3, 0x13, + 0x0c, 0xf4, 0x18, 0x6e, 0xc5, 0xf1, 0xa3, 0xf1, 0x9e, 0x63, 0x9b, 0x3a, 0x5b, 0xea, 0x79, 0x2e, + 0x72, 0x73, 0x2a, 0xd2, 0xe5, 0xbc, 0xa7, 0x64, 0xa2, 0xfe, 0x38, 0x05, 0x0a, 0x36, 0xf6, 0x83, + 0x1d, 0x32, 0xdc, 0x23, 0x5e, 0x2f, 0x30, 0x82, 0xb1, 0x8f, 0x6e, 0x41, 0xce, 0x21, 0x86, 0x45, + 0x3c, 0x3e, 0xa8, 0x3c, 0x96, 0x2d, 0xb4, 0xcb, 0xce, 0xb7, 0x61, 0x1e, 0x18, 0x7b, 0xb6, 0x63, + 0x07, 0x13, 0x3e, 0x94, 0xa5, 0xe4, 0x0d, 0x3e, 0xab, 0xb3, 0x8a, 0x63, 0x82, 0xf8, 0x82, 0x1a, + 0xb4, 0x0a, 0x0b, 0x43, 0xe2, 0xfb, 0xc6, 0x80, 0xf0, 0x91, 0x16, 0x70, 0xd8, 0x54, 0xdf, 0x87, + 0x52, 0x5c, 0x0e, 0x15, 0x61, 0x61, 0xb7, 0xfd, 0xb4, 0xdd, 0x79, 0xde, 0x56, 0xe6, 0xd0, 0x32, + 0x14, 0x77, 0xdb, 0x58, 0xab, 0x35, 0xb6, 0x6a, 0xf5, 0x6d, 0x4d, 0x49, 0xa1, 0x45, 0x28, 0x4c, + 0x9b, 0x69, 0xf5, 0x4f, 0x52, 0x00, 0xcc, 0xdc, 0x72, 0x52, 0xef, 0x41, 0xd6, 0x0f, 0x8c, 0x40, + 0xec, 0xd9, 0xa5, 0x47, 0x6f, 0x5c, 0xb6, 0xc2, 0x72, 0xbc, 0xec, 0x1f, 0xc1, 0x42, 0x24, 0x3e, + 0xc2, 0xf4, 0x85, 0x11, 0x32, 0xf7, 0x61, 0x58, 0x96, 0x27, 0x07, 0xce, 0x7f, 0xab, 0xef, 0x43, + 0x96, 0x4b, 0x5f, 0x1c, 0x6e, 0x1e, 0x32, 0x4d, 0xf6, 0x2b, 0x85, 0x0a, 0x90, 0xc5, 0x5a, 0xad, + 0xf9, 0x1d, 0x25, 0x8d, 0x14, 0x28, 0x35, 0x5b, 0xbd, 0x46, 0xa7, 0xdd, 0xd6, 0x1a, 0x7d, 0xad, + 0xa9, 0xcc, 0xab, 0x77, 0x21, 0xdb, 0x1a, 0x32, 0xcd, 0x77, 0xd8, 0x81, 0xd8, 0x27, 0x1e, 0x71, + 0xcd, 0xf0, 0x9c, 0x4d, 0x09, 0xea, 0x67, 0x25, 0xc8, 0xee, 0xd0, 0xb1, 0x1b, 0xa0, 0x47, 0x31, + 0xa7, 0xb6, 0x94, 0x1c, 0x3f, 0x70, 0x60, 0xb5, 0x3f, 0x19, 0x11, 0xe9, 0xf4, 0x6e, 0x41, 0x4e, + 0x1c, 0x1d, 0x39, 0x1d, 0xd9, 0x62, 0xf4, 0xc0, 0xf0, 0x06, 0x24, 0x90, 0xf3, 0x91, 0x2d, 0xf4, + 0x16, 0xbb, 0xcf, 0x0c, 0x8b, 0xba, 0xce, 0x84, 0x9f, 0xb0, 0xbc, 0xb8, 0xb4, 0x30, 0x31, 0xac, + 0x8e, 0xeb, 0x4c, 0x70, 0xc4, 0x45, 0x5b, 0x50, 0xda, 0xb3, 0x5d, 0x4b, 0xa7, 0x23, 0x71, 0x05, + 0x64, 0x2f, 0x3f, 0x8f, 0x62, 0x54, 0x75, 0xdb, 0xb5, 0x3a, 0x02, 0x8c, 0x8b, 0x7b, 0xd3, 0x06, + 0x6a, 0xc3, 0xd2, 0x11, 0x75, 0xc6, 0x43, 0x12, 0xe9, 0xca, 0x71, 0x5d, 0x6f, 0x5e, 0xae, 0xeb, + 0x19, 0xc7, 0x87, 0xda, 0x16, 0x8f, 0xe2, 0x4d, 0xf4, 0x14, 0x16, 0x83, 0xe1, 0x68, 0xdf, 0x8f, + 0xd4, 0x2d, 0x70, 0x75, 0x5f, 0xb9, 0xc2, 0x60, 0x0c, 0x1e, 0x6a, 0x2b, 0x05, 0xb1, 0x16, 0xda, + 0x84, 0xa2, 0x49, 0x5d, 0xdf, 0xf6, 0x03, 0xe2, 0x9a, 0x93, 0xd5, 0x3c, 0xb7, 0xfd, 0x15, 0xb3, + 0x6c, 0x4c, 0xc1, 0x38, 0x2e, 0x59, 0xfe, 0xc1, 0x3c, 0x14, 0x63, 0x26, 0x40, 0x3d, 0x28, 0x8e, + 0x3c, 0x3a, 0x32, 0x06, 0xfc, 0x3e, 0x94, 0x8b, 0xfa, 0xf0, 0x73, 0x99, 0xaf, 0xda, 0x9d, 0x0a, + 0xe2, 0xb8, 0x16, 0xf4, 0x0e, 0x94, 0x5c, 0xea, 0x7a, 0xc4, 0x1c, 0x7b, 0xbe, 0x7d, 0x24, 0x16, + 0x3d, 0x5f, 0x57, 0xce, 0xcf, 0xd6, 0x4b, 0x6d, 0xea, 0xe2, 0x90, 0x8e, 0x2f, 0xa0, 0xd4, 0xd3, + 0x34, 0x14, 0x63, 0x2a, 0xd1, 0x3d, 0xc8, 0xe3, 0x2e, 0x6e, 0x3d, 0xab, 0xf5, 0x35, 0x65, 0xae, + 0x7c, 0xe7, 0xe4, 0xb4, 0xb2, 0xca, 0xc7, 0x10, 0xef, 0xb6, 0xeb, 0xd9, 0x47, 0x6c, 0xe7, 0xbf, + 0x05, 0x0b, 0x21, 0x34, 0x55, 0xfe, 0xf2, 0xc9, 0x69, 0xe5, 0x95, 0x59, 0x68, 0x0c, 0x89, 0x7b, + 0x5b, 0x35, 0xac, 0x35, 0x95, 0x74, 0x32, 0x12, 0xf7, 0x0e, 0x0c, 0x8f, 0x58, 0xe8, 0x2b, 0x90, + 0x93, 0xc0, 0xf9, 0x72, 0xf9, 0xe4, 0xb4, 0x72, 0x6b, 0x16, 0x38, 0xc5, 0xe1, 0xde, 0x76, 0xed, + 0x99, 0xa6, 0x64, 0x92, 0x71, 0xb8, 0xe7, 0x18, 0x47, 0x04, 0xbd, 0x01, 0x59, 0x01, 0xcb, 0x96, + 0x6f, 0x9f, 0x9c, 0x56, 0xbe, 0xf4, 0x92, 0x3a, 0x86, 0x2a, 0xaf, 0xfe, 0xe6, 0x1f, 0xac, 0xcd, + 0xfd, 0xf9, 0x1f, 0xae, 0x29, 0xb3, 0xec, 0xf2, 0xff, 0xa6, 0x60, 0xf1, 0xc2, 0x8e, 0x43, 0x2a, + 0xe4, 0x5c, 0x6a, 0xd2, 0x91, 0xb8, 0x5c, 0xf3, 0x75, 0x38, 0x3f, 0x5b, 0xcf, 0xb5, 0x69, 0x83, + 0x8e, 0x26, 0x58, 0x72, 0xd0, 0xd3, 0x99, 0xf0, 0xe0, 0xf1, 0xe7, 0xdc, 0xce, 0x89, 0x01, 0xc2, + 0x87, 0xb0, 0x68, 0x79, 0xf6, 0x11, 0xf1, 0x74, 0x93, 0xba, 0xfb, 0xf6, 0x40, 0x5e, 0x9c, 0xe5, + 0xc4, 0x18, 0x96, 0x03, 0x71, 0x49, 0x08, 0x34, 0x38, 0xfe, 0xa7, 0x08, 0x0d, 0xca, 0x23, 0x28, + 0xc5, 0x0f, 0x08, 0xbb, 0xcd, 0x7c, 0xfb, 0x97, 0x89, 0x0c, 0x56, 0x79, 0x68, 0x8b, 0x0b, 0x8c, + 0x22, 0x42, 0xd5, 0x37, 0x21, 0x33, 0xa4, 0x96, 0xd0, 0xb3, 0x58, 0xbf, 0xc9, 0x22, 0x94, 0x7f, + 0x3a, 0x5b, 0x2f, 0x52, 0xbf, 0xba, 0x61, 0x3b, 0x64, 0x87, 0x5a, 0x04, 0x73, 0x00, 0xf3, 0xb5, + 0xe1, 0x09, 0x95, 0xb7, 0x81, 0x6c, 0xaa, 0xdf, 0x4f, 0x41, 0x86, 0x39, 0x31, 0xf4, 0x65, 0xc8, + 0xd4, 0x5b, 0xed, 0xa6, 0x32, 0x57, 0xbe, 0x71, 0x72, 0x5a, 0x59, 0xe4, 0xd6, 0x62, 0x0c, 0x76, + 0x18, 0xd0, 0x3a, 0xe4, 0x9e, 0x75, 0xb6, 0x77, 0x77, 0xd8, 0xce, 0xbb, 0x79, 0x72, 0x5a, 0x59, + 0x8e, 0xd8, 0xc2, 0x9e, 0xe8, 0x55, 0xc8, 0xf6, 0x77, 0xba, 0x1b, 0x3d, 0x25, 0x5d, 0x46, 0x27, + 0xa7, 0x95, 0xa5, 0x88, 0xcf, 0xa7, 0x83, 0x5e, 0x83, 0x6c, 0xbb, 0xdb, 0xea, 0x6a, 0xca, 0x7c, + 0xf9, 0xd6, 0xc9, 0x69, 0x05, 0x45, 0x6c, 0x9e, 0x63, 0x74, 0xed, 0x11, 0x29, 0xdf, 0x90, 0x7b, + 0xa2, 0x10, 0xf1, 0xd4, 0x1f, 0xa5, 0xa0, 0x18, 0x3b, 0xe4, 0x6c, 0x5b, 0x37, 0xb5, 0x8d, 0xda, + 0xee, 0x76, 0x5f, 0x99, 0x8b, 0x6d, 0xeb, 0x18, 0xa4, 0x49, 0xf6, 0x8d, 0xb1, 0xc3, 0x7c, 0x2b, + 0x34, 0x3a, 0xed, 0x5e, 0xab, 0xd7, 0xd7, 0xda, 0x7d, 0x25, 0x55, 0x5e, 0x3d, 0x39, 0xad, 0xac, + 0xcc, 0x82, 0x37, 0xc6, 0x8e, 0xc3, 0x36, 0x76, 0xa3, 0xd6, 0xd8, 0xe2, 0x27, 0x65, 0xba, 0xb1, + 0x63, 0xa8, 0x86, 0x61, 0x1e, 0x10, 0x0b, 0xbd, 0x0d, 0x85, 0xa6, 0xb6, 0xad, 0x6d, 0xd6, 0xf8, + 0x8d, 0x52, 0x7e, 0xf5, 0xe4, 0xb4, 0x72, 0xfb, 0xe5, 0xde, 0x1d, 0x32, 0x30, 0x02, 0x62, 0xcd, + 0x6c, 0xf0, 0x18, 0x44, 0xfd, 0xef, 0x34, 0x2c, 0x62, 0x96, 0xa0, 0x7b, 0x41, 0x97, 0x3a, 0xb6, + 0x39, 0x41, 0x5d, 0x28, 0x98, 0xd4, 0xb5, 0xec, 0x98, 0x6f, 0x7a, 0x74, 0x49, 0x90, 0x36, 0x95, + 0x0a, 0x5b, 0x8d, 0x50, 0x12, 0x4f, 0x95, 0xa0, 0xfb, 0x90, 0xb5, 0x88, 0x63, 0x4c, 0x64, 0xb4, + 0x78, 0xbb, 0x2a, 0x4a, 0x00, 0xd5, 0xb0, 0x04, 0x50, 0x6d, 0xca, 0x12, 0x00, 0x16, 0x38, 0x9e, + 0x15, 0x19, 0x2f, 0x74, 0x23, 0x08, 0xc8, 0x70, 0x14, 0x88, 0x3d, 0x92, 0xc1, 0xc5, 0xa1, 0xf1, + 0xa2, 0x26, 0x49, 0xe8, 0x21, 0xe4, 0x8e, 0x6d, 0xd7, 0xa2, 0xc7, 0x32, 0x1a, 0xbc, 0x42, 0xa9, + 0x04, 0xaa, 0x27, 0x2c, 0x0c, 0x9a, 0x19, 0x26, 0xdb, 0x66, 0xed, 0x4e, 0x5b, 0x0b, 0xb7, 0x99, + 0xe4, 0x77, 0xdc, 0x36, 0x75, 0x99, 0xf7, 0x80, 0x4e, 0x5b, 0xdf, 0xa8, 0xb5, 0xb6, 0x77, 0x31, + 0xdb, 0x6a, 0x2b, 0x27, 0xa7, 0x15, 0x25, 0x82, 0x6c, 0x18, 0xb6, 0xc3, 0xd2, 0x93, 0xdb, 0x30, + 0x5f, 0x6b, 0x7f, 0x47, 0x49, 0x97, 0x95, 0x93, 0xd3, 0x4a, 0x29, 0x62, 0xd7, 0xdc, 0xc9, 0xd4, + 0xee, 0xb3, 0xfd, 0xaa, 0x7f, 0x33, 0x0f, 0xa5, 0xdd, 0x91, 0x65, 0x04, 0x44, 0x9c, 0x52, 0x54, + 0x81, 0xe2, 0xc8, 0xf0, 0x0c, 0xc7, 0x21, 0x8e, 0xed, 0x0f, 0x65, 0x71, 0x23, 0x4e, 0x42, 0xef, + 0x7e, 0x5e, 0x33, 0xd6, 0xf3, 0xec, 0xe4, 0x7d, 0xef, 0x5f, 0xd7, 0x53, 0xa1, 0x41, 0x77, 0x61, + 0x69, 0x5f, 0x8c, 0x56, 0x37, 0x4c, 0xbe, 0xb0, 0xf3, 0x7c, 0x61, 0xab, 0x49, 0x0b, 0x1b, 0x1f, + 0x56, 0x55, 0x4e, 0xb2, 0xc6, 0xa5, 0xf0, 0xe2, 0x7e, 0xbc, 0x89, 0x1e, 0xc3, 0xc2, 0x90, 0xba, + 0x76, 0x40, 0xbd, 0xeb, 0x57, 0x21, 0x44, 0xa2, 0x7b, 0x70, 0x83, 0x2d, 0x6e, 0x38, 0x1e, 0xce, + 0xe6, 0x21, 0x44, 0x1a, 0x2f, 0x0f, 0x8d, 0x17, 0xb2, 0x43, 0xcc, 0xc8, 0xa8, 0x0e, 0x59, 0xea, + 0xb1, 0x18, 0x35, 0xc7, 0x87, 0xfb, 0xf6, 0xb5, 0xc3, 0x15, 0x8d, 0x0e, 0x93, 0xc1, 0x42, 0x54, + 0xfd, 0x06, 0x2c, 0x5e, 0x98, 0x04, 0x0b, 0xcd, 0xba, 0xb5, 0xdd, 0x9e, 0xa6, 0xcc, 0xa1, 0x12, + 0xe4, 0x1b, 0x9d, 0x76, 0xbf, 0xd5, 0xde, 0x65, 0xb1, 0x65, 0x09, 0xf2, 0xb8, 0xb3, 0xbd, 0x5d, + 0xaf, 0x35, 0x9e, 0x2a, 0x69, 0xb5, 0x0a, 0xc5, 0x98, 0x36, 0xb4, 0x04, 0xd0, 0xeb, 0x77, 0xba, + 0xfa, 0x46, 0x0b, 0xf7, 0xfa, 0x22, 0x32, 0xed, 0xf5, 0x6b, 0xb8, 0x2f, 0x09, 0x29, 0xf5, 0x3f, + 0xd3, 0xe1, 0x8a, 0xca, 0x60, 0xb4, 0x7e, 0x31, 0x18, 0xbd, 0x62, 0xf0, 0x32, 0x1c, 0x9d, 0x36, + 0xa2, 0xa0, 0xf4, 0x5d, 0x00, 0xbe, 0x71, 0x88, 0xa5, 0x1b, 0x81, 0x5c, 0xf8, 0xf2, 0x4b, 0x46, + 0xee, 0x87, 0x35, 0x36, 0x5c, 0x90, 0xe8, 0x5a, 0x80, 0x3e, 0x80, 0x92, 0x49, 0x87, 0x23, 0x87, + 0x48, 0xe1, 0xf9, 0x6b, 0x85, 0x8b, 0x11, 0xbe, 0x16, 0xc4, 0xc3, 0xe1, 0xcc, 0xc5, 0x80, 0xfd, + 0xd7, 0x53, 0xa1, 0x65, 0x12, 0x22, 0xe0, 0x12, 0xe4, 0x77, 0xbb, 0xcd, 0x5a, 0xbf, 0xd5, 0xde, + 0x54, 0x52, 0x08, 0x20, 0xc7, 0x4d, 0xdd, 0x54, 0xd2, 0x2c, 0x72, 0x6f, 0x74, 0x76, 0xba, 0xdb, + 0x1a, 0xf7, 0x58, 0x68, 0x05, 0x94, 0xd0, 0xd8, 0x3a, 0x37, 0xa4, 0xd6, 0x54, 0x32, 0xe8, 0x26, + 0x2c, 0x47, 0x54, 0x29, 0x99, 0x45, 0xb7, 0x00, 0x45, 0xc4, 0xa9, 0x8a, 0x9c, 0xfa, 0xab, 0xb0, + 0xdc, 0xa0, 0x6e, 0x60, 0xd8, 0x6e, 0x94, 0xd5, 0x3c, 0x62, 0x93, 0x96, 0x24, 0xdd, 0x96, 0xb5, + 0xa9, 0xfa, 0xf2, 0xf9, 0xd9, 0x7a, 0x31, 0x82, 0xb6, 0x9a, 0x3c, 0x3c, 0x93, 0x0d, 0x8b, 0x9d, + 0xdf, 0x91, 0x6d, 0x71, 0xe3, 0x66, 0xeb, 0x0b, 0xe7, 0x67, 0xeb, 0xf3, 0xdd, 0x56, 0x13, 0x33, + 0x1a, 0xfa, 0x32, 0x14, 0xc8, 0x0b, 0x3b, 0xd0, 0x4d, 0x76, 0xab, 0x31, 0x03, 0x66, 0x71, 0x9e, + 0x11, 0x1a, 0xd4, 0x22, 0x6a, 0x1d, 0xa0, 0x4b, 0xbd, 0x40, 0xf6, 0xfc, 0x0e, 0x64, 0x47, 0xd4, + 0xe3, 0xd5, 0x94, 0x4b, 0x6b, 0x7c, 0x0c, 0x2e, 0x36, 0x2a, 0x16, 0x60, 0xf5, 0xfb, 0xf3, 0x00, + 0x7d, 0xc3, 0x3f, 0x94, 0x4a, 0x9e, 0x40, 0x21, 0xaa, 0x97, 0xca, 0xb2, 0xcc, 0x95, 0xab, 0x1d, + 0x81, 0xd1, 0xe3, 0x70, 0xb3, 0x89, 0x7c, 0x2d, 0x31, 0xad, 0x0e, 0x3b, 0x4a, 0x4a, 0x79, 0x2e, + 0x26, 0x65, 0x2c, 0x48, 0x20, 0x9e, 0x27, 0x57, 0x9e, 0xfd, 0x44, 0x0d, 0x7e, 0x2d, 0x08, 0xa3, + 0xc9, 0x88, 0x3f, 0xb1, 0x10, 0x35, 0xb3, 0x22, 0x5b, 0x73, 0x78, 0x2a, 0x87, 0x3e, 0x84, 0x22, + 0x9b, 0xb7, 0xee, 0x73, 0x9e, 0x0c, 0xf6, 0x2f, 0x35, 0x95, 0xd0, 0x80, 0x61, 0x34, 0xb5, 0xf2, + 0xab, 0x00, 0xc6, 0x68, 0xe4, 0xd8, 0xc4, 0xd2, 0xf7, 0x26, 0x3c, 0xba, 0x2f, 0xe0, 0x82, 0xa4, + 0xd4, 0x27, 0xec, 0xb8, 0x84, 0x6c, 0x23, 0xe0, 0x11, 0xfb, 0x35, 0x06, 0x94, 0xe8, 0x5a, 0x50, + 0x57, 0x60, 0xc9, 0x1b, 0xbb, 0xcc, 0xa0, 0x72, 0x74, 0xea, 0x1f, 0xa7, 0xe1, 0x95, 0x36, 0x09, + 0x8e, 0xa9, 0x77, 0x58, 0x0b, 0x02, 0xc3, 0x3c, 0x18, 0x12, 0x57, 0x2e, 0x5f, 0x2c, 0x89, 0x4a, + 0x5d, 0x48, 0xa2, 0x56, 0x61, 0xc1, 0x70, 0x6c, 0xc3, 0x27, 0x22, 0xf4, 0x2b, 0xe0, 0xb0, 0xc9, + 0x52, 0x3d, 0x96, 0x38, 0x12, 0xdf, 0x27, 0xa2, 0xd2, 0xc3, 0x06, 0x1e, 0x12, 0xd0, 0x77, 0xe1, + 0x96, 0x0c, 0xf2, 0x8c, 0xa8, 0x2b, 0x96, 0xc4, 0x84, 0x25, 0x63, 0x2d, 0x31, 0x93, 0x4d, 0x1e, + 0x9c, 0x8c, 0x02, 0xa7, 0xe4, 0xce, 0x28, 0x90, 0x31, 0xe5, 0x8a, 0x95, 0xc0, 0x2a, 0x6f, 0xc2, + 0xed, 0x4b, 0x45, 0xbe, 0x50, 0x25, 0xe9, 0xef, 0xd3, 0x00, 0xad, 0x6e, 0x6d, 0x47, 0x1a, 0xa9, + 0x09, 0xb9, 0x7d, 0x63, 0x68, 0x3b, 0x93, 0xab, 0x3c, 0xe0, 0x14, 0x5f, 0xad, 0x09, 0x73, 0x6c, + 0x70, 0x19, 0x2c, 0x65, 0x79, 0x1e, 0x3b, 0xde, 0x73, 0x49, 0x10, 0xe5, 0xb1, 0xbc, 0xc5, 0x86, + 0xe1, 0x19, 0x6e, 0xb4, 0x75, 0x45, 0x83, 0x2d, 0x00, 0x0b, 0x79, 0x8e, 0x8d, 0x49, 0xe8, 0xb6, + 0x64, 0x13, 0x6d, 0xf1, 0x7a, 0x2d, 0xf1, 0x8e, 0x88, 0xb5, 0x9a, 0xe5, 0x46, 0xbd, 0x6e, 0x3c, + 0x58, 0xc2, 0x85, 0xed, 0x22, 0xe9, 0xf2, 0xfb, 0x3c, 0x64, 0x9a, 0xb2, 0xbe, 0x90, 0x8d, 0x1e, + 0xc0, 0xe2, 0x85, 0x79, 0xbe, 0x54, 0x40, 0x68, 0x75, 0x9f, 0xbd, 0xa3, 0x64, 0xe4, 0xaf, 0x6f, + 0x28, 0x39, 0xf5, 0xaf, 0xe6, 0x85, 0xa3, 0x91, 0x56, 0x4d, 0x7e, 0xa7, 0xc8, 0xf3, 0xdd, 0x6d, + 0x52, 0x47, 0x3a, 0x80, 0x37, 0xaf, 0xf6, 0x3f, 0x2c, 0x8f, 0xe4, 0x70, 0x1c, 0x09, 0xa2, 0x75, + 0x28, 0x8a, 0x5d, 0xac, 0xb3, 0x03, 0xc7, 0xcd, 0xba, 0x88, 0x41, 0x90, 0x98, 0x24, 0xba, 0x0b, + 0x4b, 0xbc, 0xe0, 0xe4, 0x1f, 0x10, 0x4b, 0x60, 0x32, 0x1c, 0xb3, 0x18, 0x51, 0x39, 0x6c, 0x07, + 0x4a, 0x92, 0xa0, 0xf3, 0x6c, 0x20, 0xcb, 0x07, 0x74, 0xef, 0xba, 0x01, 0x09, 0x11, 0x9e, 0x24, + 0x14, 0x47, 0xd3, 0x86, 0xfa, 0x8b, 0x90, 0x0f, 0x07, 0x8b, 0x56, 0x61, 0xbe, 0xdf, 0xe8, 0x2a, + 0x73, 0xe5, 0xe5, 0x93, 0xd3, 0x4a, 0x31, 0x24, 0xf7, 0x1b, 0x5d, 0xc6, 0xd9, 0x6d, 0x76, 0x95, + 0xd4, 0x45, 0xce, 0x6e, 0xb3, 0x8b, 0xca, 0x90, 0xe9, 0x35, 0xfa, 0xdd, 0x30, 0x3e, 0x0b, 0x59, + 0x8c, 0x56, 0xce, 0xb0, 0xf8, 0x4c, 0xdd, 0x87, 0x62, 0xac, 0x77, 0xf4, 0x3a, 0x2c, 0xb4, 0xda, + 0x9b, 0x58, 0xeb, 0xf5, 0x94, 0x39, 0x91, 0x1e, 0xc4, 0xb8, 0x2d, 0x77, 0xc0, 0xd6, 0x0e, 0xbd, + 0x0a, 0x99, 0xad, 0x0e, 0xbb, 0xf7, 0x45, 0xfe, 0x11, 0x43, 0x6c, 0x51, 0x3f, 0x28, 0xdf, 0x94, + 0x81, 0x5f, 0x5c, 0xb1, 0xfa, 0x7b, 0x29, 0xc8, 0x89, 0x83, 0x96, 0xb8, 0x88, 0xb5, 0x69, 0x52, + 0x24, 0xd2, 0xc6, 0x37, 0x2f, 0x4f, 0xf1, 0xaa, 0x32, 0x23, 0x13, 0x5b, 0x33, 0x94, 0x2b, 0xbf, + 0x07, 0xa5, 0x38, 0xe3, 0x0b, 0x6d, 0xcc, 0xef, 0x42, 0x91, 0xed, 0xfd, 0x30, 0xd5, 0x7b, 0x04, + 0x39, 0xe1, 0x2c, 0xa2, 0x7b, 0xe8, 0xf2, 0x7c, 0x53, 0x22, 0xd1, 0x13, 0x58, 0x10, 0x39, 0x6a, + 0x58, 0xcb, 0x5e, 0xbb, 0xfa, 0x84, 0xe1, 0x10, 0xae, 0x7e, 0x08, 0x99, 0x2e, 0x21, 0x1e, 0xb3, + 0xbd, 0x4b, 0x2d, 0x32, 0xbd, 0xba, 0x65, 0x7a, 0x6d, 0x91, 0x56, 0x93, 0xa5, 0xd7, 0x16, 0x69, + 0x59, 0x51, 0x3d, 0x2e, 0x1d, 0xab, 0xc7, 0xf5, 0xa1, 0xf4, 0x9c, 0xd8, 0x83, 0x83, 0x80, 0x58, + 0x5c, 0xd1, 0xdb, 0x90, 0x19, 0x91, 0x68, 0xf0, 0xab, 0x89, 0x9b, 0x8f, 0x10, 0x0f, 0x73, 0x14, + 0xf3, 0x31, 0xc7, 0x5c, 0x5a, 0x3e, 0xc0, 0xc8, 0x96, 0xfa, 0x77, 0x69, 0x58, 0x6a, 0xf9, 0xfe, + 0xd8, 0x70, 0xcd, 0x30, 0xaa, 0xfb, 0xe6, 0xc5, 0xa8, 0x2e, 0xf1, 0xa5, 0xea, 0xa2, 0xc8, 0xc5, + 0x32, 0xa3, 0xbc, 0x59, 0xd3, 0xd1, 0xcd, 0xaa, 0xfe, 0x47, 0x2a, 0xac, 0x25, 0xde, 0x8d, 0xb9, + 0x02, 0x91, 0x23, 0xc6, 0x35, 0x91, 0x5d, 0xf7, 0xd0, 0xa5, 0xc7, 0x2e, 0xcb, 0x5e, 0xb1, 0xd6, + 0xd6, 0x9e, 0x2b, 0x29, 0xb1, 0x3d, 0x2f, 0x80, 0x30, 0x71, 0xc9, 0x31, 0xd3, 0xd4, 0xd5, 0xda, + 0x4d, 0x16, 0x85, 0xa5, 0x13, 0x34, 0x75, 0x89, 0x6b, 0xd9, 0xee, 0x00, 0xbd, 0x0e, 0xb9, 0x56, + 0xaf, 0xb7, 0xcb, 0x53, 0xc8, 0x57, 0x4e, 0x4e, 0x2b, 0x37, 0x2f, 0xa0, 0x78, 0x1d, 0xd9, 0x62, + 0x20, 0x96, 0x02, 0xb1, 0xf8, 0x2c, 0x01, 0xc4, 0x62, 0x6b, 0x01, 0xc2, 0x9d, 0x7e, 0xad, 0xaf, + 0x29, 0xd9, 0x04, 0x10, 0xa6, 0xec, 0xaf, 0x3c, 0x6e, 0xff, 0x9c, 0x06, 0xa5, 0x66, 0x9a, 0x64, + 0x14, 0x30, 0xbe, 0xcc, 0x3a, 0xfb, 0x90, 0x1f, 0xb1, 0x5f, 0x36, 0x09, 0x23, 0xa8, 0x27, 0x89, + 0x6f, 0xad, 0x33, 0x72, 0x55, 0x4c, 0x1d, 0x52, 0xb3, 0x86, 0xb6, 0xef, 0xdb, 0xd4, 0x15, 0x34, + 0x1c, 0x69, 0x2a, 0xff, 0x57, 0x0a, 0x6e, 0x26, 0x20, 0xd0, 0x03, 0xc8, 0x78, 0xd4, 0x09, 0xd7, + 0xf0, 0xce, 0x65, 0x65, 0x62, 0x26, 0x8a, 0x39, 0x12, 0xad, 0x01, 0x18, 0xe3, 0x80, 0x1a, 0xbc, + 0x7f, 0x51, 0x5c, 0xc3, 0x31, 0x0a, 0x7a, 0x0e, 0x39, 0x9f, 0x98, 0x1e, 0x09, 0xe3, 0xec, 0x0f, + 0x7f, 0xd2, 0xd1, 0x57, 0x7b, 0x5c, 0x0d, 0x96, 0xea, 0xca, 0x55, 0xc8, 0x09, 0x0a, 0xdb, 0xf6, + 0x96, 0x11, 0x18, 0xf2, 0x11, 0x81, 0xff, 0x66, 0xbb, 0xc9, 0x70, 0x06, 0xe1, 0x6e, 0x32, 0x9c, + 0x81, 0xfa, 0x97, 0x69, 0x00, 0xed, 0x45, 0x40, 0x3c, 0xd7, 0x70, 0x1a, 0x35, 0xa4, 0xc5, 0x6e, + 0x06, 0x31, 0xdb, 0xaf, 0x26, 0xbe, 0x9b, 0x44, 0x12, 0xd5, 0x46, 0x2d, 0xe1, 0x6e, 0xb8, 0x0d, + 0xf3, 0x63, 0x4f, 0x3e, 0x9f, 0x8b, 0x18, 0x79, 0x17, 0x6f, 0x63, 0x46, 0x43, 0x5a, 0xbc, 0x96, + 0x73, 0xe9, 0x23, 0x79, 0xac, 0x83, 0x44, 0xd7, 0xc5, 0x4e, 0xbe, 0x69, 0xe8, 0x26, 0x91, 0xb7, + 0x4a, 0x49, 0x9c, 0xfc, 0x46, 0xad, 0x41, 0xbc, 0x00, 0xe7, 0x4c, 0x83, 0xfd, 0xff, 0xa9, 0xfc, + 0xdb, 0xdb, 0x00, 0xd3, 0xa9, 0xa1, 0x35, 0xc8, 0x36, 0x36, 0x7a, 0xbd, 0x6d, 0x65, 0x4e, 0x38, + 0xf0, 0x29, 0x8b, 0x93, 0xd5, 0x3f, 0x4b, 0x43, 0xbe, 0x51, 0x93, 0x57, 0x6e, 0x03, 0x14, 0xee, + 0x95, 0xf8, 0xd3, 0x0b, 0x79, 0x31, 0xb2, 0xbd, 0x89, 0x74, 0x2c, 0x57, 0x24, 0xbc, 0x4b, 0x4c, + 0x84, 0x8d, 0x5a, 0xe3, 0x02, 0x08, 0x43, 0x89, 0x48, 0x23, 0xe8, 0xa6, 0x11, 0xfa, 0xf8, 0xb5, + 0xab, 0x8d, 0x25, 0x52, 0x97, 0x69, 0xdb, 0xc7, 0xc5, 0x50, 0x49, 0xc3, 0xf0, 0xd1, 0xbb, 0xb0, + 0xec, 0xdb, 0x03, 0xd7, 0x76, 0x07, 0x7a, 0x68, 0x3c, 0xfe, 0x0e, 0x54, 0xbf, 0x71, 0x7e, 0xb6, + 0xbe, 0xd8, 0x13, 0x2c, 0x69, 0xc3, 0x45, 0x89, 0x6c, 0x70, 0x53, 0xa2, 0x6f, 0xc0, 0x52, 0x4c, + 0x94, 0x59, 0x51, 0x98, 0x9d, 0x57, 0x8c, 0x23, 0xc9, 0xa7, 0x64, 0x82, 0x4b, 0x91, 0xe0, 0x53, + 0xc2, 0x6b, 0x33, 0xfb, 0xd4, 0x33, 0x89, 0xee, 0xf1, 0x33, 0xcd, 0x6f, 0xf7, 0x0c, 0x2e, 0x72, + 0x9a, 0x38, 0xe6, 0xea, 0x33, 0xb8, 0xd9, 0xf1, 0xcc, 0x03, 0xe2, 0x07, 0xc2, 0x14, 0xd2, 0x8a, + 0x1f, 0xc2, 0x9d, 0xc0, 0xf0, 0x0f, 0xf5, 0x03, 0xdb, 0x0f, 0xa8, 0x37, 0xd1, 0x3d, 0x12, 0x10, + 0x97, 0xf1, 0x75, 0xfe, 0xb4, 0x2c, 0xcb, 0x89, 0xb7, 0x19, 0x66, 0x4b, 0x40, 0x70, 0x88, 0xd8, + 0x66, 0x00, 0xb5, 0x05, 0x25, 0x96, 0xc2, 0xc8, 0xa2, 0x1a, 0x9b, 0x3d, 0x38, 0x74, 0xa0, 0x7f, + 0xee, 0x6b, 0xaa, 0xe0, 0xd0, 0x81, 0xf8, 0xa9, 0x7e, 0x1b, 0x94, 0xa6, 0xed, 0x8f, 0x8c, 0xc0, + 0x3c, 0x08, 0xeb, 0xa4, 0xa8, 0x09, 0xca, 0x01, 0x31, 0xbc, 0x60, 0x8f, 0x18, 0x81, 0x3e, 0x22, + 0x9e, 0x4d, 0xad, 0xeb, 0x57, 0x79, 0x39, 0x12, 0xe9, 0x72, 0x09, 0xf5, 0x7f, 0x52, 0x00, 0xd8, + 0xd8, 0x0f, 0xa3, 0xb5, 0xaf, 0xc1, 0x0d, 0xdf, 0x35, 0x46, 0xfe, 0x01, 0x0d, 0x74, 0xdb, 0x0d, + 0x88, 0x77, 0x64, 0x38, 0xb2, 0xb8, 0xa3, 0x84, 0x8c, 0x96, 0xa4, 0xa3, 0xb7, 0x01, 0x1d, 0x12, + 0x32, 0xd2, 0xa9, 0x63, 0xe9, 0x21, 0x53, 0x3c, 0x7c, 0x67, 0xb0, 0xc2, 0x38, 0x1d, 0xc7, 0xea, + 0x85, 0x74, 0x54, 0x87, 0x35, 0x36, 0x7d, 0xe2, 0x06, 0x9e, 0x4d, 0x7c, 0x7d, 0x9f, 0x7a, 0xba, + 0xef, 0xd0, 0x63, 0x7d, 0x9f, 0x3a, 0x0e, 0x3d, 0x26, 0x5e, 0x58, 0x37, 0x2b, 0x3b, 0x74, 0xa0, + 0x09, 0xd0, 0x06, 0xf5, 0x7a, 0x0e, 0x3d, 0xde, 0x08, 0x11, 0x2c, 0xa4, 0x9b, 0xce, 0x39, 0xb0, + 0xcd, 0xc3, 0x30, 0xa4, 0x8b, 0xa8, 0x7d, 0xdb, 0x3c, 0x44, 0xaf, 0xc3, 0x22, 0x71, 0x08, 0x2f, + 0x9f, 0x08, 0x54, 0x96, 0xa3, 0x4a, 0x21, 0x91, 0x81, 0xd4, 0x8f, 0x40, 0xd1, 0x5c, 0xd3, 0x9b, + 0x8c, 0x62, 0x6b, 0xfe, 0x36, 0x20, 0xe6, 0x24, 0x75, 0x87, 0x9a, 0x87, 0xfa, 0xd0, 0x70, 0x8d, + 0x01, 0x1b, 0x97, 0x78, 0x71, 0x54, 0x18, 0x67, 0x9b, 0x9a, 0x87, 0x3b, 0x92, 0xae, 0xbe, 0x0b, + 0xd0, 0x1b, 0x79, 0xc4, 0xb0, 0x3a, 0x2c, 0x9a, 0x60, 0xa6, 0xe3, 0x2d, 0xdd, 0x92, 0xef, 0xb9, + 0xd4, 0x93, 0x47, 0x5d, 0x11, 0x8c, 0x66, 0x44, 0x57, 0x7f, 0x1e, 0x6e, 0x76, 0x1d, 0xc3, 0xe4, + 0xdf, 0x36, 0x74, 0xa3, 0x27, 0x34, 0xf4, 0x04, 0x72, 0x02, 0x2a, 0x57, 0x32, 0xf1, 0xb8, 0x4d, + 0xfb, 0xdc, 0x9a, 0xc3, 0x12, 0x5f, 0x2f, 0x01, 0x4c, 0xf5, 0xa8, 0xff, 0x98, 0x82, 0x42, 0xa4, + 0x1f, 0x55, 0xc4, 0xcb, 0x50, 0xe0, 0x19, 0xb6, 0x2b, 0x33, 0xfe, 0x02, 0x8e, 0x93, 0x50, 0x0b, + 0x8a, 0xa3, 0x48, 0xfa, 0xca, 0x78, 0x2e, 0x61, 0xd4, 0x38, 0x2e, 0x8b, 0xde, 0x83, 0x42, 0xf8, + 0x80, 0x1e, 0x7a, 0xd8, 0xab, 0xdf, 0xdb, 0xa7, 0xf0, 0xb0, 0x90, 0xea, 0x91, 0x91, 0x63, 0x33, + 0x9f, 0x93, 0x89, 0x0a, 0xa9, 0x58, 0x92, 0xd4, 0x6f, 0x02, 0x7c, 0x8b, 0xda, 0x6e, 0x9f, 0x1e, + 0x12, 0x97, 0xbf, 0x0a, 0xb3, 0x94, 0x92, 0x84, 0x86, 0x96, 0x2d, 0x5e, 0x29, 0x10, 0xab, 0x14, + 0x3d, 0x8e, 0x8a, 0xa6, 0xfa, 0x17, 0x69, 0xc8, 0x61, 0x4a, 0x83, 0x46, 0x0d, 0x55, 0x20, 0x27, + 0x5d, 0x09, 0xbf, 0xa2, 0xea, 0x85, 0xf3, 0xb3, 0xf5, 0xac, 0xf0, 0x21, 0x59, 0x93, 0x3b, 0x8f, + 0x98, 0x93, 0x4f, 0x5f, 0xe6, 0xe4, 0xd1, 0x03, 0x28, 0x49, 0x90, 0x7e, 0x60, 0xf8, 0x07, 0x22, + 0xbf, 0xab, 0x2f, 0x9d, 0x9f, 0xad, 0x83, 0x40, 0x6e, 0x19, 0xfe, 0x01, 0x06, 0x81, 0x66, 0xbf, + 0x91, 0x06, 0xc5, 0x8f, 0xa9, 0xed, 0xea, 0x01, 0x9f, 0x84, 0xac, 0x45, 0x26, 0x2e, 0xf5, 0x74, + 0xaa, 0xf2, 0x03, 0x0a, 0xf8, 0x78, 0x3a, 0x79, 0x0d, 0x16, 0x3d, 0x4a, 0x03, 0xe1, 0xd9, 0x6c, + 0xea, 0xca, 0x32, 0x47, 0x25, 0xb1, 0xfa, 0x4d, 0x69, 0x80, 0x25, 0x0e, 0x97, 0xbc, 0x58, 0x0b, + 0x3d, 0x80, 0x15, 0xc7, 0xf0, 0x03, 0x9d, 0xbb, 0x44, 0x6b, 0xaa, 0x2d, 0xc7, 0x8d, 0x8f, 0x18, + 0x6f, 0x83, 0xb3, 0x42, 0x09, 0xf5, 0x1f, 0x52, 0x50, 0x64, 0x93, 0xb1, 0xf7, 0x6d, 0x93, 0xc5, + 0x81, 0x5f, 0x3c, 0x3c, 0xb9, 0x0d, 0xf3, 0xa6, 0xef, 0x49, 0xa3, 0xf2, 0xfb, 0xb9, 0xd1, 0xc3, + 0x98, 0xd1, 0xd0, 0x47, 0x90, 0x93, 0xe5, 0x16, 0x11, 0x99, 0xa8, 0xd7, 0x47, 0xac, 0xd2, 0x36, + 0x52, 0x8e, 0x6f, 0xf7, 0xe9, 0xe8, 0xc4, 0x3d, 0x81, 0xe3, 0x24, 0x74, 0x0b, 0xd2, 0xa6, 0x30, + 0x97, 0xfc, 0x42, 0xa7, 0xd1, 0xc6, 0x69, 0xd3, 0x55, 0x7f, 0x94, 0x82, 0xc5, 0xa9, 0x4f, 0x60, + 0x3b, 0xe0, 0x0e, 0x14, 0xfc, 0xf1, 0x9e, 0x3f, 0xf1, 0x03, 0x32, 0x0c, 0x5f, 0xbc, 0x23, 0x02, + 0x6a, 0x41, 0xc1, 0x70, 0x06, 0xd4, 0xb3, 0x83, 0x83, 0xa1, 0x4c, 0x64, 0x93, 0xa3, 0x89, 0xb8, + 0xce, 0x6a, 0x2d, 0x14, 0xc1, 0x53, 0xe9, 0x30, 0x34, 0x10, 0x9f, 0x45, 0xf0, 0xd0, 0xe0, 0x35, + 0x28, 0x39, 0xc6, 0x90, 0xd7, 0x9f, 0x02, 0x7b, 0x48, 0xc2, 0xc3, 0x20, 0x69, 0x7d, 0x7b, 0x48, + 0x54, 0x15, 0x0a, 0x91, 0x32, 0xb4, 0x0c, 0xc5, 0x9a, 0xd6, 0xd3, 0x1f, 0x3e, 0x7a, 0xa2, 0x6f, + 0x36, 0x76, 0x94, 0x39, 0x19, 0xbe, 0xfe, 0x69, 0x0a, 0x16, 0xa5, 0xc7, 0x92, 0x29, 0xc1, 0xeb, + 0xb0, 0xe0, 0x19, 0xfb, 0x41, 0x98, 0xb4, 0x64, 0xc4, 0xae, 0x66, 0x97, 0x00, 0x4b, 0x5a, 0x18, + 0x2b, 0x39, 0x69, 0x89, 0x7d, 0x83, 0x31, 0x7f, 0xe5, 0x37, 0x18, 0x99, 0xff, 0x97, 0x6f, 0x30, + 0xd4, 0x5f, 0x03, 0xd8, 0xb0, 0x1d, 0xd2, 0x17, 0xa5, 0xaa, 0xa4, 0x14, 0x94, 0x85, 0x79, 0xb2, + 0x14, 0x1a, 0x86, 0x79, 0xad, 0x26, 0x66, 0x34, 0xc6, 0x1a, 0xd8, 0x96, 0x3c, 0x8c, 0x9c, 0xb5, + 0xc9, 0x58, 0x03, 0xdb, 0x8a, 0x9e, 0xfd, 0x32, 0xd7, 0x3c, 0xfb, 0xa9, 0xcb, 0xb0, 0x88, 0x45, + 0x8d, 0x4d, 0x8c, 0x41, 0x3d, 0x4d, 0xc1, 0xb2, 0x8c, 0x77, 0x23, 0x97, 0xfd, 0x55, 0x28, 0x88, + 0xd0, 0x77, 0x9a, 0x04, 0xf2, 0x0f, 0x11, 0x04, 0xae, 0xd5, 0xc4, 0x79, 0xc1, 0x6e, 0x59, 0x68, + 0x1d, 0x8a, 0x12, 0x1a, 0xfb, 0xbc, 0x0b, 0x04, 0xa9, 0xcd, 0xe6, 0xf3, 0x0e, 0x64, 0xf6, 0x6d, + 0x87, 0xc8, 0x9d, 0x9f, 0xe8, 0x11, 0xa6, 0x16, 0xd9, 0x9a, 0xc3, 0x1c, 0x5d, 0xcf, 0x87, 0xc5, + 0x3d, 0xf5, 0x5f, 0x52, 0xbc, 0xc4, 0xcc, 0x52, 0xd5, 0xf8, 0xf8, 0x44, 0xd6, 0x3a, 0x33, 0x3e, + 0x81, 0x63, 0xe3, 0x13, 0x6c, 0x31, 0x3e, 0x09, 0x8d, 0x8f, 0x4f, 0x90, 0x7e, 0xf2, 0xf1, 0xa1, + 0x0f, 0x60, 0x41, 0x96, 0x2a, 0xa5, 0xab, 0x7b, 0x2d, 0x71, 0x67, 0xc4, 0x2d, 0xbd, 0x35, 0x87, + 0x43, 0x99, 0xd8, 0xf4, 0xb6, 0xe1, 0x56, 0xdd, 0x31, 0xcc, 0x43, 0xc7, 0xf6, 0x03, 0x62, 0xc5, + 0x3d, 0xd0, 0x23, 0xc8, 0x5d, 0x88, 0x73, 0xaf, 0x2a, 0xa2, 0x4a, 0xa4, 0xfa, 0xef, 0x29, 0x28, + 0x6d, 0x11, 0xc3, 0x09, 0x0e, 0xa6, 0x95, 0xaa, 0x80, 0xf8, 0x81, 0xbc, 0x1f, 0xf9, 0x6f, 0xf4, + 0x75, 0xc8, 0x47, 0x61, 0xd0, 0xb5, 0xcf, 0x81, 0x11, 0x14, 0x3d, 0x86, 0x05, 0x36, 0x76, 0x3a, + 0x0e, 0xf3, 0xab, 0xab, 0x5e, 0x9a, 0x24, 0x92, 0x5d, 0x5a, 0x1e, 0xe1, 0x71, 0x0f, 0xb7, 0x53, + 0x16, 0x87, 0x4d, 0xf4, 0xb3, 0x50, 0xe2, 0x0f, 0x25, 0x61, 0x98, 0x97, 0xbd, 0x4e, 0x67, 0x51, + 0xbc, 0x75, 0x8a, 0x10, 0xef, 0x8f, 0xd2, 0xb0, 0xb2, 0x63, 0x4c, 0xf6, 0x88, 0x74, 0x43, 0xc4, + 0xc2, 0xc4, 0xa4, 0x9e, 0x85, 0xba, 0x71, 0xf7, 0x75, 0xc5, 0xd3, 0x69, 0x92, 0x70, 0xb2, 0x17, + 0x0b, 0x73, 0xbe, 0x74, 0x2c, 0xe7, 0x5b, 0x81, 0xac, 0x4b, 0x5d, 0x93, 0x48, 0xdf, 0x26, 0x1a, + 0xea, 0xef, 0xa4, 0xe2, 0xbe, 0xab, 0x1c, 0x3d, 0x6b, 0xf2, 0xa2, 0x57, 0x9b, 0x06, 0x51, 0x77, + 0xe8, 0x23, 0x28, 0xf7, 0xb4, 0x06, 0xd6, 0xfa, 0xf5, 0xce, 0xb7, 0xf5, 0x5e, 0x6d, 0xbb, 0x57, + 0x7b, 0xf4, 0x40, 0xef, 0x76, 0xb6, 0xbf, 0xf3, 0xf0, 0xf1, 0x83, 0xaf, 0x2b, 0xa9, 0x72, 0xe5, + 0xe4, 0xb4, 0x72, 0xa7, 0x5d, 0x6b, 0x6c, 0x8b, 0x13, 0xb7, 0x47, 0x5f, 0xf4, 0x0c, 0xc7, 0x37, + 0x1e, 0x3d, 0xe8, 0x52, 0x67, 0xc2, 0x30, 0xe8, 0x6b, 0x80, 0x36, 0x34, 0xdc, 0xd6, 0xfa, 0x7a, + 0xe8, 0x20, 0x1b, 0xf5, 0x86, 0x92, 0x16, 0x99, 0xd4, 0x06, 0xf1, 0x5c, 0x12, 0xd4, 0xb4, 0xde, + 0xc3, 0x47, 0x4f, 0x1a, 0xf5, 0x06, 0x3b, 0xe3, 0xa5, 0xf8, 0x6d, 0x19, 0x0f, 0x02, 0x52, 0x97, + 0x06, 0x01, 0xd3, 0x58, 0x22, 0x7d, 0x49, 0x2c, 0xb1, 0x01, 0x2b, 0xa6, 0x47, 0x7d, 0x5f, 0x67, + 0xe9, 0x09, 0xb1, 0x66, 0x12, 0xa0, 0x2f, 0x9d, 0x9f, 0xad, 0xdf, 0x68, 0x30, 0x7e, 0x8f, 0xb3, + 0xa5, 0xfa, 0x1b, 0x66, 0x8c, 0xc4, 0x7b, 0x52, 0x7f, 0x30, 0xcf, 0x22, 0x3d, 0xfb, 0xc8, 0x76, + 0xc8, 0x80, 0xf8, 0xe8, 0x19, 0x2c, 0x9b, 0x1e, 0xb1, 0x58, 0xde, 0x61, 0x38, 0xf1, 0x6f, 0x92, + 0x7f, 0x26, 0x31, 0xe8, 0x8a, 0x04, 0xab, 0x8d, 0x48, 0xaa, 0x37, 0x22, 0x26, 0x5e, 0x32, 0x2f, + 0xb4, 0xd1, 0xc7, 0xb0, 0xec, 0x13, 0xc7, 0x76, 0xc7, 0x2f, 0x74, 0x93, 0xba, 0x01, 0x79, 0x11, + 0x3e, 0xe7, 0x5d, 0xa7, 0xb7, 0xa7, 0x6d, 0x33, 0xa9, 0x86, 0x10, 0xaa, 0xa3, 0xf3, 0xb3, 0xf5, + 0xa5, 0x8b, 0x34, 0xbc, 0x24, 0x35, 0xcb, 0x76, 0xf9, 0x00, 0x96, 0x2e, 0x8e, 0x06, 0xad, 0x48, + 0x47, 0xc3, 0xfd, 0x55, 0xe4, 0x48, 0xee, 0x40, 0xde, 0x23, 0x03, 0xdb, 0x0f, 0x3c, 0x61, 0x66, + 0xc6, 0x89, 0x28, 0x68, 0x15, 0x72, 0xb1, 0x2f, 0x4e, 0x18, 0x4f, 0xb6, 0x99, 0x07, 0x11, 0x1f, + 0x93, 0x95, 0x7f, 0x05, 0x66, 0xc6, 0xc2, 0x0e, 0x9d, 0x65, 0xfb, 0xc6, 0x9e, 0xec, 0x2c, 0x8f, + 0xc3, 0x26, 0xdb, 0xcb, 0x63, 0x3f, 0x0a, 0x20, 0xf9, 0x6f, 0x46, 0xe3, 0x91, 0x8e, 0xfc, 0xb4, + 0x8e, 0xc7, 0x32, 0xe1, 0x17, 0xbc, 0x99, 0xd8, 0x17, 0xbc, 0x2b, 0x90, 0x75, 0xc8, 0x11, 0x71, + 0x44, 0x8c, 0x81, 0x45, 0xe3, 0xde, 0x03, 0x28, 0x85, 0x9f, 0x8a, 0xf2, 0x6f, 0x46, 0xf2, 0x90, + 0xe9, 0xd7, 0x7a, 0x4f, 0x95, 0x39, 0x04, 0x90, 0x13, 0x7b, 0x5c, 0x3c, 0x42, 0x36, 0x3a, 0xed, + 0x8d, 0xd6, 0xa6, 0x92, 0xbe, 0xf7, 0xbb, 0x19, 0x28, 0x44, 0xcf, 0x60, 0xec, 0x4e, 0x6b, 0x6b, + 0xcf, 0xc3, 0x43, 0x12, 0xd1, 0xdb, 0xe4, 0x18, 0xbd, 0x36, 0x2d, 0xa0, 0x7d, 0x24, 0xde, 0xfd, + 0x23, 0x76, 0x58, 0x3c, 0x7b, 0x03, 0xf2, 0xb5, 0x5e, 0xaf, 0xb5, 0xd9, 0xd6, 0x9a, 0xca, 0xa7, + 0xa9, 0xf2, 0x97, 0x4e, 0x4e, 0x2b, 0x37, 0x22, 0x50, 0xcd, 0x17, 0xdb, 0x92, 0xa3, 0x1a, 0x0d, + 0xad, 0xdb, 0xd7, 0x9a, 0xca, 0x27, 0xe9, 0x59, 0x14, 0x2f, 0x08, 0xf1, 0xef, 0x99, 0x0a, 0x5d, + 0xac, 0x75, 0x6b, 0x98, 0x75, 0xf8, 0x69, 0x5a, 0xd4, 0xf5, 0xa6, 0x3d, 0x7a, 0x64, 0x64, 0x78, + 0xac, 0xcf, 0xb5, 0xf0, 0xb3, 0xc2, 0x4f, 0xe6, 0xc5, 0x87, 0x2d, 0xd3, 0x37, 0x3d, 0x62, 0x58, + 0x13, 0xd6, 0x1b, 0x7f, 0x4c, 0xe5, 0x6a, 0xe6, 0x67, 0x7a, 0xeb, 0x31, 0x1f, 0xc6, 0xb4, 0xa8, + 0xb0, 0x80, 0x77, 0xdb, 0x6d, 0x06, 0xfa, 0x24, 0x33, 0x33, 0x3b, 0x3c, 0x76, 0x59, 0xb2, 0x8f, + 0xee, 0x42, 0x3e, 0x7c, 0x6b, 0x55, 0x3e, 0xcd, 0xcc, 0x0c, 0xa8, 0x11, 0x3e, 0x14, 0xf3, 0x0e, + 0xb7, 0x76, 0xfb, 0xfc, 0xab, 0xc7, 0x4f, 0xb2, 0xb3, 0x1d, 0x1e, 0x8c, 0x03, 0x8b, 0x1e, 0xbb, + 0xec, 0x34, 0xcb, 0x12, 0xe2, 0xa7, 0x59, 0xe1, 0x25, 0x22, 0x8c, 0xac, 0x1f, 0xbe, 0x01, 0x79, + 0xac, 0x7d, 0x4b, 0x7c, 0x20, 0xf9, 0x49, 0x6e, 0x46, 0x0f, 0x26, 0x1f, 0x13, 0x93, 0xf5, 0x56, + 0x81, 0x1c, 0xd6, 0x76, 0x3a, 0xcf, 0x34, 0xe5, 0xf7, 0x73, 0x33, 0x7a, 0x30, 0x19, 0x52, 0xfe, + 0xc1, 0x57, 0xbe, 0x83, 0xbb, 0x5b, 0x35, 0xbe, 0x28, 0xb3, 0x7a, 0x3a, 0xde, 0xe8, 0xc0, 0x70, + 0x89, 0x35, 0xfd, 0xb8, 0x27, 0x62, 0xdd, 0xfb, 0x05, 0xc8, 0x87, 0x31, 0x35, 0x5a, 0x83, 0xdc, + 0xf3, 0x0e, 0x7e, 0xaa, 0x61, 0x65, 0x4e, 0x58, 0x39, 0xe4, 0x3c, 0x17, 0xd9, 0x50, 0x05, 0x16, + 0x76, 0x6a, 0xed, 0xda, 0xa6, 0x86, 0xc3, 0xfa, 0x7f, 0x08, 0x90, 0x81, 0x61, 0x59, 0x91, 0x1d, + 0x44, 0x3a, 0xeb, 0xab, 0x3f, 0xfc, 0x6c, 0x6d, 0xee, 0xc7, 0x9f, 0xad, 0xcd, 0x7d, 0x72, 0xbe, + 0x96, 0xfa, 0xe1, 0xf9, 0x5a, 0xea, 0x6f, 0xcf, 0xd7, 0x52, 0xff, 0x76, 0xbe, 0x96, 0xda, 0xcb, + 0xf1, 0xeb, 0xe6, 0xf1, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0xb9, 0x62, 0x4d, 0x1b, 0x7b, 0x33, + 0x00, 0x00, } diff --git a/vendor/github.com/docker/swarmkit/api/types.proto b/vendor/github.com/docker/swarmkit/api/types.proto index e1567d77ae89..c416fb806ec0 100644 --- a/vendor/github.com/docker/swarmkit/api/types.proto +++ b/vendor/github.com/docker/swarmkit/api/types.proto @@ -246,6 +246,8 @@ message Mount { // Propagation mode of mount. Propagation propagation = 1; + // allows non-recursive bind-mount, i.e. mount(2) with "bind" rather than "rbind". + bool nonrecursive = 2 [(gogoproto.customname) = "NonRecursive"]; } // VolumeOptions contains parameters for mounting the volume. @@ -976,6 +978,12 @@ message FileTarget { uint32 mode = 4 [(gogoproto.customtype) = "os.FileMode", (gogoproto.nullable) = false]; } +// RuntimeTarget represents that this secret is _not_ mounted into the +// container, but is used for some other purpose by the container runtime. +// +// Currently, RuntimeTarget has no fields; it's just a placeholder. +message RuntimeTarget {} + // SecretReference is the linkage between a service and a secret that it uses. message SecretReference { // SecretID represents the ID of the specific Secret that we're @@ -1003,9 +1011,10 @@ message ConfigReference { // lookup/display purposes. The config in the reference will be identified by its ID. string config_name = 2; - // Target specifies how this secret should be exposed to the task. + // Target specifies how this config should be exposed to the task. oneof target { FileTarget file = 3; + RuntimeTarget runtime = 4; } } @@ -1075,6 +1084,10 @@ message Privileges { oneof source { string file = 1; string registry = 2; + + // Config represents a Config ID from which to get the CredentialSpec. + // The Config MUST be included in the SecretReferences with a RuntimeTarget + string config = 3; } } CredentialSpec credential_spec = 1; diff --git a/vendor/github.com/docker/swarmkit/vendor.conf b/vendor/github.com/docker/swarmkit/vendor.conf index 7115c5654164..3a2c8f729a67 100644 --- a/vendor/github.com/docker/swarmkit/vendor.conf +++ b/vendor/github.com/docker/swarmkit/vendor.conf @@ -15,41 +15,41 @@ github.com/matttproud/golang_protobuf_extensions v1.0.0 google.golang.org/genproto 694d95ba50e67b2e363f3483057db5d4910c18f9 # metrics -github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/docker/go-metrics d466d4f6fd960e01820085bd7e1a24426ee7ef18 # etcd/raft -github.com/coreos/etcd v3.2.1 +github.com/coreos/etcd v3.3.9 github.com/coreos/go-systemd v17 github.com/coreos/pkg v3 -github.com/prometheus/client_golang 52437c81da6b127a9925d17eb3a382a2e5fd395e -github.com/prometheus/client_model fa8ad6fec33561be4280a8f0514318c79d7f6cb6 -github.com/prometheus/common ebdfc6da46522d58825777cf1f90490a5b1ef1d8 -github.com/prometheus/procfs abf152e5f3e97f2fafac028d2cc06c1feb87ffa5 +github.com/prometheus/client_golang v0.8.0 +github.com/prometheus/client_model 6f3806018612930941127f2a7c6c453ba2c527d2 +github.com/prometheus/common 7600349dcfe1abd18d72d3a1770870d9800a7801 +github.com/prometheus/procfs 7d6f385de8bea29190f15ba9931442a0eaef9af7 github.com/docker/distribution 83389a148052d74ac602f5f1d62f86ff2f3c4aa5 -github.com/docker/docker b9bb3bae5161f931c1dede43c67948c599197f50 -github.com/docker/go-connections 7beb39f0b969b075d1325fecb092faf27fd357b6 +github.com/docker/docker 5a718ef0f94f605fe4e4885937133c2f76ad2a41 +github.com/docker/go-connections 7395e3f8aa162843a74ed6d48e79627d9792ac55 # v0.4.0 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1 -github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef -github.com/docker/libnetwork 1f28166bb386cf9223d2d00a28382b0e474be314 -github.com/opencontainers/runc ad0f5255060d36872be04de22f8731f38ef2d7b1 +github.com/docker/libkv 458977154600b9f23984d9f4b82e79570b5ae12b +github.com/docker/libnetwork 1a06131fb8a047d919f7deaf02a4c414d7884b83 +github.com/opencontainers/runc 96ec2177ae841256168fcf76954f7177af9446eb github.com/opencontainers/go-digest v1.0.0-rc1 github.com/opencontainers/image-spec v1.0.1 github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 # v1.1.0 -github.com/Microsoft/go-winio v0.4.8 -github.com/sirupsen/logrus v1.0.3 -github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 +github.com/Microsoft/go-winio v0.4.11 +github.com/sirupsen/logrus v1.0.6 +github.com/beorn7/perks 3a771d992973f24aa725d07868b467d1ddfceaf github.com/cloudflare/cfssl 1.3.2 github.com/dustin/go-humanize 8929fe90cee4b2cb9deb468b51fb34eba64d1bf0 github.com/fernet/fernet-go 1b2437bc582b3cfbb341ee5a29f8ef5b42912ff2 github.com/google/certificate-transparency-go v1.0.20 github.com/hashicorp/go-immutable-radix 826af9ccf0feeee615d546d69b11f8e98da8c8f1 git://github.com/tonistiigi/go-immutable-radix.git github.com/hashicorp/go-memdb cb9a474f84cc5e41b273b20c6927680b2a8776ad -github.com/hashicorp/golang-lru a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4 +github.com/hashicorp/golang-lru 0fb14efe8c47ae851c0034ed7a448854d3d34cf3 github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 github.com/phayes/permbits f7e3ac5e859d0b919c5068d581cc4c5d4f4f9bc5 github.com/pivotal-golang/clock 3fd3c1944c59d9742e1cd333672181cd1a6f9fa0 @@ -60,8 +60,8 @@ github.com/spf13/cobra 8e91712f174ced10270cf66615e0a9127e7c4de5 github.com/spf13/pflag 7f60f83a2c81bc3c3c0d5297f61ddfa68da9d3b7 github.com/stretchr/testify v1.1.4 go.etcd.io/bbolt v1.3.1-etcd.8 -golang.org/x/crypto 1a580b3eff7814fc9b40602fd35256c63b50f491 -golang.org/x/net 0ed95abb35c445290478a5348a7b38bb154135fd -golang.org/x/sys 37707fdb30a5b38865cfb95e5aab41707daec7fd -golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756 -golang.org/x/time a4bde12657593d5e90d0533a3e4fd95e635124cb +golang.org/x/crypto 0709b304e793a5edb4a2c0145f281ecdc20838a4 +golang.org/x/net a680a1efc54dd51c040b3b5ce4939ea3cf2ea0d1 +golang.org/x/sys 90868a75fefd03942536221d7c0e2f84ec62a668 +golang.org/x/text f21a4dfb5e38f5895301dc265a8def02365cc3d0 # v0.3.0 +golang.org/x/time fbb02b2291d28baffd63558aa44b4b56f178d650 From 1e99ed3ca32bd198ac5865d10029d67c31fe5f91 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 2 Feb 2019 17:55:39 +0100 Subject: [PATCH 55/60] Disallow additional properties in credential_spec Signed-off-by: Sebastiaan van Stijn --- cli/compose/loader/loader_test.go | 2 +- cli/compose/schema/bindata.go | 344 +++++++++--------- .../schema/data/config_schema_v3.3.json | 12 +- .../schema/data/config_schema_v3.4.json | 12 +- .../schema/data/config_schema_v3.5.json | 12 +- .../schema/data/config_schema_v3.6.json | 12 +- .../schema/data/config_schema_v3.7.json | 12 +- cli/compose/schema/schema_test.go | 41 +++ 8 files changed, 254 insertions(+), 193 deletions(-) diff --git a/cli/compose/loader/loader_test.go b/cli/compose/loader/loader_test.go index b2d581b5d1a7..d80e0c863911 100644 --- a/cli/compose/loader/loader_test.go +++ b/cli/compose/loader/loader_test.go @@ -283,7 +283,7 @@ services: foo: image: busybox credential_spec: - File: "/foo" + file: "/foo" configs: [super] configs: super: diff --git a/cli/compose/schema/bindata.go b/cli/compose/schema/bindata.go index 028667054160..dc01e6788fb9 100644 --- a/cli/compose/schema/bindata.go +++ b/cli/compose/schema/bindata.go @@ -301,210 +301,210 @@ ZbmZv+QaLHpS4rzkKyabuw8zkGHuhdMrnbUrtIOnbTqoMzZd83f41N8R/735o4f/lZziOLoU+2E3AJpH "/data/config_schema_v3.3.json": { local: "data/config_schema_v3.3.json", - size: 15414, + size: 15491, modtime: 1518458244, compressed: ` -H4sIAAAAAAAC/+xbUW/bPg5/z6cIvL0tbQdsOOD2do/3dPd8hWcoMpNolSWNktNmQ7/7QbbjWLZsKYm7 -dv//BgxobIoSSZH8kZJ/LpbL5L2mOyhI8mWZ7IxRX+7uvmkpbuqntxK3dzmSjbn5+PmufvYuWdlxLLdD -qBQbts3qN9n+0+2nWzu8JjEHBZZIrr8BNfUzhO8lQ7CD75M9oGZSJOlqYd8plArQMNDJl6Vd3HLZkhwf -dNhqg0xsk+rxc8VhuUw04J7RDod2qe/uTvzvWrJVn2tnsdVzRYwBFP8drq16/fWe3Pz4183/Pt788za7 -ST+8d15b/SJs6ulz2DDBDJOinT9pKZ+bv57biUmeV8SEO3NvCNfgyizAPEp8CMnckr2SzM38HpldcfaS -l0XQgkeqVxKmnn4e+2mgCCa8ZWuqV9uxdvp5BK6jRkjgI9UrCVxPf53Ai6PQk7Q1RWfuaoFOPPOpyhdP -xnXVKmtESzkoLg/22Yg+aoIChElaFSyXybpkPO9rVAr4j2Vx33m4XP7sh+4On+q982vc4O37EVna91QK -A0+mEmp66loFkj4AbhiH2BEE6108ojLOtMkkZjmjxjuekzXwqzhQQneQbVAWQS6brJZEJ889PgPG4a3d -9wr7L114GCaUqIzkuaNSgkgOyWqZMAOF9mt7mZSCfS/h3w2JwRL6fHOUan7GW5SlyhRBu9end0JCZVEQ -MZcDnCNHhOYHYdbxqmaO7qt2NmdZI9IsI3zE45QBpw67tY2KskQa66V2ToJbMPH0JcvjibfnEBcyd9ct -ymINOHBJ17OGv9OF703P+oYwAZgJUkBwHyPkIAwjPNMKqEN+tNSEZZKoqJkgbJk2ePBSnqToLiwHBSLX -WV0DxAY4h0FbEMwaJnIxFbhrNjZ027UlvYGZBoJ0d+F4WRAmYowKwuBBSVaHsTcXn0Dss3bfnK0GEHuG -UhTHIB2XQDvjn5TUcH1wbEbcHwVftT6dutpLNhILYhd7nHsxkoI9O6+rwK4MFvgSnnEmHubf4vBkkGQ7 -qc0lGCXZAeFmR3dAHyaGd6mc0VKbmE3OCrINEykaIrkYiyWzKr/DVm63lnRsxw2wfSQqzpHtAWMhrlSn -ksSXWUPZPFifOaRfb+vybMKrqr84T9JnD4tQGu1JGIdzHasUhFo4i6B1aEc1DY9skPNPtANiHRupz06E -l1VYUaYLlthBJDmGFuN3WRxyPJqdM6JBX4YoBtyY2n+O3BO+sf+YHDsydJRnfOkWYNWFqJx7F5KGQetL -VpbKBd5urKgiRNfBlETzS2qhU5w6Jfx68mF51Dd31KCXqakmolRcRcWEga0tZfxJoFxzpneQnzMGpZFU -8jjH8DZo4p1hor66CJspZHvGYduTeC0lByKcRIFA8kwKfoig1IZgsCuhgZbIzCGTysyOCvWuyDT7Aa7v -nXZ9wyjtLajX4v7Tivj7tCL0QVNzGbbWJmcikwpE0De0kSrbIqGQKUAmvapwAmxeIrHzD9lothWEh9zM -FGpzYRPAmLCzl5wVbNxpPLs2Aq/VWM0P0SbgWVTInqgQpguEiMpgR/CM1FE55mYkPy0iMZB7WF3xWzUL -Sb30Z0Gv/jLSUfTjd6pSB4u4ikboLCK1e05df48I7dioIk8viuPNTJGx86WjfjQicE+7NNMGBD3ET7Rm -g8OLc+uuuKqroiLbOt5GFzrxvtocyP8SUYSkUo2YJl6MFwawvU7HBGwdizCPEh9s/soZTlnskhsAvdbg -1NF2lzR4FWD6mD10BM40WfeOMXx52SYS3IfhAYJB1jt5OGKsLhQA/Tb784YVIEszaftFZ1DSuSIQMGqH -sm/T+855U13GBw0Xk99A5NVJSFQyRFCcUaJDgOOKpnGpcmIga26ZnAPxJrCdIkg4B850EYOVkhw4OVwE -k+sDDcJ4iZAROhrVeyMKKZiRePmUBXnKjtNWJKEKxi3eY/u93YK7SvV6LhOdMP3IjjnOOBAdQduw07bj -g+Nn1YIhaDIlOavRxRyqoFLU64jZOVduVbtvbG1UKKOjXOORiVw+njHhfNpWnFDoRdFrFa0NEibM2cdU -fbUohA0gCOpFSBPlwkTJMF8vRlnc/ArdwmuNfwWSat09kHFbuvB1ypEsS1UZPPIqoJDT1zuuuOAcEvFI -NgOiiDojbagyqeZv0oTPQdNwi4ApUswVQ6JPjRMvpHkL0aFcCzC/YXRYDS98jFj1vq1XVq2u0mgTj962 -mG/9VenU76z6aixiDKG7qHLsTAx+RRwa9Bi8Yaih+hOF/iJ79tftr+Ybi+Bd/4rq4jwecVXzDdjslU0x -SGJeUzRUf0zxol7hnqJ1TDLskk1p8tyvI1J3GX0yzxeJLq6ZOmNfTHdle5M2SpyWfMa4f/thAr1NXcl7 -Idgzw/0Fv017hfGiva3Q/+hq3P+P4wefYFk5xWHQxf3pnljVn0+ljn56JPUF106iTbu9gtEb+b4Ps/rn -ZccPpEaO8N0m78L+f178PwAA//9GlSx0NjwAAA== +H4sIAAAAAAAC/+wbzW7bPPLupzDU3uokBVossL3tcU+75w1UgabGNhuKZIeUE7fIuy8oybJEUSJtK036 +fS1QIJaGQ87/H/VzsVwm7zXdQUGSL8tkZ4z6cnf3TUtxUz+9lbi9y5FszM3Hz3f1s3fJyq5juV1Cpdiw +bVa/yfafbj/d2uU1iDkosEBy/Q2oqZ8hfC8Zgl18n+wBNZMiSVcL+06hVICGgU6+LO3hlssW5Pigg1Yb +ZGKbVI+fKwzLZaIB94x2MLRHfXd3wn/Xgq1crJ3DVs8VMQZQ/Hd4tur113ty8+NfN//7ePPP2+wm/fC+ +99ryF2FTb5/DhglmmBTt/kkL+dz89dxuTPK8Aia8t/eGcA19mgWYR4kPIZpbsFeiudnfQ3OfnL3kZRGU +4BHqlYipt59HfhooggmrbA31ahprt5+H4NprhAg+Qr0SwfX21xG8OBI9CVtDdPauDtjzZz5W+fzJOK9a +Zo1wKQfF5cE+G+FHDVCAMEnLguUyWZeM5y5HpYD/WBT3nYfL5U/XdXfwVO97v8YF3r4foaV9T6Uw8GQq +oqa3rlkg6QPghnGIXUGw1uIRlnGmTSYxyxk13vWcrIFfhYESuoNsg7IIYtlkNSU6eXbwDBCHVdu1Cvsv +XXgQJpSojOR5j6UEkRyS1TJhBgrt5/YyKQX7XsK/GxCDJbh4c5RqfsRblKXKFEGr69OakFBZFETMZQDn +0BHB+YGb7VlVs0f3Vbtb71gj1CwjbMRjlAGjDpu19YqyRBprpXZPglsw8fAly+OBt+cAFzLvn1uUxRpw +YJJ9yxr+The+N470DWECMBOkgKAeI+QgDCM80wromM54hDYlriTSmSYIW6YNHrywixFPFeelulTmoEDk +OqsLilhv2UPQVhez+pxcTEWBGo2NA/ZsibMw00CQ7i5cLwvCRIyGgDB4UJLVPvHNOTsQ+6zVtrPZAGLP +UIri6PHjonFn/ZOSGq73tM2K+yPhq9ZBpI7FbCQWxB72uPeolQw1r8vALg02iyY840w8zK/i8GSQZDup +zSUJT7IDws2O7oA+TCzvQvVWS21ilJwVZBsGUjQEcnFil8zK/A5aud1a0DGNGxQKkSl2jmwPGJsvS3Wq +b3xhOpQaBIu9HujX27rWm7Cq6i/Ok/TZgyIUk90gFhuOTlIpCLW5MYLWIY1quifZIIE4wQ6AdaynPjsQ +XlauRYkuWK8H09Kx1DNey+LS0KPYOSMa9GUZxQAbU/vPkTrhW/uPybUjS0dxxteBAVTdfJdz70HScAb8 +kmWq6mfxfV9ReYiugSmJ5pcUVic/dQr49ebDWssVd9SilynQJrxUXHnGhIGtrYv8QaBcc6Z3kJ+zBqWR +VPI4w/B2e+KNYaJYuyg3U8j2jMPWoXgtJQcieoECgeSZFPwQAakNwWCLQwMtkZlDJpWZPSvUuyLT7Af0 +be+k9Q2i1DmQ0y//09f4+/Q19EFTc1lurU3ORCYViKBtaCNVtkVCIVOATHpZ0XOweYnE7j9Eo9lWEB4y +M1OozYVNAGPCxl5yVrBxo/E2doL5Wp2r+VO0ifQsymVPVAjTBUJEZbAjeEboqAxzMxKfFpE5UH/yXeFb +NQdJvfBnpV7uMdLR7MdvVKUOFnEVjNBZRGj3jHB/Dw/dk1EFnl7kx5udIn3nS3v96IygPzrTTBsQ9BC/ +0ZoNJiHn1l1xVVcFRba1v40udOJttZnu/xJShKRSjYgmnowXTmCdTsdE2jrmYR4lPtj4lTOcktgl1wmc +1uDUnLwLGrxXMD2zD83TmSZrZ/jhi8s2kOA+nB4gGGTO5OGYY3VTAdBvsz9vWAGyNJOyX3QWJZ37BgGh +diBdmd63Qj2W8UHBxcQ3EHk1CYkKhgiKM0p0KOG4omlcqpwYyJorKzPN7hRBwjlwpouYXCnJgZPDRWly +PdAgjJcIGaGjXt1ZUUjBjMTLtyzIU3bctgIJVTBXjh8R6lCv5xLRKacf0Zjjjp6Bq7Zup23HB9fPygVD +0GRKclZnF3OwgkpRnyNGc65UVas3tjYqlNFRpvHIRC4fz9hwPm4rTig4XvRaRmuDhAlz9pjKZYtC2ACC +oN4MaaJcmCgZ5uvFKJs3v0K38FrhX5FJteYeiLgtXPhu5kiUpaoMjrwKKOT0lZArbkuHSDyCzZBRRM1I +G6hMqvmbNOE5aBpuETBFirl8SPTUOPGmNG/BO5RrAeY39A6r4YWPEanet/XKquVVGi3i0dsW852/Kp3c +zqqvxiLGELqLKsfOzMGv8EODHoPXDTVQf7zQX0Rnf51+NR9sBD8cqKAujuMRFzzfgMxeWRSDIOYVRQP1 +RxQvahX9KVpHJMMu2RQnz/3UIu0fwwXzfN7Yz2umZuyL6a6ss2nDxGnKZ/T7tx8msrepK3kvlPbMcH/B +L1OnMF60txXcL7jG7f+4fvA9l6VTHAZd3J/9iVX9LVba448DUl9w7QTatNsrGBOj9ysvd152/NpqZITf +b/Iu7P/nxf8DAAD//7pHo+CDPAAA `, }, "/data/config_schema_v3.4.json": { local: "data/config_schema_v3.4.json", - size: 15797, + size: 15874, modtime: 1518458244, compressed: ` -H4sIAAAAAAAC/+xbT2/bOhK/+1MYeu9WOymwxQLb2x73tHvewBVoamyzoUh2SDlxC3/3hURJ0R+KpG2l +H4sIAAAAAAAC/+xbT2/bOhK/+1MYeu9WOymwxQLb2x73tHvewBVoamyzoUh2SDlxi3z3hURJlihKpG2l Sff1AQ+NpeGQM5w/vxlSPxbLZfKnpgfISfJ5mRyMUZ/v779qKdb26Z3E/X2GZGfWHz/d22d/JKtyHMvK IVSKHdun9k16/Nvdp7tyuCUxJwUlkdx+BWrsM4RvBUMoBz8kR0DNpEg2q0X5TqFUgIaBTj4vy8Utly1J -86DDVhtkYp9Uj88Vh+Uy0YBHRjsc2qX+cf/C/74lWw25dhZbPVfEGEDxn/HaqtdfHsj6+z/X//24/sdd -ut58+LP3utQvws5On8GOCWaYFO38SUt5rv86txOTLKuICe/NvSNcQ19mAeZJ4mNI5pbsjWSu53fI3Bfn -KHmRB3ewoXojYez08+yfBopgwiZrqd7MYsvp5xHYRo2QwA3VGwlsp79N4EUjtHuNyZfndfnvueLp5We5 -dNZXCdGLeS51umLOtD5bhU5oMgPF5alauVtnliAHYZJWTctlsi0Yz4ZalwL+XbJ46DxcLn8Mw3uHT/W+ -92vaKNr3E7K076kUBp5NJZR/aqsCSR8Bd4xD7AiC1tInVMaZNqnENGPUOMdzsgV+EwdK6AHSHco8yGWX -Wkm0k1ETwSMlNwT34NbsgHg0OuxbQ7cs/9ssHAwTSlRKsqy3DoJITslqmTADuXYLtEwKwb4V8K+axGAB -Q74ZSjU/4z3KQqWKYOlIfmUnVOY5EXN51yVyRGh+FOd7LlvP0X3VztZb1oQ0ywgzdHh8IGKEY0YZcmWB -NDYE+F3BSV+wLJ54fwlxLrP+ukWRbwFHLtn3rPHvzcL1ZrD7hjABmAqSQ9COETIQhhGeagW0R97slGdn -kqiQnCDsmTZ48gelc3dhGSgQmU5tEXJ59EwyaCuSWcNEJnxZwbIp80K5tmQwMNVAkB6uHC9zwkTMpoIw -eFKS2TD27uITiGPa2s3FagBxZChF3gTpuOzcGf+spIbbg2M94qERfNX69KavvWQnMSflYpu5FxMp2GF5 -XQV2ZShRLeEpZ+JxfhOHZ4MkPUhtrgFAyQEINwd6AProGd6l6o2W2sQYOcvJPkykaIjkaqCXzKr8Dlu5 -35ekUxY3KhwiIXeG7AgYiyKleql3XJk1lM2DBWKP9MudrQ89XlX9xXmyOTtYhNLoQMI4nNvblZzQEs4i -aB2yqBqvp6Oc/0I7ItaxkfqqMuLy8i1q64I1fhBJTqHFeCuLQ47NtnNGNOjb6rFOcDl+irQJ19i/e8dO -DJ3kGV+6BVh1ISrnzoVswqD1NStL1Qfe/VhRRYiugymJ5qfUQi9x6iXh28nH5dFwu6MGvU5N5YlScRUV -Ewb2ZSnjTgLFljN9gOySMSiNpJLHOYaz+xPvDJ766ipsppAdGYf9QOKtlByI6CUKBJKlUvBTBKU2BINd -CQ20QGZOqVRmdlSoD3mq2Xfo+96L1deMNoMFDXrsv1sRf51WhD5paq7D1tpkTKRSgQj6hjZSpXskFFIF -yKRTFb0AmxVIyvnHbDTbC8JDbmZytbuyCWBM2NkLznI27TQOq43AaxaruSGaB55FhWxPheAvECIqgwPB -C1JH5Zi7ify0iMRA/dPyit+qXsjGSX8R9BouYzOJftxOVehgEVfRCJ1GpHbHse+vEaF7e1SRb66K4/VM -kbHztaN+NCLoH6Vppg0IeoqfaMtGhxeX1l1xVVdFRfY23kYXOvG+Wt8I+CmiCEmlmtiaeDFeGcAOOh0e -2DoVYZ4kPpb5K2Po27FrriAMWoO+c/MuafAugv8MP3S+zjTZDo4xXHm5TCR4dMODML5AMMgG5xEN8uoC -BNDvs2tvWA6yMNeCK4Lmcng2vKnUuQ7R9P99JtShHFrQQ+d0yzYNgmYSk01BZNW5S1TqRVCcUaJD8OaG -FnWhMmIgrS/VXAIoPUhSESScA2c6j0FmSQacnK6yG3t8QhgvEFJCJ3PIYEQuBTMSr58yJ89pM21FEvBa -66WYwdScIIrcgY2sX6x3DLWxJbRU9a9+UD9PtiViO9ndVkIFYvRc5vBSrUxYZzPjSGMIugyo7UFDcPys -WrAhSXJmcdMcqqBS2HXEWOmNblHaaFn15croKDd8YiKTT5dH3xm0rTihMIjYtypaGyRMmIsP4IZqUQg7 -QBDUif08hZCnGJqvy6TKiuAN+qC3bv4NGLF190B2b+nCN1UnMjpVRfAwL4dc+i+u3HB3PCRiQzYDeok6 -/a2pUqnmbz+FT3g34eYHUySfK4ZEn4cnTvj0HqJDsRVxNz3fWXRYja+yTOzqQ1uJrVpdbaK3ePIeyXzr -r4rCYc/YVT0SYwg9RBWaF+L9G+LQqHviDEM11QxRKOZiz/9HpPrV7frn2WD9iUvwM4qK6upcH3FR9R3s -2RtvxSjRObeipvq9Fa/qFf0zxM6WjLuBPk1GX3RadJt/7TKGZI4PQvvYx3fDYOHvSQ8mrZXol3zGuH/3 -wYPwfBcSXwkazXB7w72ng+J50d7VGH7PNu3/zfjR122lnOI06lb/6J/X2S/TNj39DEjs9d5Oot10+wmT -3yO4vnkbnhY2355NXGDoN50X5f/nxf8CAAD//0nmRVG1PQAA +86DDVhtkYp9Uj18qDstlogGPjHY4tEv94/7M/74lW7lcO4utnitiDKD4z3Bt1esvD2T9/Z/r/35c/+Mu +XW8+/Nl7XeoXYWenz2DHBDNMinb+pKV8qf96aScmWVYRE96be0e4hr7MAsyTxMeQzC3ZG8lcz++RuS/O +UfIiD+5gQ/VGwtjp59k/DRTBhE3WUr2ZxZbTzyOwjRohgRuqNxLYTn+bwItGaP8aky/P6/Lfl4rnJD/L +pbO+SohezPOp0xdzxvXZKnREkxkoLk/Vyv06swQ5CJO0alouk23BeOZqXQr4d8niofNwufzhhvcOn+p9 +79e4UbTvR2Rp31MpDDybSqjpqa0KJH0E3DEOsSMIWksfURln2qQS04xR4x3PyRb4TRwooQdIdyjzIJdd +aiXRXkZNBI+U3BDcg1+zDvFgdNi3XLcs/9ssPAwTSlRKsqy3DoJITslqmTADufYLtEwKwb4V8K+axGAB +Lt8MpZqf8R5loVJFsHSkaWUnVOY5EXN51yVyRGh+EOd7LlvP0X3VztZb1og0ywgz9Hh8IGKEY0YZcmWB +NDYETLuCl75gWTzx/hLiXGb9dYsi3wIOXLLvWcPfm4XvjbP7hjABmAqSQ9COETIQhhGeagV0zGY8mza1 +XUlkpE4Q9kwbPIWiVW9cXJTqSpmBApHp1FY0l4fiJIO2vJk15mRiKsVYNmWSKdeWOANTDQTp4crxMidM +xFgICIMnJZmNie8u2IE4pq21XawGEEeGUuRNxI9L9Z3xz0pquD3S1iMeGsFXbYDYOB6zk5iTcrHN3KNe +MrS8rgK7MpQQmfCUM/E4v4nDs0GSHqQ216Cp5ACEmwM9AH2cGN6l6o2W2sQYOcvJPkykaIjkatSYzKr8 +Dlu535ekYxY3qEIi8XuG7AgYC0mlOhdPvjQdggbBarNH+uXOFpsTXlX9xXmyefGwCOVkN4nFpqPzruSE +ltgYQeuQRdXgPx0AiDPtgFjHRuqrapLLa8GorQs2DIKwdAx6xltZHAxttp0zokHfVtx1gsvxU6RN+Mb+ +fXLsyNBRnvF1YIBVF+9y7l3IJoyAX7NMVX0U348VVYToOpiSaH5KYXWOU+eEbycf1lrudkcNep0CbSJK +xZVnTBjYl3WRPwkUW870AbJLxqA0kkoe5xjeVlK8M0wUa1dhM4XsyDjsHYm3UnIgopcoEEiWSsFPEZTa +EAy2ODTQApk5pVKZ2VGhPuSpZt+h73tnq68ZbZwFOQ37332Nv05fQ580Nddha20yJlKpQAR9Qxup0j0S +CqkCZNKril6AzQok5fxDNprtBeEhNzO52l3ZBDAm7OwFZzkbdxpvYyeI1yxW80O0CXgWFbInKoTpAiGi +MjgQvCB1VI65G8lPi0gM1D96r/it6oVsvPQXQS93GZtR9ON3qkIHi7iKRug0IrV7zpB/jQjd26OKfHNV +HK9nioydrx31oxFB/1xOM21A0FP8RFs2OAm5tO6Kq7oqKrK38Ta60In31fp6wU8RRUgq1cjWxIvxygDW +6XRMwNaxCPMk8bHMXxnDqR275j6D0xqcOoTvkgYvNkxfCAgd1jNNts7hhy8vl4kEj354EMYXCAaZcx7R +IK8uQAD9Prv2huUgC3MtuCJoLodn7rWnzt2Kpv8/ZUIdSteCHloTapoGQTOJyaYgsurcJSr1IijOKNEh +eHNDi7pQGTGQ1jd0ZjopVAQJ58CZzmOQWZIBJ6er7MYenxDGC4SU0NEc4ozIpWBG4vVT5uQ5baatSAJe +a70UMxibE0SRe7CR9Yv1jqE2toSWqv7VD+ozHqwiWBCj5zKHc7UyYp3NjJ6jZF0G1PagITh+Vi3YkCQ5 +s7hpDlVQKew6Yqz0RrcobbSs+nJldJQbPjGRyafLo+8M2lacUHAi9q2K1gYJE+biAzhXLQphBwiCerHf +RCE0UQzN12VSZUXwBn3QWzf/BozYunsgu7d04WuvIxmdqiJ4mJdDLqcvu9xwET0kYkM2A3qJOv2tqVKp +5m8/hU94N+HmB1MknyuGRJ+HJ1749B6iQ7EVcddG31l0WA2vsozs6kNbia1aXW2it3j0Hsl866+KQrdn +7KseiTGEHqIKzQvx/g1xaNA98YahmmqGKBRzsef/I1L96nb982yw/l4m+E1GRXV1ro+43voO9uyNt2KQ +6LxbUVP93opX9Yr+GWJnS4bdwClNRl90WnSbf+0yXDLP16V97DN1w2Ax3ZN2Jq2VOC35jHH/7sMEwpu6 +kPhK0GiG2xv+PXWK50V7V8P9OG7c/5vxg0/lSjnFadCt/tE/r7OfuW16+nFI7PXeTqLddPsJY9vo/YDO +PS1sPmQbucDQbzovyv9fFv8LAAD//+uCPa4CPgAA `, }, "/data/config_schema_v3.5.json": { local: "data/config_schema_v3.5.json", - size: 16725, + size: 16802, modtime: 1518458244, compressed: ` -H4sIAAAAAAAC/+xbS4/jNhK++1cYTG7pR4DNLrBz2+Oeds/b8Ag0VZaZpkimSHnaGfi/LyRKaj0okbLV -0z3IBAimLRUf9f6qSH3dbLfkZ8OOkFPyaUuO1upPj4+/GyXv3dMHhdljivRg73/97dE9+4ncleN4Wg5h -Sh54lrg3yelvD39/KIc7EnvWUBKp/e/ArHuG8EfBEcrBT+QEaLiSZHe3Kd9pVBrQcjDk07bc3HbbkjQP -OtMai1xmpHp8qWbYbokBPHHWmaHd6k+Pr/M/tmR3w1k7m62ea2otoPzveG/V689P9P7Pf93/79f7fz4k -97tffu69LuWLcHDLp3DgkluuZLs+aSkv9V+XdmGaphUxFb21D1QY6PMswX5R+BziuSV7J57r9T0899k5 -KVHkQQ02VO/EjFt+Hf0ZYAg2bLKO6t0stlx+HYZd1Agx3FC9E8Nu+dsY3jRM+/dIPr/cl/9eqjln53Oz -dPZXMdGLeT5x+mLOtDxbgU5IMgUt1LnauV9mjiAHaUkrpu2W7Asu0qHUlYT/lFM8dR5ut1+H4b0zT/W+ -92vaKNr3E7y075mSFl5sxdT80k4Eij0DHriA2BEUnaVPiExwYxOFScqZ9Y4XdA/iphkYZUdIDqjy4CyH -xHFivBM1ETySc0sxg2jJmmOeGP5nT65PhEsLGSC5a8fuLoOxo8nCjjn06fK/3cYzIWFUJzRNe0xQRHou -d8Qt5MbP35YUkv9RwL9rEosFDOdNUen1J85QFTrRFEsvnJc9YSrPqVzLNZfwESH5UZLo+Xu9RvdVu1pv -WxPcbCOs0hMuAuEmHHBKS1cFstj4sdSPtltS8DSeOFtCnKu0v29Z5HtAchkRj5y093u38b0ZaN9SLgET -SXMI2jFCCtJyKhKjgfXIG03NaIZExXOCkHFj8eylfOWiu7EUNMjUJK6CWR56SQptObNqmEjlXEpx05RJ -pdwbGQxMDFBkxyvHq5xyGaNUkBbPWnEXxj5cfAJ5Slq7WSwGkCeOSuZNkI5L7Z3xL1oZuD04tom2Zvyu -9eldX3rkoDCn5WabtTcTKdhjeV0BdnkoITEVieDyeX0ThxeLNDkqY69BT+QIVNgjOwJ7nhnepeqNVsbG -GDnPaRYm0ixIYpSgtu6UzBFeDSfJqlrqTKuyrCSdMs1ReRIJ7FPkJ8BY9Kn0a1XlS8GhtB8sQ3uknx9c -FTrjftVfQozhri+7Dp8MOIwDxD2t5JSVuBfBmJBF1VVBMgIHr7QjYhMb0q8qVpYXiVGqC3YSgpBzClbG -W1kcxGzULjg1YG6r+jpR6PRbpE34xv5jduzE0Mk542u8wFRdLCuEdyO7MLp9yxJU9xF6P1ZUEaLrYFqh -/SZF02ucekUGbvFxHTVUd9Sgtym+ZqJUXOnVdCT8A3SxF9wcIV0yBpVVTIk4x/D2mOKdYaYQuwrEaeQn -LiAbcLxXSgCVvUSBQNNESXGOoDSWYrB9YYAVyO05UdquDh/9/ahXq2/bUf0NDTr5P3oWf52ehTkbZq/D -1samXCZKgwz6hrFKJxlSBokG5Moril6ATQt0pcFoGsMzSUXIzWyuD1d2C6wNO3sheM6nncZjtRF4zWE1 -P0SbgWdRIXumQpgvECIqgyPFBamjcszDRH7aRGKg/pl8Nd9dvZGdl34R9BpuYzeJfvxOVZhgEVfRSJNE -pHbP4fL3EaF7OqrId1fF8XqlyNj51lE/GhH0D+wMNxYkO8cvtOejU46ldVdc1VVR0Wy6FeOvTaJ9tb53 -8E1YkYopPaGaeDbeGMAOOh0zsHUqwnxR+Fzmr5TjnMauuegw6CHOnc53SYM3HuZvCoRO8bmh+8F5hy8v -l4kET354EMYXCBb54OCiQV5dgADmY7b3Lc9BFfZacEXRLodnw/tQnUsXzUHBnAl1KIcW9NQ5BnNNg6CZ -xGRTkGl1QBOVehG04IyaELy5oUVd6JRaSOqrO0sA5QyS1BSpECC4yWOQGUlB0PNVduPOWSgXBUJCWUQ7 -v9aU5Fbh9Uvm9CVplq1IAl7rvBRTmFoTZJF7sJHzi/sDR2NdCa10/asf1C+TbYnYTna3lVCBGLOWOXir -lXWuIukitrFKcshV6CT69t7kQOUIpswIUyclH0UAHuoMJCBnSc8aJqLLmPaN2r23W7ZLM0pwh4XXMG+m -pNtHTOS5MdSVcaes5HNtTVRo/cJlqr4sz6grSFsLymCQhW8VtLFIubSLD1WHYtEIB0CQDGbdclzczhS4 -63UOdVnlvUNv+1bl34D7veFmDrqNB4xqgL72PFqb1tbMzaeUG4ZgoV25vUC1ibeEeSsgz3XxHQzU5ERF -EdGsvep4e6r8ixh88X5vEdJpQ7YCFo+5SRJ136GmSpRev+EavtOwC7f7uKb5WhE2+gYI8RYMHyF2Fns5 -0U/72LHzbnzLa0KrT23v4a6V1S5axZOOsd7+qzbI8JTE1y+h1lJ2jGqtLKxwb8hEo36hN1TVVD8i1YJI -9b3b9bezwfrTseDnSRVV+GuvGywv4p73B9DrO6trlAy96qqpfqjrvdU1OH3vqG3cR5+TZPQVwU23bd5u -Y0jm+WB7qoKZ3NTUac5g0VqI85yvmD8efplBinNXed8IYq1w78mv00GLYtPechp+bzodI5rxo69PSz7l -eXTO87V/0u2+HN315DMgcTfoOwl7F1X4+r5JHZ6zN9+GTlz96VeHm/L/y+b/AQAA///+Z1URVUEAAA== +H4sIAAAAAAAC/+xbSY/jNha++1cISm6pJcBkBpi+zXFOM+cpuAWaepaZokjmkXKX0/B/H0iUVFpIkbZV +XdVIBwi6LD0ub/veQurrJknSnzU9QEnST0l6MEZ9enz8XUtxb58+SCwecyR7c//rb4/22U/pXT2O5fUQ +KsWeFZl9kx3/9vD3h3q4JTEnBTWR3P0O1NhnCH9UDKEe/JQeATWTIt3ebep3CqUCNAx0+impN5ckPUn3 +YDCtNshEkTaPz80MSZJqwCOjgxn6rf70+Dr/Y092N511sNnmuSLGAIr/zvfWvP78RO7//Nf9/369/+dD +dr/95efR61q+CHu7fA57JphhUvTrpz3luf3r3C9M8rwhJny09p5wDWOeBZgvEp9DPPdk78Rzu76D5zE7 +R8mrMqjBjuqdmLHLr6M/DRTBhE3WUr2bxdbLr8OwRY0Qwx3VOzFsl7+N4U3HtHuP6eeX+/rfczPn4nx2 +lsH+GiZGmOcSpwtz/PLsBeqRZA6Ky1Ozc7fMLEEJwqS9mJIk3VWM51OpSwH/qad4GjxMkq9TeB/M07wf +/fIbRf/ew0v/nkph4MU0TC0vbUUg6TPgnnGIHUHQWrpHZJxpk0nMckaNczwnO+A3zUAJPUC2R1kGZ9ln +lhPtnKhD8EjODcECoiWrD2Wm2Z8juT6lTBgoANO7fuz2PBk7myzsmFOfrv/bbhwTppSojOT5iAmCSE71 +jpiBUrv5S9JKsD8q+HdLYrCC6bw5SrX+xAXKSmWKYO2Fy7JPqSxLItZyzUv4iJD8LEiM/L1dY/iqX220 +LQ83SYRVOuAiADdhwKktXVZIY/HjUj9KkrRieTxxcQlxKfPxvkVV7gDT84x45qSj39uN681E+4YwAZgJ +UkLQjhFyEIYRnmkF1GczDqUtqSuNhPkUoWDa4MlJu/EgVRxKDbnMQYHIdWbLoctxPM2hr41WxZxcLMUn +O00doeq9pZOBmQaC9HDleFkSJmIsBITBk5LMYuKHAzsQx6y3tovFAOLIUIqyQ/y4PGEw/kVJDbcjbR+1 +W8bveoDYTjxmL7Ek9Wa7tb1eMre8oQCHPNT5NeEZZ+J5fROHF4MkO0htrknF0gMQbg70APR5YfiQajRa +ahNj5KwkRZhI0SCJlpyYtu2yRHh1bpquqqXBtLIoalKfac5qncgqIUd2BIxNZaV6LdFc8TyUQwRr2hHp +5wdb0i64X/MX5/Pc2RWqp0+m0S42br1qpSS0TqIRtA5ZVFtiZLNM45V2RqxjIf2qyufyijNKdcG2RDB/ +9eWo8VYWl692aueMaNC3lZADFDr+FmkTrrH/WBzrGeqdM75gDEw1TIw5d25kG06V37KeVeN0f4wVDUIM +HUxJNN+kAnvFqdfMwC4+L8qm6o4a9DaV3AJKxdVxXXvDPUBVO870AfJLxqA0kkoe5xjOhlW8MyxUdVcl +cQrZkXEoJhzvpORAxChQIJA8k4KfIii1IRjshWigFTJzyqQyq6eP7ubWq9X3va3xhibHAj8aIH+dBog+ +aWquy621yZnIpAIR9A1tpMoKJBQyBcikUxQjgM0rtKXBbBrNCkF4yM1MqfZXdguMCTt7xVnJ/E7j7AAF +8zWbq7lTtIX0LAqyFyqE5QIhojI4ELwgdDSOuffEp01kDjQ+4G/mu2s3snXSX5R6Tbex9WY/bqeqdLCI +a2iEziJCu+Ok+vtA6JGOGvLtVTjerhSJnW+N+tEZwfj0TzNtQNBT/EI7NjsyubTuiqu6GipS+Fsx7tok +2lfbSwzfhBUhqVQe1cSz8cYJ7KTTsZC2+hDmi8TnOn7lDJc0ds2tiUkPcemof0gavD6xfO0gdCWAabKb +nJK44nIdSPDoTg/C+QWCQTY5uOgyr2GCAPpjtvcNK0FW5trkiqC5PD2bXq4a3ODoDgqWTGhAObWgp96E +uqZB0ExioimIvDmgiQq9CIozSnQovbmhRV2pnBjI2ntAKx0pKoKEc+BMlzGZWZoDJ6er7MaesxDGK4SM +0Ih2fqspwYzE65csyUvWLduQBLzWeinm4FsTRFU6ciPrF/d7htrYElqq9tcY1Fc8gUWwSYxeyxyc1co6 +95pUFdtYTUsoZfj0+tbe5OzQXNcRwXdS8lEE4KAuQAAymo2swYMuc9o3avfebtk2zEjObC68hnlTKew+ +YpDnRqircaeu5EtldBS0fmEil18uj6grSFtxQmEShW8VtDZImDAXH6pOxaIQ9oAgKCy65by4XShw1+sc +qrrKe4fe9q3KvyHvd8LNUuo2HzCrAcbac2jNry2/lupigCIY6Fd23cYKWcKyFaTPbfEdBOr0SHgV0ay9 +6njbV/5FDD47P94I6bQjWyEXj7lJEnXfoaXKpFq/4Rq+07ANt/uYIuVaCBt9AyR1FgwfATurnfD00z42 +dt7Nb3l5tPrU9x7uellto1XsdYz19t+0QaanJK5+CTGG0ENUa+XCCveGSDTrFzqhqqX6gVQXINX3btff +zgbb79CC3zo1VOFPx26wvIjb4R9Ar++srlkwdKqrpfqhrvdW1+T0faC2eR99SZLRVwQ3w7Z5v40pmePr +b18F492U7zRnsmgrxGXOV4wfD78sZIpLV3nfKMVa4d6TW6eTFsWmv+U0/XjVjxHd+NmnrDWf4jQ75/k6 +Pum2n6FuR/KZkNgb9IOAvY0qfF0fuE7P2bsPTT1Xf8bV4ab+/7z5fwAAAP//yoGbgKJBAAA= `, }, "/data/config_schema_v3.6.json": { local: "data/config_schema_v3.6.json", - size: 17007, + size: 17084, modtime: 1518458244, compressed: ` -H4sIAAAAAAAC/+xbS4/jNhK++1cYTG7pxwAbBNi57XFPu+dteASaKttMUyRTpDztDPzfF3q2RJEibaun -O8gECKYtFR/1YNVXxdK31XpNfjbsAAUln9fkYK3+/Pj4u1Hyvnn6oHD/mCPd2ftPvz42z34id9U4nldD -mJI7vs+aN9nxHw+/PVTDGxJ70lARqe3vwGzzDOGPkiNUg5/IEdBwJcnmblW906g0oOVgyOd1tbn1uifp -HgymNRa53JP68bmeYb0mBvDI2WCGfqs/Pb7O/9iT3bmzDjZbP9fUWkD53+ne6tdfnuj9n/+6/9+n+38+ -ZPebX34eva7ki7Brls9hxyW3XMl+fdJTntu/zv3CNM9rYipGa++oMDDmWYL9qvA5xnNP9k48t+t7eB6z -c1SiLKIa7KjeiZlm+WX0Z4Ah2LjJNlTvZrHV8ssw3HiNGMMd1Tsx3Cx/G8Orjmn/HsmXl/vq33M95+x8 -zSyD/dVMjHyeT5w+nxOWZy/QgCRz0EKd6p37ZdYQFCAt6cW0XpNtyUXuSl1J+E81xdPg4Xr9zXXvg3nq -96NfYaPo3wd46d8zJS282Jqp+aUbESj2DLjjAlJHUGwsPSAywY3NFGY5Z9Y7XtAtiJtmYJQdINuhKqKz -7LKGE+OdqPPgiZxbintIlqw5FJnhf47k+kS4tLAHJHf92M3ZGTuZLH4w3TNd/bdZeSYkjOqM5vmICYpI -T9WOuIXC+Plbk1LyP0r4d0tisQR33hyVXn7iPapSZ5pidQrnZU+YKgoqlzqal/CRIPlJkBid93aN4at+ -tdG2AtysE6zS4y4i7ibucCpLVyWyVP9x6Tlar0nJ83Ti/SXEhcrH+5ZlsQUk5wnx5JCOfm9WvjeO9i3l -EjCTtICoHSPkIC2nIjMa2Ii809SMZkiSPycIe24snryUr1wMN5aDBpmbrMlgLne9JIc+nVnUTeRyLqQ0 -01RBpdobcQZmBiiyw5XjVUG5TFEqSIsnrXjjxj6cfwJ5zHq7uVgMII8clSw6J50W2gfjX7QycLtz7ANt -y/hdf6Y3Y+mRncKCVpvt1l4FQrDH8oYCHPJQQWIqMsHl8/ImDi8WaXZQxl6DnsgBqLAHdgD2PDN8SDUa -rYxNMXJe0H2cSLMoiVGC2rZSMkd4NZwki2ppMK3a7yvSkGlO0pNEYJ8jPwKmok+lX7MqXwiOhf1oGjoi -/fLQZKEzx6/+S4gp3PVFV/eJw2EaIB5ppaCswr0IxsQsqs0Ksgk4eKWdEJtUl35VsnJ5kpikumglIQo5 -Q7Ay3crSIGandsGpAXNb1jfwQsdfE23CN/a32bGBocE503O8yFRDLCuEdyObOLp9yxRUjxH62FfUHmJ4 -wLRC+12Splc/9YoMmsWneZSr7qRBb5N8zXiptNSrq0j4B+hyK7g5QH7JGFRWMSXSDoa3xpR+GGYSsatA -nEZ+5AL2DsdbpQRQOQoUCDTPlBSnBEpjKUbLFwZYidyeMqXt4vDRX496tfq+HDXekFPJ/1Gz+PvULMzJ -MHsdtjY25zJTGmT0bBirdLZHyiDTgFx5RTFysHmJTWowmcbwvaQidsxsoXdXVgusjR/2UvCChw+Nx2oT -8FqD1fwQbQaeJbnsmQxhPkFIyAwOFC8IHfXB3AXi0yoRA43v5Ov57tqNbLz0F0EvdxubIPrxH6rSRJO4 -mkaaLCG0ey6X/xoeeqSjmnxzlR9vV0r0nW/t9ZMRwfjCznBjQbJT+kJbPrnluDTvSsu6aiq6D5di/LlJ -8llt+w6+CytSMaUDqrmRjT6kvD0XHYYLJ6eu55zJYwsueVEW5PP6UyhjTZfMG0N7pwY0A+hDvverwucq -succ52z5mhYQp7o617cwJI32gsz3UMT6G7ihW+cmyIdYKkPBox84xZEXgkXuXOl0mHQIncB8zIsPywtQ -pb0WdlK0lwNXt1Ns0I7SXaHMmdCA0rWgp8EFYVNOiZpJCs4AmddXV0mgBEELzqiJAb8bivelzqmFrG1q -ugRqz2BsTZEKAYKbIgWzkhwEPV1lN80NFOWiRMgoS7joaDUluVV4/ZIFfcm6ZWuSyKltTinmEFoTZB09 -XNTYnIv7HUdjm+KC0u2vsVM/Bws2qTX+YZGlhndmKXPw5nHLNGnpMrXkTAooVOyO/vaqraNyBFNFhNAd -0kcRgId6DxKQs2xkDQHvMqV9o0L47ZbdhBkleJMlLGHeTMlmHyme50ZXV/kdai0U2pok1/qVy1x9vTyi -LiBtLSgDJwrfKmhjkXJpL75udsWiEXaAIBnMHstp2j+T+i9XU9VV/vsOVf9blX8D7ve6mznoNh0wyQHG -2vNoLaytmZ6wnBuGYKFfuW8tW6VbwrwVkOe2LBF11ORIRZlQxr7q4j+U/iUMPnu/RInptCNbAIun9Ngk -dYK0VJnSy5ei490em3ghlGtaLOVhk3tjiDdh+Ai+s9zKQKXxY/vOu2n/W0CrT33t4a6X1SZZxcGDsdz+ -6zKIe3/kq5dQayk7JJVWLsxwb4hEk0qq11W1VD881QWe6q9u19/PBtuP6qIfbtVU8e/gbrC8hA74D6DX -d1bXJBh61dVS/VDXe6vL6UsYqG1aR5+TZHLz5GpYNu+34ZJ5PmUPZTDBTYVuc5xFWyHOc75g/Hj4ZQYp -zjU5vxHEWqAjzK9Tp0Sx6vu/3C9xwz6iGz/5LrfiU54m9zzfxj0AzTe1m5F8HJLm24JBwN4kJb6+r3Xd -DoTuq9lAU9Q4O1xV/59X/w8AAP//zRo7vm9CAAA= +H4sIAAAAAAAC/+xbS4/jNhK++1cISm7pxwAbBNi57XFPu+dteASaKstMUyRTpDztDPzfF3q2RJEibaun +O8gECKYtFR/15FfF0rdNkqQ/a3qAkqSfk/RgjPr8+Pi7luK+ffogsXjMkezN/adfH9tnP6V39TiW10Oo +FHtWZO2b7PiPh98e6uEtiTkpqInk7negpn2G8EfFEOrBT+kRUDMp0u3dpn6nUCpAw0Cnn5N6c0kykPQP +RtNqg0wUafP43MyQJKkGPDI6mmHY6k+Pr/M/DmR39qyjzTbPFTEGUPx3vrfm9Zcncv/nv+7/9+n+nw/Z +/faXnyeva/ki7Nvlc9gzwQyTYlg/HSjP3V/nYWGS5w0x4ZO194RrmPIswHyV+BzieSB7J5679R08T9k5 +Sl6VQQ32VO/ETLv8OvrTQBFM2GRbqnez2Hr5dRhuo0aI4Z7qnRhul7+N4U3PtHuP6ZeX+/rfczPn4nzt +LKP9NUxMYp5LnK6Y45fnIFCPJHNQXJ6anbtl1hKUIEw6iClJ0l3FeG5LXQr4Tz3F0+hhknyzw/tonub9 +5JffKIb3Hl6G91QKAy+mYWp56VYEkj4D7hmH2BEEW0v3iIwzbTKJWc6ocY7nZAf8phkooQfI9ijL4Cz7 +rOVEOyfqI3gk54ZgAdGS1Ycy0+zPiVyfUiYMFIDp3TB2e7bGziYLO6bt0/V/241jwpQSlZE8nzBBEMmp +3hEzUGo3f0laCfZHBf/uSAxWYM+bo1TrT1ygrFSmCNZeuCz7lMqyJGIt17yEjwjJzw6Jib93a4xfDatN +tuXhJomwSke4CISbcMCpLV1WSGPjx6V+lCRpxfJ44uIS4lLm032LqtwBpucZ8cxJJ7+3G9cbS/uGMAGY +CVJC0I4RchCGEZ5pBdRnMw6lLakrjQzzKULBtMGTk3bjiVRxUWrMZQ4KRK6zNh26PI6nOQy50aoxJxdL +51M7TX1C1XtLrYGZBoL0cOV4WRImYiwEhMGTkqyNiR8u2IE4ZoO1XSwGEEeGUpR9xI/DCaPxL0pquD3S +Dqd2x/jdECC2lsfsJZak3my/ttdL5pY3FuCYhxpfE55xJp7XN3F4MUiyg9TmGiiWHoBwc6AHoM8Lw8dU +k9FSmxgjZyUpwkSKBkm05MR0ZZclwquxabqqlkbTyqKoSX2mOct1IrOEHNkRMBbKSvWaornO8xCGCOa0 +E9IvD21Ku+B+zV+cz7Gz66i2n9inXey59aqVktAaRCNoHbKoLsXIZkjjlXZGrGND+lWZz+UZZ5TqgmWJ +IH71YdR4K4vDq73aOSMa9G0p5CgKHX+NtAnX2N8Wx3qGeueMTxgDU42BMefOjWzDUPkt81k1hfvTWNFE +iLGDKYnmu2Rgr3HqFRm0i8+TMlvdUYPeJpNbiFJxeVxf3nAPUNWOM32A/JIxKI2kksc5hrNgFe8MC1nd +VSBOITsyDoXF8U5KDkRMDgoEkmdS8FMEpTYEg7UQDbRCZk6ZVGZ1+Ogubr1a/VDbmm7Iuhb4UQD5+xRA +9ElTcx221iZnIpMKRNA3tJEqK5BQyBQgk05RTAJsXmGbGsym0awQhIfczJRqf2W1wJiws1eclczvNM4K +UBCvtVjNDdEW4FlUyF7IEJYThIjM4EDwgqOjccy953zaRGKg6QV/M99dt5Gtk/4i6GVvY+tFP26nqnQw +iWtohM4ijnbHTfVfI0JPdNSQb6+K491KkbHzraN+NCKY3v5ppg0IeopfaMdmVyaX5l1xWVdDRQp/Kcad +m0T7atfE8F1YEZJK5VHNjWwMR8rbc9FjOH9yakfOhTy2ZIKVVZl+Tj75MtZ4ybwxtLdqQAuA3hd7v0p8 +rk/2nOGSLV/TT2JVV5eaIMakwcaS5YaMULME02Rn3R+5EEttKHh0A6cw8kIwyKwrnR6TjqET6I958WFY +CbIy18JOguZy4Gq3nY16W/orlCUTGlHaFvQ0mFBfTgmaSQzOAJE3V1dRoARBcUaJDgG/G4r3lcqJgazr +kFrpslURJJwDZ7qMwaxpDpycrrKb9gaKMF4hZIRGXHR0mhLMSLx+yZK8ZP2yDUnAa1svxRx8a4JoTg8b +NbZ+cb9nqE1bXJCq+zUN6iveTSO08E6vZQ7OPG6dji9VxZac0xJKGb7Xv7VqO2sn0PWJ4LtD+igCcFAX +IAAZzSbW4Ikuc9o3KoTfbtntMSM5a7OENcybStHuIyby3Bjq6rhDjIFSGR0VWr8ykcuvl5+oK0hbcULB +OoVvFbQ2SJgwF18322JRCHtAEBQW3XKe9i+k/uvVVFWd/75D1f9W5d+A+53hZgm6zQfMcoCp9hxa82vL +r6U6GaAIBoaVXX1qIUtYtoL0uStLBAN1eiS8iihjX3Xx70v/IgafnZ+1hHTak62AxWN6bKI6QTqqTKr1 +S9Hhbo9tuBDKFCnXirDRvTGpM2H4CLGz2glPpfFjx867ef+bR6tPQ+3hbpDVNlrFXsdYb/9NGcS+P3LV +S4gxhB6iSisXZrg3nESzSqozVHVUPyLVBZHqr27X388Guy/0gl+BNVThj+pusLyIvvkPoNd3VtfsMHSq +q6P6oa73VpfVlzBS27yOviTJ6ObJzbhsPmzDJnN8F+/LYLyb8t3mWIt2QlzmfMXz4+GXBaS41OT8RhBr +hY4wt06tEsVm6P+yP+v1x4h+/Owj35pPcZrd83yb9gC0H+huJ/KxSNpvC0YH9jYq8XV9+mt3IPSf4Hqa +oqbZ4ab+/7z5fwAAAP//nm8U9rxCAAA= `, }, "/data/config_schema_v3.7.json": { local: "data/config_schema_v3.7.json", - size: 17777, + size: 17854, modtime: 1518458244, compressed: ` -H4sIAAAAAAAC/+xcS4/bOBK++1cYmrlNPwLsYBeb2x73tHvehiPQVNnmNEVyipTTTuD/vtDTEkWKlK1O -dzAdIEi3VHzUg8WvHsr31Xqd/KrpAXKSfF4nB2PU58fHP7QU9/XTB4n7xwzJztx/+v2xfvZLcleOY1k5 -hEqxY/u0fpMe//bwj4dyeE1iTgpKIrn9A6ipnyH8WTCEcvBTcgTUTIpkc7cq3ymUCtAw0Mnndbm59boj -aR/0ptUGmdgn1eNzNcN6nWjAI6O9Gbqt/vJ4mf+xI7uzZ+1ttnquiDGA4r/jvVWvvzyR+2//uv/fp/t/ -PqT3m99+Hbwu5Yuwq5fPYMcEM0yKbv2kozw3P527hUmWVcSED9beEa5hyLMA81Xic4jnjuyNeG7Wd/A8 -ZOcoeZEHNdhSvREz9fLL6E8DRTBhk62p3sxiy+WXYbj2GiGGW6o3Yrhe/jaGVy3T7j0mX17uy3/P1ZyT -89Wz9PZXMTHweS5xunyOX56dQD2SzEBxeap27pZZTZCDMEknpvU62RaMZ7bUpYD/lFM89R6u199t996b -p3o/+M1vFN17Dy/deyqFgRdTMTW9dC0CSZ8Bd4xD7AiCtaV7RMaZNqnENGPUOMdzsgV+0wyU0AOkO5R5 -cJZdWnOinRO1HjySc0NwD9GS1Yc81ezbQK5PCRMG9oDJXTd2c7bGjiYLH0z7TJd/NivHhAklKiVZNmCC -IJJTuSNmINdu/tZJIdifBfy7ITFYgD1vhlItP/EeZaFSRbA8hdOyT6jMcyKWOppz+IiQ/OiSGJz3Zo3+ -q261wbY83KwjrNLhLgLuJuxwSkuXBdJY/zH3HK3XScGyeOL9HOJcZsN9iyLfAibnEfHokA5+36xcbyzt -G8IEYCpIDkE7RshAGEZ4qhXQAXmrqQnNJFH+PEHYM23w5KS8cNHfWAYKRKbTOoKZ73qTDLpwZlE3kYmp -K6WeprxUyr0l1sBUA0F6uHK8zAkTMUoFYfCkJKvd2LvzTyCOaWc3s8UA4shQirx10nFXe2/8i5IabneO -3UXbMH7XnenNUHrJTmJOys22a688V7DD8voC7PNQQmLCU87E8/ImDi8GSXqQ2lyDnpIDEG4O9AD0eWJ4 -n2owWmoTY+QsJ/swkWBD97+VkgMRQyJFg/NoyYlp0ilThFdjzmRRVfamlft9Seqz31EME4n+M2RHwFiI -KtUl9HLd0yFsEIxVB6RfHupQdeKMVj9xPsbErivYfmJxGIeaB1rJCS3BMYLWIYtqQod0hCAutCNiHev3 -r4po5keSUaoLphuCuNSHPeOtLA6HtmrnjGjQt4WGPS90/D3SJlxj/z451jPUO2d8IBiYqg94OXduZBOG -wK8Zp6ohjB/6ispD9A+Ykmh+SGR18VMX+FAvPg62bHVHDXqdCG3CS8XFZ23awj1AFVvO9AGyOWNQGkkl -jzsYzkRU/GGYiNauQnoK2ZFx2Fscu2AMAslSKfgpglIbgsEchwZaIDOnVCqzOMZ0J60uVt/lrIYbstL9 -H4mNv05iQ580Nddha20yJlKpQATPhjZSpXskFFIFyKRTFAMHmxVYhwajaTTbC8JDx8zkandlSsGY8GEv -OMuZ/9A4rDYCr9VYzQ3RJuBZlMueiBCmA4SIyOBAcMbVUR3Mned+WkVioGHhvprvrtnIxkk/C3rZ29h4 -0Y/7UBU6GMRVNEKnEVe7owL9c3jogY4q8s1VfrxZKdJ3vrbXj0YEw6qeZtqAoKf4hbZsVAqZG3fFRV0V -Fdn7UzHu2CT6rDbNCT+EFSGpVB7V3MhGd6W8PhcthvMHp7bnnIhjcyZYXuTJ5/UnX8QaL5lXhvZWDmgC -0Pt871eJz+XNnjGcsuXzdLvGsBViZj+JlaqdaoLokwYbS6YbMkLNEkyTrVVWcuZthQE8ugFWGKEhGGRW -fajFrn2IBfp9VlEMy0EW5lp4StDMB7h221mvt6Wtx0yZUI/StqCnXrWxTrsEzSQGj4DIqjpYFHhBUJxR -okMA8YYkP0rOt4Q+p02P1BxQPoHGFUHCOXCm8xh0m2TAyekqy6kLWoTxAiElNKIk0uhKMCPx+iVz8pK2 -y1YkgXNbn1PMwLcmiOqesfFlfTLudwy1qdMQUjW/Dd3/2Zvaia0GXK4OlREDHybxYRL9DF0VG+ilzMGZ -BFimDVAVsfWKJIdchrpAbk/5WypH0CVM8BUg34sAHNR7EICMpgNr8Fw5Y9pXqqLcbtk19pCc1SHmEuZN -paj3EeN5bnR1pd8pgXiujI5yrV+ZyOTX+TBrAWkrTihY0OxWQWuDhAkzu1fBFotC2AGCoDB5LMc5o4m8 -0XIJeYVAsjcoGd2q/Bs+LnC6myk8Px4wCgyH2nNoza+tia7DjGmKYKBbuWteXMVbwrQVJM9NTivoqJMj -4UVEDeSqrhFf7iBi8Nn5rVNIpy3ZAgFaTBdXVBtRQ5VKtXwdI9wqtAln0Zki+VIeNrqxKnEGDO/BdxZb -4UlTv2/feTfusPRo9alLSN11stpEq9h7MJbbf5Ubs4uPriQaMYbQQ1S+bWba4wekL0fpeqdLa6g+PNoM -j/az2//7s9XmM9Dgp4YVVfjLzRssNOKbjXeg/59EraNL2KnWhupDrT+LWq2mm556x8WfKYlHdwav+rWe -bhs2meM/c/BFWN5N+UqV1qKNsKc5X/DeevhtAslOdfC/EgRcoN3RrVMrhbLqmhvtb9H9vqQdP/oyveRT -nEbFye/DBpf6q/LNQD4WSf11TQ8obKICc9f36nZ7TfvduKfjbxi9rsq/59X/AwAA//+9o87pcUUAAA== +H4sIAAAAAAAC/+xc3W/bOBJ/918haPdt46TALe5wfbvHe7p7vsAVaGpsc0OR3CHlxi38vx/0GYkiRdpW +mhSbAkUTafgxn/zNcNTvqyRJf9X0AAVJPyfpwRj1+eHhDy3Funl6L3H/kCPZmfWn3x+aZ7+kd9U4lldD +qBQ7ts+aN9nxb/f/uK+GNyTmpKAikts/gJrmGcKfJUOoBj+mR0DNpEg3d6vqnUKpAA0DnX5Oqs0lSU/S +PRhMqw0ysU/rx+d6hiRJNeCR0cEM/VZ/eXiZ/6Enu7NnHWy2fq6IMYDiv9O91a+/PJL1t3+t//dp/c/7 +bL357dfR60q+CLtm+Rx2TDDDpOjXT3vKc/vTuV+Y5HlNTPho7R3hGsY8CzBfJT6FeO7J3ojndn0Hz2N2 +jpKXRVCDHdUbMdMsv4z+NFAEEzbZhurNLLZafhmGm6gRYrijeiOGm+VvY3jVMe3eY/rleV39e67nnJ2v +mWWwv5qJUcxzidMVc/zy7AXqkWQOistTvXO3zBqCAoRJezElSbotGc9tqUsB/6mmeBw8TJLvdngfzFO/ +H/3mN4r+vYeX/j2VwsCzqZmaX7oRgaRPgDvGIXYEwcbSPSLjTJtMYpYzapzjOdkCv2kGSugBsh3KIjjL +Lms40c6JuggeybkhuIdoyepDkWn2bSTXx5QJA3vA9K4fuzlbYyeThR3T9unqz2blmDClRGUkz0dMEERy +qnbEDBTazV+SloL9WcK/WxKDJdjz5ijV8hPvUZYqUwQrL5yXfUplURCxlGtewkeE5CeHxMjf2zWGr/rV +RtvycJNEWKUjXATCTTjgVJYuS6Sx8eNSP0qStGR5PPH+EuJC5uN9i7LYAqbnCfHESUe/b1auN5b2DWEC +MBOkgKAdI+QgDCM80wqoz2YcSptTVxoZ5lOEPdMGT07alSdSxUWpIZc5KBC5zpp06PI4nubQ50aLxpxc +zJ1PzTTVCVXtLbUGZhoI0sOV42VBmIixEBAGT0qyJia+u2AH4pj11naxGEAcGUpRdBE/DicMxj8rqeH2 +SNuf2i3jd32A2Fges5NYkGqz3dpeL5la3lCAQx4qfE14xpl4Wt7E4dkgyQ5Sm2ugWHoAws2BHoA+zQwf +Uo1GS21ijJwVZB8mEmx8lmyl5EDEmEjR4DxacmLa2swc4dUANl1UlYNp5X5fkfrsd5IQRaYSObIjYCze +leolj3Md+iGgEUx8R6Rf7pu8d8ZH6584nwJs13luP7GPxNjD7UUrBaEV0kbQOmRRbR6STeDIC+2EWMfG +/avSo8vT0ijVBWsXQZDrA7LxVhYHaju1c0Y06NvyzEEUOv4eaROusX+fHesZ6p0zPqsMTDVEz5w7N7IJ +4+nXTHrVOCcYx4o6QgwdTEk0PyRNe4lTL/ChWXyaudnqjhr0OuneTJSKS/a6Goh7gCq3nOkD5JeMQWkk +lTzOMZxVrXhnmEn9rkJ6CtmRcdhbHLtgDALJMyn4KYJSG4LBgokGWiIzp0wqszjGdFfAXqy+L4CNN2Td +HXxUSf46VRJ90tRch621yZnIpAIR9A1tpMr2SChkCpBJpyhGATYvsUkNJtNotheEh9zMFGp3ZUnBmLCz +l5wVzO80zjJREK81WM0N0WbgWVTInskQ5hOEiMzgQPCCo6N2zJ3nfFpFYqBxF0A93127kY2T/iLoZW9j +40U/bqcqdTCJq2mEziKOdsd19s8RoUc6qsk3V8XxdqXI2PnaUT8aEYyvCDXTBgQ9xS+0ZZN7lUvzrris +q6Yie38pxp2bRPtq2+nwQ1gRkkrlUc2NbPRHyutz0WE4f3JqR86ZPLZgghVlkX5OPvky1njJvDK0t2pA +M4DeF3u/SnyqTvac4Zwtn+d7P8Z9FRc2p1il2rmOiiFpsEtlvrsj1HnBNNlal1HOuq0wgEc3wAojNASD +zLof6rDrEGKBfp+3KIYVIEtzLTwlaC4HuHYP26BRpruPmTOhAaVtQY+9CXVll6CZxOAREHl9DxYFXhAU +Z5ToEEC8ociPkvMtoU9Z23C10N2tIkg4B850EYNu0xw4OV1lOc2FFmG8RMgIjbgSaXUlmJF4/ZIFec66 +ZWuSgN82foo5+NYEUZ8zNr5sPGO9Y6hNU4aQqv1tHP4XvOouVU4MfJjEh0kMK3R1bqCXMgdnEWCZnkJV +xt5XpAUUMtw5cmvJf9KwoiuY4LuAfC8CcFDvQQAymo2swXPkTGlf6RbldstusIfkrEkxlzBvKkWzj5jI +c2Ooq+JOBcQLZXRUaP3KRC6/Xg6zFpC24oSCBc1uFbQ2SJgwF/cq2GJRCDtAEBRm3XJaM5qpGy1XkFcI +JH+DK6NblX/DlwrOcDOH56cDJonhWHsOrfm15ddSlSFSBAP9yq5OyJAlzFtB+tTWtIKBOj0SXkbcgVzV +NeKrHUQMPjs/nArptCNbIEGL6eKKaiNqqTKplr/HCLcKbcJVdKZIsVSEjW6sSp0Jw3uIneVWeMrU7zt2 +3k07LD1afewLUne9rDbRKvY6xnL7r2tj9uWjq4hGjCH0EFVvu7Ds8QPKl5NyvTOktVQfEe2CiPaz2//7 +s9X2m9Lgd4s1Vfgz0BssNOJLj3eg/59ErZND2KnWlupDrT+LWq2mm4F6p5c/cxKP7gxeDe96+m3YZI7/ +GcKXYXk35buqtBZthT3P+YLn1v1vM0h2roP/lSDgAu2Obp1aJZRV39xof9jujyXd+Mln7hWf4jS5nPw+ +bnBpPlHfjORjkTRf1wyAwiYqMXd9/G6313QfoXs6/sbZ66r6e179PwAA//8ZL3SpvkUAAA== `, }, diff --git a/cli/compose/schema/data/config_schema_v3.3.json b/cli/compose/schema/data/config_schema_v3.3.json index 0b735cfe28c9..13a58044d843 100644 --- a/cli/compose/schema/data/config_schema_v3.3.json +++ b/cli/compose/schema/data/config_schema_v3.3.json @@ -118,10 +118,14 @@ } }, "container_name": {"type": "string"}, - "credential_spec": {"type": "object", "properties": { - "file": {"type": "string"}, - "registry": {"type": "string"} - }}, + "credential_spec": { + "type": "object", + "properties": { + "file": {"type": "string"}, + "registry": {"type": "string"} + }, + "additionalProperties": false + }, "depends_on": {"$ref": "#/definitions/list_of_strings"}, "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "dns": {"$ref": "#/definitions/string_or_list"}, diff --git a/cli/compose/schema/data/config_schema_v3.4.json b/cli/compose/schema/data/config_schema_v3.4.json index 93fe821a1308..8660c98da42b 100644 --- a/cli/compose/schema/data/config_schema_v3.4.json +++ b/cli/compose/schema/data/config_schema_v3.4.json @@ -121,10 +121,14 @@ } }, "container_name": {"type": "string"}, - "credential_spec": {"type": "object", "properties": { - "file": {"type": "string"}, - "registry": {"type": "string"} - }}, + "credential_spec": { + "type": "object", + "properties": { + "file": {"type": "string"}, + "registry": {"type": "string"} + }, + "additionalProperties": false + }, "depends_on": {"$ref": "#/definitions/list_of_strings"}, "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "dns": {"$ref": "#/definitions/string_or_list"}, diff --git a/cli/compose/schema/data/config_schema_v3.5.json b/cli/compose/schema/data/config_schema_v3.5.json index 4244e102a45d..bf9c56c02e37 100644 --- a/cli/compose/schema/data/config_schema_v3.5.json +++ b/cli/compose/schema/data/config_schema_v3.5.json @@ -122,10 +122,14 @@ } }, "container_name": {"type": "string"}, - "credential_spec": {"type": "object", "properties": { - "file": {"type": "string"}, - "registry": {"type": "string"} - }}, + "credential_spec": { + "type": "object", + "properties": { + "file": {"type": "string"}, + "registry": {"type": "string"} + }, + "additionalProperties": false + }, "depends_on": {"$ref": "#/definitions/list_of_strings"}, "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "dns": {"$ref": "#/definitions/string_or_list"}, diff --git a/cli/compose/schema/data/config_schema_v3.6.json b/cli/compose/schema/data/config_schema_v3.6.json index 95a552b346cb..cd6a638ceb7f 100644 --- a/cli/compose/schema/data/config_schema_v3.6.json +++ b/cli/compose/schema/data/config_schema_v3.6.json @@ -122,10 +122,14 @@ } }, "container_name": {"type": "string"}, - "credential_spec": {"type": "object", "properties": { - "file": {"type": "string"}, - "registry": {"type": "string"} - }}, + "credential_spec": { + "type": "object", + "properties": { + "file": {"type": "string"}, + "registry": {"type": "string"} + }, + "additionalProperties": false + }, "depends_on": {"$ref": "#/definitions/list_of_strings"}, "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "dns": {"$ref": "#/definitions/string_or_list"}, diff --git a/cli/compose/schema/data/config_schema_v3.7.json b/cli/compose/schema/data/config_schema_v3.7.json index f1597df001e6..69d5c52f87f5 100644 --- a/cli/compose/schema/data/config_schema_v3.7.json +++ b/cli/compose/schema/data/config_schema_v3.7.json @@ -122,10 +122,14 @@ } }, "container_name": {"type": "string"}, - "credential_spec": {"type": "object", "properties": { - "file": {"type": "string"}, - "registry": {"type": "string"} - }}, + "credential_spec": { + "type": "object", + "properties": { + "file": {"type": "string"}, + "registry": {"type": "string"} + }, + "additionalProperties": false + }, "depends_on": {"$ref": "#/definitions/list_of_strings"}, "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "dns": {"$ref": "#/definitions/string_or_list"}, diff --git a/cli/compose/schema/schema_test.go b/cli/compose/schema/schema_test.go index 6fa027e4956d..bb15098b2835 100644 --- a/cli/compose/schema/schema_test.go +++ b/cli/compose/schema/schema_test.go @@ -1,6 +1,7 @@ package schema import ( + "fmt" "testing" "gotest.tools/assert" @@ -78,6 +79,46 @@ func TestValidateAllowsXFields(t *testing.T) { assert.NilError(t, err) } +func TestValidateCredentialSpecs(t *testing.T) { + config := dict{ + "version": "99.99", + "services": dict{ + "foo": dict{ + "image": "busybox", + "credential_spec": dict{ + "config": "foobar", + }, + }, + }, + } + tests := []struct { + version string + expectedErr string + }{ + {version: "3.0", expectedErr: "credential_spec"}, + {version: "3.1", expectedErr: "credential_spec"}, + {version: "3.2", expectedErr: "credential_spec"}, + {version: "3.3", expectedErr: "config"}, + {version: "3.4", expectedErr: "config"}, + {version: "3.5", expectedErr: "config"}, + {version: "3.6", expectedErr: "config"}, + {version: "3.7", expectedErr: "config"}, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.version, func(t *testing.T) { + err := Validate(config, tc.version) + if tc.expectedErr != "" { + assert.ErrorContains(t, err, fmt.Sprintf("Additional property %s is not allowed", tc.expectedErr)) + } else { + assert.NilError(t, err) + } + }) + } + +} + func TestValidateSecretConfigNames(t *testing.T) { config := dict{ "version": "3.5", From 700cceca4c7f56364d573a1f938938a3bbc089bb Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 4 Feb 2019 08:25:01 +0100 Subject: [PATCH 56/60] Update authors Signed-off-by: Sebastiaan van Stijn --- .mailmap | 1 + AUTHORS | 3 +++ 2 files changed, 4 insertions(+) diff --git a/.mailmap b/.mailmap index 553af0a1e9bb..49eda14ee8d8 100644 --- a/.mailmap +++ b/.mailmap @@ -492,5 +492,6 @@ Zachary Jaffee ZhangHang Zhenkun Bi Zhou Hao +Zhoulin Xie Zhu Kunjia Zou Yu diff --git a/AUTHORS b/AUTHORS index e29faa19f4c3..84e25433a548 100644 --- a/AUTHORS +++ b/AUTHORS @@ -205,9 +205,11 @@ Fabio Falci Fabrizio Soppelsa Felix Hupfeld Felix Rabe +Filip Jareš Flavio Crisciani Florian Klein Foysal Iqbal +François Scala Fred Lifton Frederic Hemberger Frederick F. Kautz IV @@ -683,6 +685,7 @@ Zhang Wentao ZhangHang zhenghenghuo Zhou Hao +Zhoulin Xie Zhu Guihua Álex González Álvaro Lázaro From 593acf077b859430d1a3f95ca0e172c88bb8c9bc Mon Sep 17 00:00:00 2001 From: John Howard Date: Mon, 7 Jan 2019 15:34:33 -0800 Subject: [PATCH 57/60] Add --device support for Windows Adds support for --device in Windows. This must take the form of: --device='class/clsid'. See this post for more information: https://blogs.technet.microsoft.com/virtualization/2018/08/13/bringing-device-support-to-windows-server-containers/ Signed-off-by: John Howard --- cli/command/container/create.go | 2 +- cli/command/container/opts.go | 59 +++++++++++++++++++++++++----- cli/command/container/opts_test.go | 11 ++++-- cli/command/container/run.go | 2 +- 4 files changed, 58 insertions(+), 16 deletions(-) diff --git a/cli/command/container/create.go b/cli/command/container/create.go index 49a3d4356cf8..868e4fb4baaf 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -72,7 +72,7 @@ func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, options *createOptio } } copts.env = *opts.NewListOptsRef(&newEnv, nil) - containerConfig, err := parse(flags, copts) + containerConfig, err := parse(flags, copts, dockerCli.ServerInfo().OSType) if err != nil { reportError(dockerCli.Err(), "create", err.Error(), true) return cli.StatusError{StatusCode: 125} diff --git a/cli/command/container/opts.go b/cli/command/container/opts.go index bce5c231e0c7..4546839629d0 100644 --- a/cli/command/container/opts.go +++ b/cli/command/container/opts.go @@ -141,7 +141,7 @@ func addFlags(flags *pflag.FlagSet) *containerOptions { deviceReadIOps: opts.NewThrottledeviceOpt(opts.ValidateThrottleIOpsDevice), deviceWriteBps: opts.NewThrottledeviceOpt(opts.ValidateThrottleBpsDevice), deviceWriteIOps: opts.NewThrottledeviceOpt(opts.ValidateThrottleIOpsDevice), - devices: opts.NewListOpts(validateDevice), + devices: opts.NewListOpts(nil), // devices can only be validated after we know the server OS env: opts.NewListOpts(opts.ValidateEnv), envFile: opts.NewListOpts(nil), expose: opts.NewListOpts(nil), @@ -299,7 +299,7 @@ type containerConfig struct { // a HostConfig and returns them with the specified command. // If the specified args are not valid, it will return an error. // nolint: gocyclo -func parse(flags *pflag.FlagSet, copts *containerOptions) (*containerConfig, error) { +func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*containerConfig, error) { var ( attachStdin = copts.attach.Get("stdin") attachStdout = copts.attach.Get("stdout") @@ -417,10 +417,22 @@ func parse(flags *pflag.FlagSet, copts *containerOptions) (*containerConfig, err } } - // parse device mappings + // validate and parse device mappings. Note we do late validation of the + // device path (as opposed to during flag parsing), as at the time we are + // parsing flags, we haven't yet sent a _ping to the daemon to determine + // what operating system it is. deviceMappings := []container.DeviceMapping{} for _, device := range copts.devices.GetAll() { - deviceMapping, err := parseDevice(device) + var ( + validated string + deviceMapping container.DeviceMapping + err error + ) + validated, err = validateDevice(device, serverOS) + if err != nil { + return nil, err + } + deviceMapping, err = parseDevice(validated, serverOS) if err != nil { return nil, err } @@ -747,7 +759,19 @@ func parseStorageOpts(storageOpts []string) (map[string]string, error) { } // parseDevice parses a device mapping string to a container.DeviceMapping struct -func parseDevice(device string) (container.DeviceMapping, error) { +func parseDevice(device, serverOS string) (container.DeviceMapping, error) { + switch serverOS { + case "linux": + return parseLinuxDevice(device) + case "windows": + return parseWindowsDevice(device) + } + return container.DeviceMapping{}, errors.Errorf("unknown server OS: %s", serverOS) +} + +// parseLinuxDevice parses a device mapping string to a container.DeviceMapping struct +// knowing that the target is a Linux daemon +func parseLinuxDevice(device string) (container.DeviceMapping, error) { src := "" dst := "" permissions := "rwm" @@ -781,6 +805,12 @@ func parseDevice(device string) (container.DeviceMapping, error) { return deviceMapping, nil } +// parseWindowsDevice parses a device mapping string to a container.DeviceMapping struct +// knowing that the target is a Windows daemon +func parseWindowsDevice(device string) (container.DeviceMapping, error) { + return container.DeviceMapping{PathOnHost: device}, nil +} + // validateDeviceCgroupRule validates a device cgroup rule string format // It will make sure 'val' is in the form: // 'type major:minor mode' @@ -813,14 +843,23 @@ func validDeviceMode(mode string) bool { } // validateDevice validates a path for devices +func validateDevice(val string, serverOS string) (string, error) { + switch serverOS { + case "linux": + return validateLinuxPath(val, validDeviceMode) + case "windows": + // Windows does validation entirely server-side + return val, nil + } + return "", errors.Errorf("unknown server OS: %s", serverOS) +} + +// validateLinuxPath is the implementation of validateDevice knowing that the +// target server operating system is a Linux daemon. // It will make sure 'val' is in the form: // [host-dir:]container-path[:mode] // It also validates the device mode. -func validateDevice(val string) (string, error) { - return validatePath(val, validDeviceMode) -} - -func validatePath(val string, validator func(string) bool) (string, error) { +func validateLinuxPath(val string, validator func(string) bool) (string, error) { var containerPath string var mode string diff --git a/cli/command/container/opts_test.go b/cli/command/container/opts_test.go index 70bedc661751..3a6aa5857054 100644 --- a/cli/command/container/opts_test.go +++ b/cli/command/container/opts_test.go @@ -16,6 +16,7 @@ import ( "github.com/spf13/pflag" "gotest.tools/assert" is "gotest.tools/assert/cmp" + "gotest.tools/skip" ) func TestValidateAttach(t *testing.T) { @@ -48,7 +49,7 @@ func parseRun(args []string) (*container.Config, *container.HostConfig, *network return nil, nil, nil, err } // TODO: fix tests to accept ContainerConfig - containerConfig, err := parse(flags, copts) + containerConfig, err := parse(flags, copts, runtime.GOOS) if err != nil { return nil, nil, nil, err } @@ -351,6 +352,7 @@ func TestParseWithExpose(t *testing.T) { } func TestParseDevice(t *testing.T) { + skip.If(t, runtime.GOOS == "windows") // Windows validates server-side valids := map[string]container.DeviceMapping{ "/dev/snd": { PathOnHost: "/dev/snd", @@ -393,7 +395,7 @@ func TestParseModes(t *testing.T) { flags, copts := setupRunFlags() args := []string{"--pid=container:", "img", "cmd"} assert.NilError(t, flags.Parse(args)) - _, err := parse(flags, copts) + _, err := parse(flags, copts, runtime.GOOS) assert.ErrorContains(t, err, "--pid: invalid PID mode") // pid ok @@ -615,6 +617,7 @@ func TestParseEntryPoint(t *testing.T) { } func TestValidateDevice(t *testing.T) { + skip.If(t, runtime.GOOS == "windows") // Windows validates server-side valid := []string{ "/home", "/home:/home", @@ -649,13 +652,13 @@ func TestValidateDevice(t *testing.T) { } for _, path := range valid { - if _, err := validateDevice(path); err != nil { + if _, err := validateDevice(path, runtime.GOOS); err != nil { t.Fatalf("ValidateDevice(`%q`) should succeed: error %q", path, err) } } for path, expectedError := range invalid { - if _, err := validateDevice(path); err == nil { + if _, err := validateDevice(path, runtime.GOOS); err == nil { t.Fatalf("ValidateDevice(`%q`) should have failed validation", path) } else { if err.Error() != expectedError { diff --git a/cli/command/container/run.go b/cli/command/container/run.go index e42e8115e177..2ce3f3679c59 100644 --- a/cli/command/container/run.go +++ b/cli/command/container/run.go @@ -78,7 +78,7 @@ func runRun(dockerCli command.Cli, flags *pflag.FlagSet, ropts *runOptions, copt } } copts.env = *opts.NewListOptsRef(&newEnv, nil) - containerConfig, err := parse(flags, copts) + containerConfig, err := parse(flags, copts, dockerCli.ServerInfo().OSType) // just in case the parse does not exit if err != nil { reportError(dockerCli.Err(), "run", err.Error(), true) From 7632776b35ceeed1a86be76b5d9d4e9e4997598a Mon Sep 17 00:00:00 2001 From: Philipp Schmied Date: Thu, 7 Feb 2019 09:17:35 +0100 Subject: [PATCH 58/60] Prevent overwriting irregular files (cp, save, export commands) Signed-off-by: Philipp Schmied --- cli/command/container/cp.go | 9 +++++++ cli/command/container/cp_test.go | 9 +++++++ cli/command/container/export.go | 4 ++++ cli/command/container/export_test.go | 16 +++++++++++++ cli/command/image/save.go | 14 +---------- cli/command/image/save_test.go | 7 +++++- cli/command/utils.go | 35 ++++++++++++++++++++++++++++ 7 files changed, 80 insertions(+), 14 deletions(-) diff --git a/cli/command/container/cp.go b/cli/command/container/cp.go index ffb9a211c2d1..20809bc36206 100644 --- a/cli/command/container/cp.go +++ b/cli/command/container/cp.go @@ -128,6 +128,10 @@ func copyFromContainer(ctx context.Context, dockerCli command.Cli, copyConfig cp } } + if err := command.ValidateOutputPath(dstPath); err != nil { + return err + } + client := dockerCli.Client() // if client requests to follow symbol link, then must decide target file to be copied var rebaseName string @@ -209,6 +213,11 @@ func copyToContainer(ctx context.Context, dockerCli command.Cli, copyConfig cpCo dstStat, err = client.ContainerStatPath(ctx, copyConfig.container, linkTarget) } + // Validate the destination path + if err := command.ValidateOutputPathFileMode(dstStat.Mode); err != nil { + return errors.Wrapf(err, `destination "%s:%s" must be a directory or a regular file`, copyConfig.container, dstPath) + } + // Ignore any error and assume that the parent directory of the destination // path exists, in which case the copy may still succeed. If there is any // type of conflict (e.g., non-directory overwriting an existing directory diff --git a/cli/command/container/cp_test.go b/cli/command/container/cp_test.go index 67cdaf15a9bf..f08b7ee52035 100644 --- a/cli/command/container/cp_test.go +++ b/cli/command/container/cp_test.go @@ -190,3 +190,12 @@ func TestSplitCpArg(t *testing.T) { }) } } + +func TestRunCopyFromContainerToFilesystemIrregularDestination(t *testing.T) { + options := copyOptions{source: "container:/dev/null", destination: "/dev/random"} + cli := test.NewFakeCli(nil) + err := runCopy(cli, options) + assert.Assert(t, err != nil) + expected := `"/dev/random" must be a directory or a regular file` + assert.ErrorContains(t, err, expected) +} diff --git a/cli/command/container/export.go b/cli/command/container/export.go index f0f67373d7a2..ee77cdb55de4 100644 --- a/cli/command/container/export.go +++ b/cli/command/container/export.go @@ -41,6 +41,10 @@ func runExport(dockerCli command.Cli, opts exportOptions) error { return errors.New("cowardly refusing to save to a terminal. Use the -o flag or redirect") } + if err := command.ValidateOutputPath(opts.output); err != nil { + return errors.Wrap(err, "failed to export container") + } + clnt := dockerCli.Client() responseBody, err := clnt.ContainerExport(context.Background(), opts.container) diff --git a/cli/command/container/export_test.go b/cli/command/container/export_test.go index b961ec763000..340d55e10e59 100644 --- a/cli/command/container/export_test.go +++ b/cli/command/container/export_test.go @@ -31,3 +31,19 @@ func TestContainerExportOutputToFile(t *testing.T) { assert.Assert(t, fs.Equal(dir.Path(), expected)) } + +func TestContainerExportOutputToIrregularFile(t *testing.T) { + cli := test.NewFakeCli(&fakeClient{ + containerExportFunc: func(container string) (io.ReadCloser, error) { + return ioutil.NopCloser(strings.NewReader("foo")), nil + }, + }) + cmd := NewExportCommand(cli) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs([]string{"-o", "/dev/random", "container"}) + + err := cmd.Execute() + assert.Assert(t, err != nil) + expected := `"/dev/random" must be a directory or a regular file` + assert.ErrorContains(t, err, expected) +} diff --git a/cli/command/image/save.go b/cli/command/image/save.go index ef23ca1bb118..218a9a7974ba 100644 --- a/cli/command/image/save.go +++ b/cli/command/image/save.go @@ -3,8 +3,6 @@ package image import ( "context" "io" - "os" - "path/filepath" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" @@ -44,7 +42,7 @@ func RunSave(dockerCli command.Cli, opts saveOptions) error { return errors.New("cowardly refusing to save to a terminal. Use the -o flag or redirect") } - if err := validateOutputPath(opts.output); err != nil { + if err := command.ValidateOutputPath(opts.output); err != nil { return errors.Wrap(err, "failed to save image") } @@ -61,13 +59,3 @@ func RunSave(dockerCli command.Cli, opts saveOptions) error { return command.CopyToFile(opts.output, responseBody) } - -func validateOutputPath(path string) error { - dir := filepath.Dir(path) - if dir != "" && dir != "." { - if _, err := os.Stat(dir); os.IsNotExist(err) { - return errors.Errorf("unable to validate output path: directory %q does not exist", dir) - } - } - return nil -} diff --git a/cli/command/image/save_test.go b/cli/command/image/save_test.go index d051e8cb5c31..cc960ba0e00c 100644 --- a/cli/command/image/save_test.go +++ b/cli/command/image/save_test.go @@ -44,7 +44,12 @@ func TestNewSaveCommandErrors(t *testing.T) { { name: "output directory does not exist", args: []string{"-o", "fakedir/out.tar", "arg1"}, - expectedError: "failed to save image: unable to validate output path: directory \"fakedir\" does not exist", + expectedError: "failed to save image: invalid output path: directory \"fakedir\" does not exist", + }, + { + name: "output file is irregular", + args: []string{"-o", "/dev/null", "arg1"}, + expectedError: "failed to save image: invalid output path: \"/dev/null\" must be a directory or a regular file", }, } for _, tc := range testCases { diff --git a/cli/command/utils.go b/cli/command/utils.go index 13954c010145..2dcc8a2df20f 100644 --- a/cli/command/utils.go +++ b/cli/command/utils.go @@ -11,6 +11,7 @@ import ( "github.com/docker/docker/api/types/filters" "github.com/docker/docker/pkg/system" + "github.com/pkg/errors" "github.com/spf13/pflag" ) @@ -125,3 +126,37 @@ func AddPlatformFlag(flags *pflag.FlagSet, target *string) { flags.SetAnnotation("platform", "version", []string{"1.32"}) flags.SetAnnotation("platform", "experimental", nil) } + +// ValidateOutputPath validates the output paths of the `export` and `save` commands. +func ValidateOutputPath(path string) error { + dir := filepath.Dir(path) + if dir != "" && dir != "." { + if _, err := os.Stat(dir); os.IsNotExist(err) { + return errors.Errorf("invalid output path: directory %q does not exist", dir) + } + } + // check whether `path` points to a regular file + // (if the path exists and doesn't point to a directory) + if fileInfo, err := os.Stat(path); !os.IsNotExist(err) { + if fileInfo.Mode().IsDir() || fileInfo.Mode().IsRegular() { + return nil + } + + if err := ValidateOutputPathFileMode(fileInfo.Mode()); err != nil { + return errors.Wrapf(err, fmt.Sprintf("invalid output path: %q must be a directory or a regular file", path)) + } + } + return nil +} + +// ValidateOutputPathFileMode validates the output paths of the `cp` command and serves as a +// helper to `ValidateOutputPath` +func ValidateOutputPathFileMode(fileMode os.FileMode) error { + switch { + case fileMode&os.ModeDevice != 0: + return errors.New("got a device") + case fileMode&os.ModeIrregular != 0: + return errors.New("got an irregular file") + } + return nil +} From 0e469c1d1d85a134ce2c3233231cad2379af83c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20K=C3=A4ufl?= Date: Thu, 7 Feb 2019 13:23:13 +0100 Subject: [PATCH 59/60] Fix typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michael Käufl --- docs/reference/commandline/build.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/commandline/build.md b/docs/reference/commandline/build.md index a2f4763f8479..21220c631d86 100644 --- a/docs/reference/commandline/build.md +++ b/docs/reference/commandline/build.md @@ -504,13 +504,13 @@ stable. Squashing layers can be beneficial if your Dockerfile produces multiple layers -modifying the same files, for example, file that are created in one step, and +modifying the same files, for example, files that are created in one step, and removed in another step. For other use-cases, squashing images may actually have a negative impact on performance; when pulling an image consisting of multiple layers, layers can be pulled in parallel, and allows sharing layers between images (saving space). -For most use cases, multi-stage are a better alternative, as they give more +For most use cases, multi-stage builds are a better alternative, as they give more fine-grained control over your build, and can take advantage of future optimizations in the builder. Refer to the [use multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) section in the userguide for more information. @@ -531,7 +531,7 @@ The `--squash` option has a number of known limitations: downloading a single layer cannot be parallelized. - When attempting to squash an image that does not make changes to the filesystem (for example, the Dockerfile only contains `ENV` instructions), - the squash step will fail (see [issue #33823](https://github.com/moby/moby/issues/33823) + the squash step will fail (see [issue #33823](https://github.com/moby/moby/issues/33823)). #### Prerequisites From 0b49495b1d361efa72e3ef35ff000c863966bfe1 Mon Sep 17 00:00:00 2001 From: Matteo Orefice Date: Sun, 10 Feb 2019 14:04:45 +0000 Subject: [PATCH 60/60] Prevent bash process substitution error in cygwin Signed-off-by: Matteo Orefice --- contrib/completion/bash/docker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/completion/bash/docker b/contrib/completion/bash/docker index b40fc4141da2..970feba6fa16 100644 --- a/contrib/completion/bash/docker +++ b/contrib/completion/bash/docker @@ -593,7 +593,7 @@ __docker_append_to_completions() { # The result is cached for the duration of one invocation of bash completion. __docker_fetch_info() { if [ -z "$info_fetched" ] ; then - read -r client_experimental server_experimental server_os < <(__docker_q version -f '{{.Client.Experimental}} {{.Server.Experimental}} {{.Server.Os}}') + read -r client_experimental server_experimental server_os <<< "$(__docker_q version -f '{{.Client.Experimental}} {{.Server.Experimental}} {{.Server.Os}}')" info_fetched=true fi }