Merge pull request #28774 from vieux/1.13.0-rc3-cherrypicks

1.13.0-rc3 cherry-picks
This commit is contained in:
Vincent Demeester 2016-11-30 07:36:53 +01:00 committed by GitHub
commit 005a5428ee
84 changed files with 871 additions and 300 deletions

View file

@ -92,7 +92,7 @@ be found.
+ Add `--format` on `docker stats` [#24987](https://github.com/docker/docker/pull/24987)
+ Make `docker node ps` default to `self` in swarm node [#25214](https://github.com/docker/docker/pull/25214)
+ Add `--group` in `docker service create` [#25317](https://github.com/docker/docker/pull/25317)
+ Add `--no-trunc` to service/node/stack ps output [#25337(https://github.com/docker/docker/pull/25337)
+ Add `--no-trunc` to service/node/stack ps output [#25337](https://github.com/docker/docker/pull/25337)
+ Add Logs to `ContainerAttachOptions` so go clients can request to retrieve container logs as part of the attach process [#26718](https://github.com/docker/docker/pull/26718)
+ Allow client to talk to an older server [#27745](https://github.com/docker/docker/pull/27745)
* Inform user client-side that a container removal is in progress [#26074](https://github.com/docker/docker/pull/26074)

View file

@ -172,10 +172,10 @@ func (n *networkRouter) buildNetworkResource(nw libnetwork.Network) *types.Netwo
r.Driver = nw.Type()
r.EnableIPv6 = info.IPv6Enabled()
r.Internal = info.Internal()
r.Attachable = info.Attachable()
r.Options = info.DriverOptions()
r.Containers = make(map[string]types.EndpointResource)
buildIpamResources(r, info)
r.Internal = info.Internal()
r.Labels = info.Labels()
peers := info.Peers()

View file

@ -144,6 +144,9 @@ func (filters Args) Add(name, value string) {
func (filters Args) Del(name, value string) {
if _, ok := filters.fields[name]; ok {
delete(filters.fields[name], value)
if len(filters.fields[name]) == 0 {
delete(filters.fields, name)
}
}
}

View file

@ -129,6 +129,8 @@ type Backend interface {
ContainerWait(containerID string, timeout time.Duration) (int, error)
// ContainerUpdateCmdOnBuild updates container.Path and container.Args
ContainerUpdateCmdOnBuild(containerID string, cmd []string) error
// ContainerCreateWorkdir creates the workdir (currently only used on Windows)
ContainerCreateWorkdir(containerID string) error
// ContainerCopy copies/extracts a source FileInfo to a destination path inside a container
// specified by a container object.

View file

@ -18,6 +18,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/strslice"
"github.com/docker/docker/builder"
@ -279,12 +280,37 @@ func workdir(b *Builder, args []string, attributes map[string]bool, original str
return err
}
// NOTE: You won't find the "mkdir" for the directory in here. Rather we
// just set the value in the image's runConfig.WorkingDir property
// and container.SetupWorkingDirectory() will create it automatically
// for us the next time the image is used to create a container.
// For performance reasons, we explicitly do a create/mkdir now
// This avoids having an unnecessary expensive mount/unmount calls
// (on Windows in particular) during each container create.
// Prior to 1.13, the mkdir was deferred and not executed at this step.
if b.disableCommit {
// Don't call back into the daemon if we're going through docker commit --change "WORKDIR /foo".
// We've already updated the runConfig and that's enough.
return nil
}
b.runConfig.Image = b.image
return b.commit("", b.runConfig.Cmd, fmt.Sprintf("WORKDIR %v", b.runConfig.WorkingDir))
cmd := b.runConfig.Cmd
b.runConfig.Cmd = strslice.StrSlice(append(getShell(b.runConfig), fmt.Sprintf("#(nop) WORKDIR %s", b.runConfig.WorkingDir)))
defer func(cmd strslice.StrSlice) { b.runConfig.Cmd = cmd }(cmd)
if hit, err := b.probeCache(); err != nil {
return err
} else if hit {
return nil
}
container, err := b.docker.ContainerCreate(types.ContainerCreateConfig{Config: b.runConfig}, true)
if err != nil {
return err
}
b.tmpContainers[container.ID] = struct{}{}
if err := b.docker.ContainerCreateWorkdir(container.ID); err != nil {
return err
}
return b.commit(container.ID, b.runConfig.Cmd, "WORKDIR "+b.runConfig.WorkingDir)
}
// RUN some command yo

View file

@ -2,6 +2,7 @@ package cli
import (
"fmt"
"strings"
"github.com/spf13/cobra"
)
@ -17,6 +18,7 @@ func SetupRootCommand(rootCmd *cobra.Command) {
rootCmd.SetUsageTemplate(usageTemplate)
rootCmd.SetHelpTemplate(helpTemplate)
rootCmd.SetFlagErrorFunc(FlagErrorFunc)
rootCmd.SetHelpCommand(helpCommand)
rootCmd.PersistentFlags().BoolP("help", "h", false, "Print usage")
rootCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help")
@ -39,6 +41,23 @@ func FlagErrorFunc(cmd *cobra.Command, err error) error {
}
}
var helpCommand = &cobra.Command{
Use: "help [command]",
Short: "Help about the command",
PersistentPreRun: func(cmd *cobra.Command, args []string) {},
PersistentPostRun: func(cmd *cobra.Command, args []string) {},
RunE: func(c *cobra.Command, args []string) error {
cmd, args, e := c.Root().Find(args)
if cmd == nil || e != nil || len(args) > 0 {
return fmt.Errorf("unknown help topic: %v", strings.Join(args, " "))
}
helpFunc := cmd.HelpFunc()
helpFunc(cmd, args)
return nil
},
}
func hasSubCommands(cmd *cobra.Command) bool {
return len(operationSubCommands(cmd)) > 0
}

View file

@ -43,7 +43,7 @@ func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display network IDs")
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate the output")
flags.StringVar(&opts.format, "format", "", "Pretty-print networks using a Go template")
flags.VarP(&opts.filter, "filter", "f", "Provide filter values (i.e. 'dangling=true')")
flags.VarP(&opts.filter, "filter", "f", "Provide filter values (e.g. 'driver=bridge')")
return cmd
}

View file

@ -64,7 +64,7 @@ func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
options := pluginCreateOptions{}
cmd := &cobra.Command{
Use: "create [OPTIONS] reponame[:tag] PATH-TO-ROOTFS (rootfs + config.json)",
Use: "create [OPTIONS] PLUGIN[:tag] PATH-TO-ROOTFS(rootfs + config.json)",
Short: "Create a plugin from a rootfs and config",
Args: cli.RequiresMinArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {

View file

@ -20,7 +20,7 @@ func newEnableCommand(dockerCli *command.DockerCli) *cobra.Command {
var opts enableOpts
cmd := &cobra.Command{
Use: "enable PLUGIN",
Use: "enable [OPTIONS] PLUGIN",
Short: "Enable a plugin",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {

View file

@ -14,7 +14,7 @@ import (
func newPushCommand(dockerCli *command.DockerCli) *cobra.Command {
cmd := &cobra.Command{
Use: "push NAME[:TAG]",
Use: "push PLUGIN[:TAG]",
Short: "Push a plugin to a registry",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {

View file

@ -25,7 +25,7 @@ func newSecretCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
}
cmd := &cobra.Command{
Use: "create [name]",
Use: "create [OPTIONS] SECRET [SECRET...]",
Short: "Create a secret using stdin as content",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {

View file

@ -16,8 +16,8 @@ type inspectOptions struct {
func newSecretInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
opts := inspectOptions{}
cmd := &cobra.Command{
Use: "inspect SECRET [SECRET]",
Short: "Inspect a secret",
Use: "inspect [OPTIONS] SECRET [SECRET...]",
Short: "Display detailed information on one or more secrets",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.names = args

View file

@ -21,7 +21,7 @@ func newSecretListCommand(dockerCli *command.DockerCli) *cobra.Command {
opts := listOptions{}
cmd := &cobra.Command{
Use: "ls",
Use: "ls [OPTIONS]",
Short: "List secrets",
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {

View file

@ -2,6 +2,7 @@ package secret
import (
"fmt"
"strings"
"github.com/docker/docker/cli"
"github.com/docker/docker/cli/command"
@ -15,8 +16,8 @@ type removeOptions struct {
func newSecretRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
return &cobra.Command{
Use: "rm SECRET [SECRET]",
Short: "Remove a secret",
Use: "rm SECRET [SECRET...]",
Short: "Remove one or more secrets",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts := removeOptions{
@ -36,13 +37,20 @@ func runSecretRemove(dockerCli *command.DockerCli, opts removeOptions) error {
return err
}
var errs []string
for _, id := range ids {
if err := client.SecretRemove(ctx, id); err != nil {
fmt.Fprintf(dockerCli.Out(), "WARN: %s\n", err)
errs = append(errs, err.Error())
continue
}
fmt.Fprintln(dockerCli.Out(), id)
}
if len(errs) > 0 {
return fmt.Errorf("%s", strings.Join(errs, "\n"))
}
return nil
}

View file

@ -33,7 +33,6 @@ func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
flags.VarP(&opts.labels, flagLabel, "l", "Service labels")
flags.Var(&opts.containerLabels, flagContainerLabel, "Container labels")
flags.StringVar(&opts.hostname, flagHostname, "", "Container hostname")
flags.VarP(&opts.env, flagEnv, "e", "Set environment variables")
flags.Var(&opts.envFile, flagEnvFile, "Read in a file of environment variables")
flags.Var(&opts.mounts, flagMount, "Attach a filesystem mount to the service")

View file

@ -182,7 +182,7 @@ func (o *SecretOpt) Set(value string) error {
value := parts[1]
switch key {
case "source":
case "source", "src":
spec.source = value
case "target":
tDir, _ := filepath.Split(value)
@ -570,6 +570,7 @@ func addServiceFlags(cmd *cobra.Command, opts *serviceOptions) {
flags.StringVarP(&opts.workdir, flagWorkdir, "w", "", "Working directory inside the container")
flags.StringVarP(&opts.user, flagUser, "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
flags.StringVar(&opts.hostname, flagHostname, "", "Container hostname")
flags.Var(&opts.resources.limitCPU, flagLimitCPU, "Limit CPUs")
flags.Var(&opts.resources.limitMemBytes, flagLimitMemory, "Limit Memory")

View file

@ -208,6 +208,7 @@ func updateService(flags *pflag.FlagSet, spec *swarm.ServiceSpec) error {
updateEnvironment(flags, &cspec.Env)
updateString(flagWorkdir, &cspec.Dir)
updateString(flagUser, &cspec.User)
updateString(flagHostname, &cspec.Hostname)
if err := updateMounts(flags, &cspec.Mounts); err != nil {
return err
}

View file

@ -1,6 +1,7 @@
package stack
import (
"errors"
"fmt"
"io/ioutil"
"os"
@ -59,19 +60,36 @@ func newDeployCommand(dockerCli *command.DockerCli) *cobra.Command {
}
func runDeploy(dockerCli *command.DockerCli, opts deployOptions) error {
ctx := context.Background()
switch {
case opts.bundlefile == "" && opts.composefile == "":
return fmt.Errorf("Please specify either a bundle file (with --bundle-file) or a Compose file (with --compose-file).")
case opts.bundlefile != "" && opts.composefile != "":
return fmt.Errorf("You cannot specify both a bundle file and a Compose file.")
case opts.bundlefile != "":
return deployBundle(dockerCli, opts)
return deployBundle(ctx, dockerCli, opts)
default:
return deployCompose(dockerCli, opts)
return deployCompose(ctx, dockerCli, opts)
}
}
func deployCompose(dockerCli *command.DockerCli, opts deployOptions) error {
// checkDaemonIsSwarmManager does an Info API call to verify that the daemon is
// a swarm manager. This is necessary because we must create networks before we
// create services, but the API call for creating a network does not return a
// proper status code when it can't create a network in the "global" scope.
func checkDaemonIsSwarmManager(ctx context.Context, dockerCli *command.DockerCli) error {
info, err := dockerCli.Client().Info(ctx)
if err != nil {
return err
}
if !info.Swarm.ControlAvailable {
return errors.New("This node is not a swarm manager. Use \"docker swarm init\" or \"docker swarm join\" to connect this node to swarm and try again.")
}
return nil
}
func deployCompose(ctx context.Context, dockerCli *command.DockerCli, opts deployOptions) error {
configDetails, err := getConfigDetails(opts)
if err != nil {
return err
@ -99,7 +117,10 @@ func deployCompose(dockerCli *command.DockerCli, opts deployOptions) error {
propertyWarnings(deprecatedProperties))
}
ctx := context.Background()
if err := checkDaemonIsSwarmManager(ctx, dockerCli); err != nil {
return err
}
namespace := namespace{name: opts.namespace}
networks := convertNetworks(namespace, config.Networks)
@ -326,7 +347,7 @@ func convertVolumeToMount(
source = stackVolume.External.Name
} else {
volumeOptions = &mount.VolumeOptions{
Labels: stackVolume.Labels,
Labels: getStackLabels(namespace.name, stackVolume.Labels),
NoCopy: isNoCopy(mode),
}

View file

@ -8,12 +8,16 @@ import (
"github.com/docker/docker/cli/command"
)
func deployBundle(dockerCli *command.DockerCli, opts deployOptions) error {
func deployBundle(ctx context.Context, dockerCli *command.DockerCli, opts deployOptions) error {
bundle, err := loadBundlefile(dockerCli.Err(), opts.namespace, opts.bundlefile)
if err != nil {
return err
}
if err := checkDaemonIsSwarmManager(ctx, dockerCli); err != nil {
return err
}
namespace := namespace{name: opts.namespace}
networks := make(map[string]types.NetworkCreate)
@ -71,8 +75,6 @@ func deployBundle(dockerCli *command.DockerCli, opts deployOptions) error {
services[internalName] = serviceSpec
}
ctx := context.Background()
if err := createNetworks(ctx, dockerCli, namespace, networks); err != nil {
return err
}

View file

@ -10,7 +10,7 @@ import (
)
func addComposefileFlag(opt *string, flags *pflag.FlagSet) {
flags.StringVar(opt, "compose-file", "", "Path to a Compose file")
flags.StringVarP(opt, "compose-file", "c", "", "Path to a Compose file")
}
func addBundlefileFlag(opt *string, flags *pflag.FlagSet) {

View file

@ -171,8 +171,8 @@ func parseExternalCA(caSpec string) (*swarm.ExternalCA, error) {
func addSwarmFlags(flags *pflag.FlagSet, opts *swarmOptions) {
flags.Int64Var(&opts.taskHistoryLimit, flagTaskHistoryLimit, 5, "Task history retention limit")
flags.DurationVar(&opts.dispatcherHeartbeat, flagDispatcherHeartbeat, time.Duration(5*time.Second), "Dispatcher heartbeat period (ns|us|ms|s|m|h) (default 5s)")
flags.DurationVar(&opts.nodeCertExpiry, flagCertExpiry, time.Duration(90*24*time.Hour), "Validity period for node certificates (ns|us|ms|s|m|h) (default 2160h0m0s)")
flags.DurationVar(&opts.dispatcherHeartbeat, flagDispatcherHeartbeat, time.Duration(5*time.Second), "Dispatcher heartbeat period (ns|us|ms|s|m|h)")
flags.DurationVar(&opts.nodeCertExpiry, flagCertExpiry, time.Duration(90*24*time.Hour), "Validity period for node certificates (ns|us|ms|s|m|h)")
flags.Var(&opts.externalCA, flagExternalCA, "Specifications of one or more certificate signing endpoints")
flags.Uint64Var(&opts.maxSnapshots, flagMaxSnapshots, 0, "Number of additional Raft snapshots to retain")
flags.Uint64Var(&opts.snapshotInterval, flagSnapshotInterval, 10000, "Number of log entries between Raft snapshots")

View file

@ -24,14 +24,13 @@ var versionTemplate = `Client:
OS/Arch: {{.Client.Os}}/{{.Client.Arch}}{{if .ServerOK}}
Server:
Version: {{.Server.Version}}
API version: {{.Server.APIVersion}}
Minimum API version: {{.Server.MinAPIVersion}}
Go version: {{.Server.GoVersion}}
Git commit: {{.Server.GitCommit}}
Built: {{.Server.BuildTime}}
OS/Arch: {{.Server.Os}}/{{.Server.Arch}}
Experimental: {{.Server.Experimental}}{{end}}`
Version: {{.Server.Version}}
API version: {{.Server.APIVersion}} (minimum version {{.Server.MinAPIVersion}})
Go version: {{.Server.GoVersion}}
Git commit: {{.Server.GitCommit}}
Built: {{.Server.BuildTime}}
OS/Arch: {{.Server.Os}}/{{.Server.Arch}}
Experimental: {{.Server.Experimental}}{{end}}`
type versionOptions struct {
format string

View file

@ -6,6 +6,7 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/versions"
"golang.org/x/net/context"
)
@ -14,8 +15,16 @@ func (cli *Client) ImageList(ctx context.Context, options types.ImageListOptions
var images []types.ImageSummary
query := url.Values{}
if options.Filters.Len() > 0 {
filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters)
optionFilters := options.Filters
referenceFilters := optionFilters.Get("reference")
if versions.LessThan(cli.version, "1.25") && len(referenceFilters) > 0 {
query.Set("filter", referenceFilters[0])
for _, filterValue := range referenceFilters {
optionFilters.Del("reference", filterValue)
}
}
if optionFilters.Len() > 0 {
filterJSON, err := filters.ToParamWithVersion(cli.version, optionFilters)
if err != nil {
return images, err
}

View file

@ -109,3 +109,51 @@ func TestImageList(t *testing.T) {
}
}
}
func TestImageListApiBefore125(t *testing.T) {
expectedFilter := "image:tag"
client := &Client{
client: newMockClient(func(req *http.Request) (*http.Response, error) {
query := req.URL.Query()
actualFilter := query.Get("filter")
if actualFilter != expectedFilter {
return nil, fmt.Errorf("filter not set in URL query properly. Expected '%s', got %s", expectedFilter, actualFilter)
}
actualFilters := query.Get("filters")
if actualFilters != "" {
return nil, fmt.Errorf("filters should have not been present, were with value: %s", actualFilters)
}
content, err := json.Marshal([]types.ImageSummary{
{
ID: "image_id2",
},
{
ID: "image_id2",
},
})
if err != nil {
return nil, err
}
return &http.Response{
StatusCode: http.StatusOK,
Body: ioutil.NopCloser(bytes.NewReader(content)),
}, nil
}),
version: "1.24",
}
filters := filters.NewArgs()
filters.Add("reference", "image:tag")
options := types.ImageListOptions{
Filters: filters,
}
images, err := client.ImageList(context.Background(), options)
if err != nil {
t.Fatal(err)
}
if len(images) != 2 {
t.Fatalf("expected 2 images, got %v", images)
}
}

View file

@ -1,13 +1,14 @@
package main
import (
"io/ioutil"
"os"
"testing"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/utils"
"github.com/docker/docker/cli/command"
"github.com/docker/docker/pkg/testutil/assert"
"github.com/docker/docker/utils"
)
func TestClientDebugEnabled(t *testing.T) {
@ -16,14 +17,16 @@ func TestClientDebugEnabled(t *testing.T) {
cmd := newDockerCommand(&command.DockerCli{})
cmd.Flags().Set("debug", "true")
if err := cmd.PersistentPreRunE(cmd, []string{}); err != nil {
t.Fatalf("Unexpected error: %s", err.Error())
}
if os.Getenv("DEBUG") != "1" {
t.Fatal("expected debug enabled, got false")
}
if logrus.GetLevel() != logrus.DebugLevel {
t.Fatalf("expected logrus debug level, got %v", logrus.GetLevel())
}
err := cmd.PersistentPreRunE(cmd, []string{})
assert.NilError(t, err)
assert.Equal(t, os.Getenv("DEBUG"), "1")
assert.Equal(t, logrus.GetLevel(), logrus.DebugLevel)
}
func TestExitStatusForInvalidSubcommandWithHelpFlag(t *testing.T) {
discard := ioutil.Discard
cmd := newDockerCommand(command.NewDockerCli(os.Stdin, discard, discard))
cmd.SetArgs([]string{"help", "invalid"})
err := cmd.Execute()
assert.Error(t, err, "unknown help topic: invalid")
}

View file

@ -222,6 +222,23 @@ echo 'Optional Features:'
echo " $(wrap_color '(note that cgroup swap accounting is not enabled in your kernel config, you can enable it by setting boot option "swapaccount=1")' bold black)"
fi
}
{
if is_set LEGACY_VSYSCALL_NATIVE; then
echo -n "- "; wrap_good "CONFIG_LEGACY_VSYSCALL_NATIVE" 'enabled'
elif is_set LEGACY_VSYSCALL_EMULATE; then
echo -n "- "; wrap_good "CONFIG_LEGACY_VSYSCALL_EMULATE" 'enabled'
elif is_set LEGACY_VSYSCALL_NONE; then
echo -n "- "; wrap_bad "CONFIG_LEGACY_VSYSCALL_NONE" 'enabled'
echo " $(wrap_color '(containers using eglibc <= 2.13 will not work. Switch to' bold black)"
echo " $(wrap_color ' "CONFIG_VSYSCALL_[NATIVE|EMULATE]" or use "vsyscall=[native|emulate]"' bold black)"
echo " $(wrap_color ' on kernel command line. Note that this will disable ASLR for the,' bold black)"
echo " $(wrap_color ' VDSO which may assist in exploiting security vulnerabilities.)' bold black)"
# else Older kernels (prior to 3dc33bd30f3e, released in v4.40-rc1) do
# not have these LEGACY_VSYSCALL options and are effectively
# LEGACY_VSYSCALL_EMULATE. Even older kernels are presumably
# effectively LEGACY_VSYSCALL_NATIVE.
fi
}
if [ "$kernelMajor" -lt 4 ] || [ "$kernelMajor" -eq 4 -a "$kernelMinor" -le 5 ]; then
check_flags MEMCG_KMEM

View file

@ -237,11 +237,13 @@ __docker_complete_volumes() {
COMPREPLY=( $(compgen -W "$(__docker_volumes "$@")" -- "$current") )
}
# __docker_plugins returns a list of all plugins of a given type.
# __docker_plugins_bundled returns a list of all plugins of a given type.
# The type has to be specified with the mandatory option `--type`.
# Valid types are: Network, Volume, Authorization.
# Completions may be added or removed with `--add` and `--remove`
__docker_plugins() {
# This function only deals with plugins that come bundled with Docker.
# For plugins managed by `docker plugin`, see `__docker_plugins_installed`.
__docker_plugins_bundled() {
local type add=() remove=()
while true ; do
case "$1" in
@ -270,16 +272,39 @@ __docker_plugins() {
echo "${plugins[@]} ${add[@]}"
}
# __docker_complete_plugins applies completion of plugins based on the current
# __docker_complete_plugins_bundled applies completion of plugins based on the current
# value of `$cur` or the value of the optional first option `--cur`, if given.
# The plugin type has to be specified with the next option `--type`.
__docker_complete_plugins() {
# This function only deals with plugins that come bundled with Docker.
# For completion of plugins managed by `docker plugin`, see
# `__docker_complete_plugins_installed`.
__docker_complete_plugins_bundled() {
local current="$cur"
if [ "$1" = "--cur" ] ; then
current="$2"
shift 2
fi
COMPREPLY=( $(compgen -W "$(__docker_plugins "$@")" -- "$current") )
COMPREPLY=( $(compgen -W "$(__docker_plugins_bundled "$@")" -- "$current") )
}
# __docker_plugins_installed returns a list of all plugins that were installed with
# the Docker plugin API.
# For built-in pugins, see `__docker_plugins_bundled`.
__docker_plugins_installed() {
__docker_q plugin ls | awk 'NR>1 {print $1}'
}
# __docker_complete_plugins_installed applies completion of plugins that were installed
# with the Docker plugin API, based on the current value of `$cur` or the value of
# the optional first option `--cur`, if given.
# For completion of built-in pugins, see `__docker_complete_plugins_bundled`.
__docker_complete_plugins_installed() {
local current="$cur"
if [ "$1" = "--cur" ] ; then
current="$2"
shift 2
fi
COMPREPLY=( $(compgen -W "$(__docker_plugins_installed "$@")" -- "$current") )
}
__docker_runtimes() {
@ -1439,7 +1464,7 @@ _docker_container_run() {
__docker_complete_containers_all --cur "${cur#*:}"
;;
*)
COMPREPLY=( $( compgen -W "$(__docker_plugins --type Network) $(__docker_networks) container:" -- "$cur") )
COMPREPLY=( $( compgen -W "$(__docker_plugins_bundled --type Network) $(__docker_networks) container:" -- "$cur") )
if [ "${COMPREPLY[*]}" = "container:" ] ; then
__docker_nospace
fi
@ -1486,7 +1511,7 @@ _docker_container_run() {
return
;;
--volume-driver)
__docker_complete_plugins --type Volume
__docker_complete_plugins_bundled --type Volume
return
;;
--volumes-from)
@ -1742,7 +1767,7 @@ _docker_daemon() {
case "$prev" in
--authorization-plugin)
__docker_complete_plugins --type Authorization
__docker_complete_plugins_bundled --type Authorization
return
;;
--cluster-store)
@ -2348,7 +2373,7 @@ _docker_network_create() {
;;
--driver|-d)
# remove drivers that allow one instance only, add drivers missing in `docker info`
__docker_complete_plugins --type Network --remove host --remove null --add macvlan
__docker_complete_plugins_bundled --type Network --remove host --remove null --add macvlan
return
;;
--label)
@ -2399,7 +2424,7 @@ _docker_network_ls() {
local key=$(__docker_map_key_of_current_option '--filter|-f')
case "$key" in
driver)
__docker_complete_plugins --cur "${cur##*=}" --type Network --add macvlan
__docker_complete_plugins_bundled --cur "${cur##*=}" --type Network --add macvlan
return
;;
id)
@ -2621,6 +2646,7 @@ _docker_service_update() {
--health-interval
--health-retries
--health-timeout
--hostname
--label -l
--limit-cpu
--limit-memory
@ -2664,7 +2690,7 @@ _docker_service_update() {
--dns-search
--env-file
--group
--hostname
--host
--mode
--name
--port
@ -2675,6 +2701,14 @@ _docker_service_update() {
_filedir
return
;;
--host)
case "$cur" in
*:)
__docker_complete_resolved_hostname
return
;;
esac
;;
--mode)
COMPREPLY=( $( compgen -W "global replicated" -- "$cur" ) )
return
@ -2698,6 +2732,8 @@ _docker_service_update() {
--dns-search-rm
--group-add
--group-rm
--host-add
--host-rm
--image
--port-add
--port-rm
@ -2712,6 +2748,14 @@ _docker_service_update() {
COMPREPLY=( $(compgen -g -- "$cur") )
return
;;
--host-add|--host-rm)
case "$cur" in
*:)
__docker_complete_resolved_hostname
return
;;
esac
;;
--image)
__docker_complete_image_repos_and_tags
return
@ -3049,6 +3093,160 @@ _docker_pause() {
_docker_container_pause
}
_docker_plugin() {
local subcommands="
create
disable
enable
inspect
install
ls
push
rm
set
"
local aliases="
list
remove
"
__docker_subcommands "$subcommands $aliases" && return
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
;;
*)
COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
;;
esac
}
_docker_plugin_create() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--compress --help" -- "$cur" ) )
;;
*)
local counter=$(__docker_pos_first_nonflag)
if [ $cword -eq $counter ]; then
# reponame
return
elif [ $cword -eq $((counter + 1)) ]; then
_filedir -d
fi
;;
esac
}
_docker_plugin_disable() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
;;
*)
__docker_complete_plugins_installed
;;
esac
}
_docker_plugin_enable() {
case "$prev" in
--timeout)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help --timeout" -- "$cur" ) )
;;
*)
__docker_complete_plugins_installed
;;
esac
}
_docker_plugin_inspect() {
case "$prev" in
--format|f)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--format -f --help" -- "$cur" ) )
;;
*)
__docker_complete_plugins_installed
;;
esac
}
_docker_plugin_install() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--disable --grant-all-permissions--help" -- "$cur" ) )
;;
esac
}
_docker_plugin_list() {
_docker_plugin_ls
}
_docker_plugin_ls() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help --no-trunc" -- "$cur" ) )
;;
esac
}
_docker_plugin_push() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
;;
*)
local counter=$(__docker_pos_first_nonflag)
if [ $cword -eq $counter ]; then
__docker_complete_plugins_installed
fi
;;
esac
}
_docker_plugin_remove() {
_docker_plugin_rm
}
_docker_plugin_rm() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--force -f --help" -- "$cur" ) )
;;
*)
__docker_complete_plugins_installed
;;
esac
}
_docker_plugin_set() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
;;
*)
local counter=$(__docker_pos_first_nonflag)
if [ $cword -eq $counter ]; then
__docker_complete_plugins_installed
fi
;;
esac
}
_docker_port() {
_docker_container_port
}
@ -3300,7 +3498,7 @@ _docker_version() {
_docker_volume_create() {
case "$prev" in
--driver|-d)
__docker_complete_plugins --type Volume
__docker_complete_plugins_bundled --type Volume
return
;;
--label|--opt|-o)
@ -3340,7 +3538,7 @@ _docker_volume_ls() {
return
;;
driver)
__docker_complete_plugins --cur "${cur##*=}" --type Volume
__docker_complete_plugins_bundled --cur "${cur##*=}" --type Volume
return
;;
name)
@ -3440,6 +3638,7 @@ _docker() {
network
node
pause
plugin
port
ps
pull

View file

@ -1753,6 +1753,7 @@ __docker_service_subcommand() {
"($help)--health-interval=[Time between running the check]:time: "
"($help)--health-retries=[Consecutive failures needed to report unhealthy]:retries:(1 2 3 4 5)"
"($help)--health-timeout=[Maximum time to allow one check to run]:time: "
"($help)--hostname=[Service container hostname]:hostname: " \
"($help)*--label=[Service labels]:label: "
"($help)--limit-cpu=[Limit CPUs]:value: "
"($help)--limit-memory=[Limit Memory]:value: "
@ -1792,7 +1793,6 @@ __docker_service_subcommand() {
"($help)*--dns-option=[Set DNS options]:DNS option: " \
"($help)*--dns-search=[Set custom DNS search domains]:DNS search: " \
"($help)*--env-file=[Read environment variables from a file]:environment file:_files" \
"($help)--hostname=[Service containers hostname]:hostname: " \
"($help)--mode=[Service Mode]:mode:(global replicated)" \
"($help)--name=[Service name]:name: " \
"($help)*--port=[Publish a port]:port: " \

View file

@ -8,6 +8,12 @@ import (
"path/filepath"
"github.com/docker/docker/api/types"
"github.com/docker/docker/utils"
)
var (
validCheckpointNameChars = utils.RestrictedNameChars
validCheckpointNamePattern = utils.RestrictedNamePattern
)
// CheckpointCreate checkpoints the process running in a container with CRIU
@ -28,6 +34,10 @@ func (daemon *Daemon) CheckpointCreate(name string, config types.CheckpointCreat
checkpointDir = container.CheckpointDir()
}
if !validCheckpointNamePattern.MatchString(config.CheckpointID) {
return fmt.Errorf("Invalid checkpoint ID (%s), only %s are allowed", config.CheckpointID, validCheckpointNameChars)
}
err = daemon.containerd.CreateCheckpoint(container.ID, config.CheckpointID, checkpointDir, config.Exit)
if err != nil {
return fmt.Errorf("Cannot checkpoint container %s: %s", name, err)

View file

@ -1767,7 +1767,7 @@ func (c *Cluster) populateNetworkID(ctx context.Context, client swarmapi.Control
apiNetwork, err := getNetwork(ctx, client, n.Target)
if err != nil {
if ln, _ := c.config.Backend.FindNetwork(n.Target); ln != nil && !ln.Info().Dynamic() {
err = fmt.Errorf("network %s is not eligible for docker services", ln.Name())
err = fmt.Errorf("The network %s cannot be used with services. Only networks scoped to the swarm can be used, such as those created with the overlay driver.", ln.Name())
return apierrors.NewRequestForbiddenError(err)
}
return err
@ -1855,6 +1855,12 @@ func validateAndSanitizeInitRequest(req *types.InitRequest) error {
return fmt.Errorf("invalid ListenAddr %q: %v", req.ListenAddr, err)
}
if req.Spec.Annotations.Name == "" {
req.Spec.Annotations.Name = "default"
} else if req.Spec.Annotations.Name != "default" {
return errors.New(`swarm spec must be named "default"`)
}
return nil
}

View file

@ -571,6 +571,7 @@ func (c *containerConfig) networkCreateRequest(name string) (clustertypes.Networ
Options: na.Network.DriverState.Options,
Labels: na.Network.Spec.Annotations.Labels,
Internal: na.Network.Spec.Internal,
Attachable: na.Network.Spec.Attachable,
EnableIPv6: na.Network.Spec.Ipv6Enabled,
CheckDuplicate: true,
}

View file

@ -11,8 +11,8 @@ var (
errNoIP = errors.New("could not find the system's IP address")
errMustSpecifyListenAddr = errors.New("must specify a listening address because the address to advertise is not recognized as a system address, and a system's IP address to use could not be uniquely identified")
errBadListenAddr = errors.New("listen address must be an IP address or network interface (with optional port number)")
errBadAdvertiseAddr = errors.New("advertise address must be an IP address or network interface (with optional port number)")
errBadDefaultAdvertiseAddr = errors.New("default advertise address must be an IP address or network interface (without a port number)")
errBadAdvertiseAddr = errors.New("advertise address must be a non-zero IP address or network interface (with optional port number)")
errBadDefaultAdvertiseAddr = errors.New("default advertise address must be a non-zero IP address or network interface (without a port number)")
)
func resolveListenAddr(specifiedAddr string) (string, string, error) {
@ -69,7 +69,7 @@ func (c *Cluster) resolveAdvertiseAddr(advertiseAddr, listenAddrPort string) (st
}
// If it's not an interface, it must be an IP (for now)
if net.ParseIP(advertiseHost) == nil {
if ip := net.ParseIP(advertiseHost); ip == nil || ip.IsUnspecified() {
return "", "", errBadAdvertiseAddr
}
@ -89,7 +89,7 @@ func (c *Cluster) resolveAdvertiseAddr(advertiseAddr, listenAddrPort string) (st
}
// If it's not an interface, it must be an IP (for now)
if net.ParseIP(c.config.DefaultAdvertiseAddr) == nil {
if ip := net.ParseIP(c.config.DefaultAdvertiseAddr); ip == nil || ip.IsUnspecified() {
return "", "", errBadDefaultAdvertiseAddr
}

View file

@ -1028,7 +1028,7 @@ func (daemon *Daemon) ActivateContainerServiceBinding(containerName string) erro
}
sb := daemon.getNetworkSandbox(container)
if sb == nil {
return fmt.Errorf("network sandbox not exists for container %s", containerName)
return fmt.Errorf("network sandbox does not exist for container %s", containerName)
}
return sb.EnableService()
}
@ -1041,7 +1041,7 @@ func (daemon *Daemon) DeactivateContainerServiceBinding(containerName string) er
}
sb := daemon.getNetworkSandbox(container)
if sb == nil {
return fmt.Errorf("network sandbox not exists for container %s", containerName)
return fmt.Errorf("network sandbox does not exist for container %s", containerName)
}
return sb.DisableService()
}

View file

@ -4,7 +4,6 @@ package daemon
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net"
@ -1281,12 +1280,6 @@ func (daemon *Daemon) setupSeccompProfile() error {
return fmt.Errorf("opening seccomp profile (%s) failed: %v", daemon.configStore.SeccompProfile, err)
}
daemon.seccompProfile = b
p := struct {
DefaultAction string `json:"defaultAction"`
}{}
if err := json.Unmarshal(daemon.seccompProfile, &p); err != nil {
return err
}
}
return nil
}

View file

@ -2,6 +2,7 @@ package daemon
import (
"fmt"
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/container"
@ -58,7 +59,7 @@ func (daemon *Daemon) generateIDAndName(name string) (string, string, error) {
}
func (daemon *Daemon) reserveName(id, name string) (string, error) {
if !validContainerNamePattern.MatchString(name) {
if !validContainerNamePattern.MatchString(strings.TrimPrefix(name, "/")) {
return "", fmt.Errorf("Invalid container name (%s), only %s are allowed", name, validContainerNameChars)
}
if name[0] != '/' {

View file

@ -269,6 +269,7 @@ func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string
libnetwork.NetworkOptionEnableIPv6(create.EnableIPv6),
libnetwork.NetworkOptionDriverOpts(create.Options),
libnetwork.NetworkOptionLabels(create.Labels),
libnetwork.NetworkOptionAttachable(create.Attachable),
}
if create.IPAM != nil {

21
daemon/workdir.go Normal file
View file

@ -0,0 +1,21 @@
package daemon
// ContainerCreateWorkdir creates the working directory. This is solves the
// issue arising from https://github.com/docker/docker/issues/27545,
// which was initially fixed by https://github.com/docker/docker/pull/27884. But that fix
// was too expensive in terms of performance on Windows. Instead,
// https://github.com/docker/docker/pull/28514 introduces this new functionality
// where the builder calls into the backend here to create the working directory.
func (daemon *Daemon) ContainerCreateWorkdir(cID string) error {
container, err := daemon.GetContainer(cID)
if err != nil {
return err
}
err = daemon.Mount(container)
if err != nil {
return err
}
defer daemon.Unmount(container)
rootUID, rootGID := daemon.GetRemappedUIDGID()
return container.SetupWorkingDirectory(rootUID, rootGID)
}

View file

@ -4562,14 +4562,14 @@ image](#create-an-image) section for more details.
"Placement": {},
"Resources": {
"Limits": {
"MemoryBytes": 104857600.0
"MemoryBytes": 104857600
},
"Reservations": {
}
},
"RestartPolicy": {
"Condition": "on-failure",
"Delay": 10000000000.0,
"Delay": 10000000000,
"MaxAttempts": 10
}
},
@ -4579,7 +4579,7 @@ image](#create-an-image) section for more details.
}
},
"UpdateConfig": {
"Delay": 30000000000.0,
"Delay": 30000000000,
"Parallelism": 2,
"FailureAction": "pause"
},

View file

@ -1,7 +1,4 @@
---
aliases: [
"/engine/extend/"
]
title: "Plugin config"
description: "How develop and use a plugin with the managed plugin system"
keywords: "API, Usage, plugins, documentation, developer"
@ -172,53 +169,50 @@ Config provides the base accessible fields for working with V0 plugin format
*Example showing the 'tiborvass/no-remove' plugin config.*
```
```json
{
"description": "A test plugin for Docker",
"documentation": "https://docs.docker.com/engine/extend/plugins/",
"entrypoint": ["plugin-no-remove", "/data"],
"interface" : {
"types": ["docker.volumedriver/1.0"],
"socket": "plugins.sock"
},
"network": {
"type": "host"
},
"mounts": [
{
"source": "/data",
"destination": "/data",
"type": "bind",
"options": ["shared", "rbind"]
},
{
"destination": "/foobar",
"type": "tmpfs"
}
],
"args": {
"name": "args",
"description": "command line arguments",
"value": []
},
"env": [
{
"name": "DEBUG",
"description": "If set, prints debug messages",
"value": "1"
}
],
"devices": [
{
"name": "device",
"description": "a host device to mount",
"path": "/dev/cpu_dma_latency"
}
]
"description": "A test plugin for Docker",
"documentation": "https://docs.docker.com/engine/extend/plugins/",
"entrypoint": ["plugin-no-remove", "/data"],
"interface": {
"types": ["docker.volumedriver/1.0"],
"socket": "plugins.sock"
},
"network": {
"type": "host"
},
"mounts": [
{
"source": "/data",
"destination": "/data",
"type": "bind",
"options": ["shared", "rbind"]
},
{
"destination": "/foobar",
"type": "tmpfs"
}
],
"args": {
"name": "args",
"description": "command line arguments",
"value": []
},
"env": [
{
"name": "DEBUG",
"description": "If set, prints debug messages",
"value": "1"
}
],
"linux": {
"devices": [
{
"name": "device",
"description": "a host device to mount",
"path": "/dev/cpu_dma_latency"
}
]
}
}
```

View file

@ -1,6 +1,4 @@
---
aliases:
- /engine/extend/
description: Develop and use a plugin with the managed plugin system
keywords: "API, Usage, plugins, documentation, developer"
title: Managed plugin system

View file

@ -1,5 +1,6 @@
---
aliases: "/engine/extend/plugins/"
redirect_from:
- "/engine/extend/plugins/"
title: "Use Docker Engine plugins"
description: "How to add additional functionality to Docker with plugins extensions"
keywords: "Examples, Usage, plugins, docker, documentation, user guide"

View file

@ -1,18 +0,0 @@
---
title: "Implement plugins"
description: "Develop plugins and use existing plugins for Docker Engine"
keywords: ["extend, plugins, docker, documentation, developer"]
type: "menu"
identifier: "engine_extend"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
pull requests against that repo. If you see this file in
another repository, consider it read-only there, as it will
periodically be overwritten by the definitive file. Pull
requests which include edits to this file in other repositories
will be rejected.
-->
<!--menu page not rendered-->

View file

@ -2,7 +2,8 @@
title: "Access authorization plugin"
description: "How to create authorization plugins to manage access control to your Docker daemon."
keywords: "security, authorization, authentication, docker, documentation, plugin, extend"
aliases: ["/engine/extend/authorization/"]
redirect_from:
- "/engine/extend/authorization/"
---
<!-- This file is maintained within the docker/docker Github

View file

@ -61,7 +61,7 @@ referring to that network will be sent to the plugin,
## Write a network plugin
Network plugins implement the [Docker plugin
API](https://docs.docker.com/extend/plugin_api/) and the network plugin protocol
API](plugin_api.md) and the network plugin protocol
## Network plugin protocol

View file

@ -26,7 +26,6 @@ Usage: docker [OPTIONS] COMMAND [ARG...]
A self-sufficient runtime for containers.
Options:
--config string Location of client config files (default "/root/.docker")
-D, --debug Enable debug mode
--help Print usage
@ -72,8 +71,8 @@ by the `docker` command line:
to the same URL as the registry.
* `DOCKER_TMPDIR` Location for temporary Docker files.
Because Docker is developed using 'Go', you can also use any environment
variables used by the 'Go' runtime. In particular, you may find these useful:
Because Docker is developed using Go, you can also use any environment
variables used by the Go runtime. In particular, you may find these useful:
* `HTTP_PROXY`
* `HTTPS_PROXY`

View file

@ -22,7 +22,6 @@ Usage: dockerd [OPTIONS]
A self-sufficient runtime for containers.
Options:
--add-runtime value Register an additional OCI compatible runtime (default [])
--api-cors-header string Set CORS headers in the Engine API
--authorization-plugin value Authorization plugins to load (default [])

View file

@ -111,7 +111,8 @@ storage driver and a node that is part of a 2-node swarm:
Goroutines: 123
System Time: 2016-11-12T17:24:37.955404361-08:00
EventsListeners: 0
Http Proxy: http://proxy.example.com:80/
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
@ -121,6 +122,9 @@ storage driver and a node that is part of a 2-node swarm:
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.
@ -172,7 +176,7 @@ information about the devicemapper storage driver is shown:
Docker Root Dir: /var/lib/docker
Debug mode (client): false
Debug mode (server): false
Username: xyz
Username: gordontheturtle
Registry: https://index.docker.io/v1/
Insecure registries:
myinsecurehost:5000
@ -214,4 +218,7 @@ Here is a sample output for a daemon running on Windows Server 2016:
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

View file

@ -61,11 +61,8 @@ being connected to.
```bash
$ docker network connect --alias db --alias mysql multi-host-network container2
```
You can pause, restart, and stop containers that are connected to a network.
Paused containers remain connected and can be revealed by a `network inspect`.
When the container is stopped, it does not appear on the network until you restart
it.
A container connects to its configured networks when it runs.
If specified, the container's IP address(es) is reapplied when a stopped
container is restarted. If the IP address is no longer available, the container

View file

@ -24,11 +24,11 @@ Aliases:
ls, list
Options:
-f, --filter value Provide filter values (i.e. 'dangling=true') (default [])
--format string Pretty-print networks using a Go template
--help Print usage
--no-trunc Do not truncate the output
-q, --quiet Only display network IDs
-f, --filter filter Provide filter values (e.g. 'driver=bridge')
--format string Pretty-print networks using a Go template
--help Print usage
--no-trunc Do not truncate the output
-q, --quiet Only display network IDs
```
Lists all the networks the Engine `daemon` knows about. This includes the

View file

@ -16,7 +16,7 @@ keywords: "plugin, create"
# plugin create
```markdown
Usage: docker plugin create [OPTIONS] reponame[:tag] PATH-TO-ROOTFS
Usage: docker plugin create [OPTIONS] PLUGIN[:tag] PATH-TO-ROOTFS(rootfs + config.json)
Create a plugin from a rootfs and configuration
@ -26,7 +26,7 @@ Options:
```
Creates a plugin. Before creating the plugin, prepare the plugin's root filesystem as well as
the config.json (https://github.com/docker/docker/blob/master/docs/extend/config.md)
[the config.json](../../extend/config.md)
The following example shows how to create a sample `plugin`.

View file

@ -16,12 +16,13 @@ keywords: "plugin, enable"
# plugin enable
```markdown
Usage: docker plugin enable PLUGIN
Usage: docker plugin enable [OPTIONS] PLUGIN
Enable a plugin
Options:
--help Print usage
--help Print usage
--timeout int HTTP client timeout (in seconds)
```
Enables a plugin. The plugin must be installed before it can be enabled,

View file

@ -14,7 +14,7 @@ keywords: "plugin, push"
-->
```markdown
Usage: docker plugin push NAME[:TAG]
Usage: docker plugin push PLUGIN[:TAG]
Push a plugin to a registry

View file

@ -262,6 +262,26 @@ https://docs.docker.com/engine/installation/binaries/#/get-the-linux-binary)),
you give the container the full access to create and manipulate the host's
Docker daemon.
On Windows, the paths must be specified using Windows-style semantics.
PS C:\> docker run -v c:\foo:c:\dest microsoft/nanoserver cmd /s /c type c:\dest\somefile.txt
Contents of file
PS C:\> docker run -v c:\foo:d: microsoft/nanoserver cmd /s /c type d:\somefile.txt
Contents of file
The following examples will fail when using Windows-based containers, as the
destination of a volume or bind-mount inside the container must be one of:
a non-existing or empty directory; or a drive other than C:. Further, the source
of a bind mount must be a local directory, not a file.
net use z: \\remotemachine\share
docker run -v z:\foo:c:\dest ...
docker run -v \\uncpath\to\directory:c:\dest ...
docker run -v c:\foo\somefile.txt:c:\dest ...
docker run -v c:\foo:c: ...
docker run -v c:\foo:c:\existing-directory-with-contents ...
For in-depth information about volumes, refer to [manage data in containers](https://docs.docker.com/engine/tutorials/dockervolumes/)
### Add bin-mounts or volumes using the --mount flag

View file

@ -128,7 +128,7 @@ and are automated builds:
This example displays images with a name containing 'busybox', at least
3 stars and are official builds:
$ docker search --filter "is-automated=true" --filter "stars=3" busybox
$ docker search --filter "is-official=true" --filter "stars=3" busybox
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
progrium/busybox 50 [OK]
radial/busyboxplus Full-chain, Internet enabled, busybox made... 8 [OK]

View file

@ -16,7 +16,7 @@ keywords: ["secret, create"]
# secret create
```Markdown
Usage: docker secret create [NAME]
Usage: docker secret create [OPTIONS] SECRET
Create a secret using stdin as content
Options:
@ -76,5 +76,3 @@ $ docker secret inspect secret.json
* [secret inspect](secret_inspect.md)
* [secret ls](secret_ls.md)
* [secret rm](secret_rm.md)
<style>table tr > td:first-child { white-space: nowrap;}</style>

View file

@ -137,7 +137,7 @@ Create a service specifying the secret, target, user/group ID and mode:
```bash
$ docker service create --name redis \
--secret source=ssh-key,target=ssh \
--secret source=app-key,target=app,uid=1000,gid=1001,mode=0400 \
--secret src=app-key,target=app,uid=1000,gid=1001,mode=0400 \
redis:3.0.6
4cdgfyky7ozwh3htjfw0d12qv
```
@ -474,6 +474,35 @@ accessible at the target port on every node regardless if there is a task for
the service running on the node. For more information refer to
[Use swarm mode routing mesh](https://docs.docker.com/engine/swarm/ingress/).
### Publish a port for TCP only or UCP only
By default, when you publish a port, it is a TCP port. You can
specifically publish a UDP port instead of or in addition to a TCP port. When
you publish both TCP and UDP ports, Docker 1.12.2 and earlier require you to
add the suffix `/tcp` for TCP ports. Otherwise it is optional.
#### TCP only
The following two commands are equivalent.
```bash
$ docker service create --name dns-cache -p 53:53 dns-cache
$ docker service create --name dns-cache -p 53:53/tcp dns-cache
```
#### TCP and UDP
```bash
$ docker service create --name dns-cache -p 53:53/tcp -p 53:53/udp dns-cache
```
#### UDP only
```bash
$ docker service create --name dns-cache -p 53:53/udp dns-cache
```
### Create services using templates
You can use templates for some flags of `service create`, using the syntax

View file

@ -45,6 +45,7 @@ Options:
--help Print usage
--host-add list Add or update a custom host-to-IP mapping (host:ip) (default [])
--host-rm list Remove a custom host-to-IP mapping (host:ip) (default [])
--hostname string Container hostname
--image string Service image tag
--label-add list Add or update a service label (default [])
--label-rm list Remove a label by its key (default [])

View file

@ -1,37 +0,0 @@
---
title: "stack config"
description: "The stack config command description and usage"
keywords: "stack, config"
advisory: "experimental"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
pull requests against that repo. If you see this file in
another repository, consider it read-only there, as it will
periodically be overwritten by the definitive file. Pull
requests which include edits to this file in other repositories
will be rejected.
-->
# stack config (experimental)
```markdown
Usage: docker stack config [OPTIONS] STACK
Print the stack configuration
Options:
--file string Path to a Distributed Application Bundle file (Default: STACK.dab)
--help Print usage
```
Displays the configuration of a stack.
## Related information
* [stack deploy](stack_deploy.md)
* [stack rm](stack_rm.md)
* [stack services](stack_services.md)
* [stack ps](stack_ps.md)
* [stack ls](stack_ls.md)

View file

@ -2,7 +2,6 @@
title: "stack deploy"
description: "The stack deploy command description and usage"
keywords: "stack, deploy, up"
advisory: "experimental"
---
<!-- This file is maintained within the docker/docker Github
@ -14,7 +13,7 @@ advisory: "experimental"
will be rejected.
-->
# stack deploy (experimental)
# stack deploy
```markdown
Usage: docker stack deploy [OPTIONS] STACK
@ -26,7 +25,7 @@ Aliases:
Options:
--bundle-file string Path to a Distributed Application Bundle file
--compose-file string Path to a Compose file
-c, --compose-file string Path to a Compose file
--help Print usage
--with-registry-auth Send registry authentication details to Swarm agents
```

View file

@ -2,7 +2,6 @@
title: "stack ls"
description: "The stack ls command description and usage"
keywords: "stack, ls"
advisory: "experimental"
---
<!-- This file is maintained within the docker/docker Github
@ -14,12 +13,18 @@ advisory: "experimental"
will be rejected.
-->
# stack ls (experimental)
# stack ls
```markdown
Usage: docker stack ls
List stacks
Aliases:
ls, list
Options:
--help Print usage
```
Lists the stacks.

View file

@ -2,7 +2,6 @@
title: "stack ps"
description: "The stack ps command description and usage"
keywords: "stack, ps"
advisory: "experimental"
---
<!-- This file is maintained within the docker/docker Github
@ -14,7 +13,7 @@ advisory: "experimental"
will be rejected.
-->
# stack ps (experimental)
# stack ps
```markdown
Usage: docker stack ps [OPTIONS] STACK

View file

@ -2,7 +2,6 @@
title: "stack rm"
description: "The stack rm command description and usage"
keywords: "stack, rm, remove, down"
advisory: "experimental"
---
<!-- This file is maintained within the docker/docker Github
@ -14,7 +13,7 @@ advisory: "experimental"
will be rejected.
-->
# stack rm (experimental)
# stack rm
```markdown
Usage: docker stack rm STACK

View file

@ -21,16 +21,16 @@ Usage: docker swarm init [OPTIONS]
Initialize a swarm
Options:
--advertise-addr value Advertised address (format: <ip|interface>[:port])
--advertise-addr string Advertised address (format: <ip|interface>[:port])
--autolock Enable manager autolocking (requiring an unlock key to start a stopped manager)
--cert-expiry duration Validity period for node certificates (ns|us|ms|s|m|h) (default 2160h0m0s)
--dispatcher-heartbeat duration Dispatcher heartbeat period (ns|us|ms|s|m|h) (default 5s)
--external-ca value Specifications of one or more certificate signing endpoints
--external-ca external-ca Specifications of one or more certificate signing endpoints
--force-new-cluster Force create a new cluster from current state
--help Print usage
--listen-addr value Listen address (format: <ip|interface>[:port])
--max-snapshots int Number of additional Raft snapshots to retain
--snapshot-interval int Number of log entries between Raft snapshots
--listen-addr node-addr Listen address (format: <ip|interface>[:port]) (default 0.0.0.0:2377)
--max-snapshots uint Number of additional Raft snapshots to retain
--snapshot-interval uint Number of log entries between Raft snapshots (default 10000)
--task-history-limit int Task history retention limit (default 5)
```
@ -76,7 +76,7 @@ This flag sets the validity period for node certificates.
### `--dispatcher-heartbeat`
This flags sets the frequency with which nodes are told to use as a
This flag sets the frequency with which nodes are told to use as a
period to report their health.
### `--external-ca`
@ -103,7 +103,7 @@ name, the default port 2377 will be used.
This flag specifies the address that will be advertised to other members of the
swarm for API access and overlay networking. If unspecified, Docker will check
if the system has a single IP address, and use that IP address with with the
if the system has a single IP address, and use that IP address with the
listening port (see `--listen-addr`). If the system has multiple IP addresses,
`--advertise-addr` must be specified so that the correct address is chosen for
inter-manager communication and overlay networking.

View file

@ -77,7 +77,7 @@ This flag is generally not necessary when joining an existing swarm.
This flag specifies the address that will be advertised to other members of the
swarm for API access. If unspecified, Docker will check if the system has a
single IP address, and use that IP address with with the listening port (see
single IP address, and use that IP address with the listening port (see
`--listen-addr`). If the system has multiple IP addresses, `--advertise-addr`
must be specified so that the correct address is chosen for inter-manager
communication and overlay networking.

View file

@ -123,8 +123,8 @@ and pass along signals. All of that is configurable:
--sig-proxy=true: Proxy all received signals to the process (non-TTY mode only)
-i : Keep STDIN open even if not attached
If you do not specify `-a` then Docker will [attach all standard
streams]( https://github.com/docker/docker/blob/75a7f4d90cde0295bcfb7213004abce8d4779b75/commands.go#L1797).
If you do not specify `-a` then Docker will [attach to both stdout and stderr
]( https://github.com/docker/docker/blob/4118e0c9eebda2412a09ae66e90c34b85fae3275/runconfig/opts/parse.go#L267).
You can specify to which of the three standard streams (`STDIN`, `STDOUT`,
`STDERR`) you'd like to connect instead, as in:
@ -345,13 +345,13 @@ Supported networks :
<td class="no-wrap"><strong>container</strong>:&lt;name|id&gt;</td>
<td>
Use the network stack of another container, specified via
its *name* or *id*.
its <i>name</i> or <i>id</i>.
</td>
</tr>
<tr>
<td class="no-wrap"><strong>NETWORK</strong></td>
<td>
Connects the container to a user created network (using `docker network create` command)
Connects the container to a user created network (using <code>docker network create</code> command)
</td>
</tr>
</tbody>
@ -624,15 +624,15 @@ but the volume for `/bar` will not. Volumes inherited via `--volumes-from` will
with the same logic -- if the original volume was specified with a name it will **not** be removed.
## Security configuration
--security-opt="label=user:USER" : Set the label user for the container
--security-opt="label=role:ROLE" : Set the label role for the container
--security-opt="label=type:TYPE" : Set the label type for the container
--security-opt="label=level:LEVEL" : Set the label level for the container
--security-opt="label=disable" : Turn off label confinement for the container
--security-opt="apparmor=PROFILE" : Set the apparmor profile to be applied to the container
--security-opt="no-new-privileges" : Disable container processes from gaining new privileges
--security-opt="seccomp=unconfined": Turn off seccomp confinement for the container
--security-opt="seccomp=profile.json: White listed syscalls seccomp Json file to be used as a seccomp filter
--security-opt="label=user:USER" : Set the label user for the container
--security-opt="label=role:ROLE" : Set the label role for the container
--security-opt="label=type:TYPE" : Set the label type for the container
--security-opt="label=level:LEVEL" : Set the label level for the container
--security-opt="label=disable" : Turn off label confinement for the container
--security-opt="apparmor=PROFILE" : Set the apparmor profile to be applied to the container
--security-opt="no-new-privileges" : Disable container processes from gaining new privileges
--security-opt="seccomp=unconfined" : Turn off seccomp confinement for the container
--security-opt="seccomp=profile.json": White listed syscalls seccomp Json file to be used as a seccomp filter
You can override the default labeling scheme for each container by specifying
@ -737,7 +737,7 @@ We have four ways to set user memory usage:
<td class="no-wrap"><strong>memory=L&lt;inf, memory-swap=2*L</strong></td>
<td>
(specify memory without memory-swap) The container is not allowed to
use more than L bytes of memory, swap *plus* memory usage is double
use more than L bytes of memory, swap <i>plus</i> memory usage is double
of that.
</td>
</tr>
@ -747,7 +747,7 @@ We have four ways to set user memory usage:
</td>
<td>
(specify both memory and memory-swap) The container is not allowed to
use more than L bytes of memory, swap *plus* memory usage is limited
use more than L bytes of memory, swap <i>plus</i> memory usage is limited
by S.
</td>
</tr>

View file

@ -1878,8 +1878,8 @@ func (s *DockerSuite) TestBuildWindowsAddCopyPathProcessing(c *check.C) {
WORKDIR /wc2
ADD wc2 c:/wc2
WORKDIR c:/
RUN sh -c "[ $(cat c:/wc1) = 'hellowc1' ]"
RUN sh -c "[ $(cat c:/wc2) = 'worldwc2' ]"
RUN sh -c "[ $(cat c:/wc1/wc1) = 'hellowc1' ]"
RUN sh -c "[ $(cat c:/wc2/wc2) = 'worldwc2' ]"
# Trailing slash on COPY/ADD, Windows-style path.
WORKDIR /wd1
@ -7283,3 +7283,31 @@ func (s *DockerSuite) TestBuildWindowsUser(c *check.C) {
}
c.Assert(strings.ToLower(out), checker.Contains, "username=user")
}
// Verifies if COPY file . when WORKDIR is set to a non-existing directory,
// the directory is created and the file is copied into the directory,
// as opposed to the file being copied as a file with the name of the
// directory. Fix for 27545 (found on Windows, but regression good for Linux too).
// Note 27545 was reverted in 28505, but a new fix was added subsequently in 28514.
func (s *DockerSuite) TestBuildCopyFileDotWithWorkdir(c *check.C) {
name := "testbuildcopyfiledotwithworkdir"
ctx, err := fakeContext(`FROM busybox
WORKDIR /foo
COPY file .
RUN ["cat", "/foo/file"]
`,
map[string]string{})
if err != nil {
c.Fatal(err)
}
defer ctx.Close()
if err := ctx.Add("file", "content"); err != nil {
c.Fatal(err)
}
if _, err = buildImageFromContext(name, ctx, true); err != nil {
c.Fatal(err)
}
}

View file

@ -104,7 +104,6 @@ func (s *DockerSuite) TestCommitWithHostBindMount(c *check.C) {
}
func (s *DockerSuite) TestCommitChange(c *check.C) {
testRequires(c, DaemonIsLinux)
dockerCmd(c, "run", "--name", "test", "busybox", "true")
imageID, _ := dockerCmd(c, "commit",
@ -122,12 +121,14 @@ func (s *DockerSuite) TestCommitChange(c *check.C) {
"test", "test-commit")
imageID = strings.TrimSpace(imageID)
prefix, slash := getPrefixAndSlashFromDaemonPlatform()
prefix = strings.ToUpper(prefix) // Force C: as that's how WORKDIR is normalised on Windows
expected := map[string]string{
"Config.ExposedPorts": "map[8080/tcp:{}]",
"Config.Env": "[DEBUG=true test=1 PATH=/foo]",
"Config.Labels": "map[foo:bar]",
"Config.Cmd": "[/bin/sh]",
"Config.WorkingDir": "/opt",
"Config.WorkingDir": prefix + slash + "opt",
"Config.Entrypoint": "[/bin/sh]",
"Config.User": "testuser",
"Config.Volumes": "map[/var/lib/docker:{}]",

View file

@ -276,7 +276,7 @@ func (s *DockerSuite) TestEventsImageLoad(c *check.C) {
}
func (s *DockerSuite) TestEventsPluginOps(c *check.C) {
testRequires(c, DaemonIsLinux)
testRequires(c, DaemonIsLinux, Network)
since := daemonUnixTime(c)

View file

@ -4,6 +4,7 @@ import (
"github.com/docker/docker/pkg/integration/checker"
"github.com/go-check/check"
"io/ioutil"
"os"
"path/filepath"
"strings"
@ -143,7 +144,7 @@ func (s *DockerSuite) TestPluginInstallArgs(c *check.C) {
}
func (s *DockerSuite) TestPluginInstallImage(c *check.C) {
testRequires(c, DaemonIsLinux)
testRequires(c, DaemonIsLinux, Network)
out, _, err := dockerCmdWithError("plugin", "install", "redis")
c.Assert(err, checker.NotNil)
c.Assert(out, checker.Contains, "content is not a plugin")
@ -169,3 +170,34 @@ func (s *DockerSuite) TestPluginEnableDisableNegative(c *check.C) {
_, _, err = dockerCmdWithError("plugin", "remove", pName)
c.Assert(err, checker.IsNil)
}
func (s *DockerSuite) TestPluginCreate(c *check.C) {
testRequires(c, DaemonIsLinux, Network)
name := "foo/bar-driver"
temp, err := ioutil.TempDir("", "foo")
c.Assert(err, checker.IsNil)
defer os.RemoveAll(temp)
data := `{"description": "foo plugin"}`
err = ioutil.WriteFile(filepath.Join(temp, "config.json"), []byte(data), 0644)
c.Assert(err, checker.IsNil)
out, _, err := dockerCmdWithError("plugin", "create", name, temp)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, name)
out, _, err = dockerCmdWithError("plugin", "ls")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, name)
out, _, err = dockerCmdWithError("plugin", "create", name, temp)
c.Assert(err, checker.NotNil)
c.Assert(out, checker.Contains, "already exist")
out, _, err = dockerCmdWithError("plugin", "ls")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, name)
// The output will consists of one HEADER line and one line of foo/bar-driver
c.Assert(len(strings.Split(strings.TrimSpace(out), "\n")), checker.Equals, 2)
}

View file

@ -4570,7 +4570,7 @@ func (s *DockerSuite) TestRunServicingContainer(c *check.C) {
}
func (s *DockerSuite) TestRunDuplicateMount(c *check.C) {
testRequires(c, SameHostDaemon, DaemonIsLinux)
testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)
tmpFile, err := ioutil.TempFile("", "touch-me")
c.Assert(err, checker.IsNil)

View file

@ -1312,7 +1312,7 @@ func (s *DockerSuite) TestUserNoEffectiveCapabilitiesChroot(c *check.C) {
}
func (s *DockerSuite) TestUserNoEffectiveCapabilitiesMknod(c *check.C) {
testRequires(c, DaemonIsLinux)
testRequires(c, DaemonIsLinux, NotUserNamespace)
ensureSyscallTest(c)
// test that a root user has default capability CAP_MKNOD

View file

@ -86,6 +86,13 @@ func (s *DockerSwarmSuite) TestSwarmInitIPv6(c *check.C) {
c.Assert(out, checker.Contains, "Swarm: active")
}
func (s *DockerSwarmSuite) TestSwarmInitUnspecifiedAdvertiseAddr(c *check.C) {
d := s.AddDaemon(c, false, false)
out, err := d.Cmd("swarm", "init", "--advertise-addr", "0.0.0.0")
c.Assert(err, checker.NotNil)
c.Assert(out, checker.Contains, "advertise address must be a non-zero IP address")
}
func (s *DockerSwarmSuite) TestSwarmIncompatibleDaemon(c *check.C) {
// init swarm mode and stop a daemon
d := s.AddDaemon(c, true, true)
@ -418,6 +425,34 @@ func (s *DockerSwarmSuite) TestSwarmContainerAttachByNetworkId(c *check.C) {
waitAndAssert(c, 3*time.Second, checkNetwork, checker.Not(checker.Contains), "testnet")
}
func (s *DockerSwarmSuite) TestOverlayAttachable(c *check.C) {
d1 := s.AddDaemon(c, true, true)
d2 := s.AddDaemon(c, true, false)
out, err := d1.Cmd("network", "create", "-d", "overlay", "--attachable", "ovnet")
c.Assert(err, checker.IsNil, check.Commentf(out))
// validate attachable
out, err = d1.Cmd("network", "inspect", "--format", "{{json .Attachable}}", "ovnet")
c.Assert(err, checker.IsNil, check.Commentf(out))
c.Assert(strings.TrimSpace(out), checker.Equals, "true")
// validate containers can attache to this overlay network
out, err = d1.Cmd("run", "-d", "--network", "ovnet", "--name", "c1", "busybox", "top")
c.Assert(err, checker.IsNil, check.Commentf(out))
out, err = d2.Cmd("run", "-d", "--network", "ovnet", "--name", "c2", "busybox", "top")
c.Assert(err, checker.IsNil, check.Commentf(out))
// redo validation, there was a bug that the value of attachable changes after
// containers attach to the network
out, err = d1.Cmd("network", "inspect", "--format", "{{json .Attachable}}", "ovnet")
c.Assert(err, checker.IsNil, check.Commentf(out))
c.Assert(strings.TrimSpace(out), checker.Equals, "true")
out, err = d2.Cmd("network", "inspect", "--format", "{{json .Attachable}}", "ovnet")
c.Assert(err, checker.IsNil, check.Commentf(out))
c.Assert(strings.TrimSpace(out), checker.Equals, "true")
}
func (s *DockerSwarmSuite) TestSwarmRemoveInternalNetwork(c *check.C) {
d := s.AddDaemon(c, true, true)

View file

@ -14,7 +14,7 @@ func (s *DockerSuite) TestVersionEnsureSucceeds(c *check.C) {
"Client:": 1,
"Server:": 1,
" Version:": 2,
" API version:": 3,
" API version:": 2,
" Go version:": 2,
" Git commit:": 2,
" OS/Arch:": 2,
@ -40,7 +40,7 @@ func (s *DockerSuite) TestVersionPlatform_l(c *check.C) {
func testVersionPlatform(c *check.C, platform string) {
out, _ := dockerCmd(c, "version")
expected := "OS/Arch: " + platform
expected := "OS/Arch: " + platform
split := strings.Split(out, "\n")
c.Assert(len(split) >= 14, checker.Equals, true, check.Commentf("got %d lines from version", len(split)))

View file

@ -103,6 +103,43 @@ which displayed different visualizations of the image data. Docker core removed
this functionality in the 1.7 version. If you liked this functionality, you can
still find it in the third-party dockviz tool: https://github.com/justone/dockviz.
## Listing images in a desired format
When using the --format option, the image command will either output the data
exactly as the template declares or, when using the `table` directive, will
include column headers as well. You can use special characters like `\t` for
inserting tab spacing between columns.
The following example uses a template without headers and outputs the ID and
Repository entries separated by a colon for all images:
docker images --format "{{.ID}}: {{.Repository}}"
77af4d6b9913: <none>
b6fa739cedf5: committ
78a85c484bad: ipbabble
30557a29d5ab: docker
5ed6274db6ce: <none>
746b819f315e: postgres
746b819f315e: postgres
746b819f315e: postgres
746b819f315e: postgres
To list all images with their repository and tag in a table format you can use:
docker images --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"
IMAGE ID REPOSITORY TAG
77af4d6b9913 <none> <none>
b6fa739cedf5 committ latest
78a85c484bad ipbabble <none>
30557a29d5ab docker latest
5ed6274db6ce <none> <none>
746b819f315e postgres 9
746b819f315e postgres 9.3
746b819f315e postgres 9.3.5
746b819f315e postgres latest
Valid template placeholders are listed above.
## Listing only the shortened image IDs
Listing just the shortened image IDs. This can be useful for some automated

View file

@ -103,7 +103,8 @@ storage driver:
Goroutines: 123
System Time: 2016-11-12T17:24:37.955404361-08:00
EventsListeners: 0
Http Proxy: http://proxy.example.com:80/
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
@ -113,6 +114,9 @@ storage driver:
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
@ -166,7 +170,7 @@ information about the devicemapper storage driver is shown:
Docker Root Dir: /var/lib/docker
Debug mode (client): false
Debug mode (server): false
Username: xyz
Username: gordontheturtle
Registry: https://index.docker.io/v1/
Insecure registries:
myinsecurehost:5000

View file

@ -24,11 +24,8 @@ You can also use the `docker run --net=<network-name>` option to start a contain
```bash
$ docker run -itd --net=multi-host-network --ip 172.20.88.22 --ip6 2001:db8::8822 busybox
```
You can pause, restart, and stop containers that are connected to a network.
Paused containers remain connected and can be revealed by a `network inspect`.
When the container is stopped, it does not appear on the network until you restart
it.
A container connects to its configured networks when it runs.
If specified, the container's IP address(es) is reapplied when a stopped
container is restarted. If the IP address is no longer available, the container

View file

@ -19,6 +19,7 @@ import (
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/plugin/distribution"
"github.com/docker/docker/plugin/v2"
"github.com/docker/docker/reference"
"golang.org/x/net/context"
)
@ -60,27 +61,7 @@ func (pm *Manager) Inspect(name string) (tp types.Plugin, err error) {
return p.PluginObj, nil
}
// Pull pulls a plugin and computes the privileges required to install it.
func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.AuthConfig) (types.PluginPrivileges, error) {
ref, err := distribution.GetRef(name)
if err != nil {
logrus.Debugf("error in distribution.GetRef: %v", err)
return nil, err
}
name = ref.String()
if p, _ := pm.pluginStore.GetByName(name); p != nil {
logrus.Debug("plugin already exists")
return nil, fmt.Errorf("%s exists", name)
}
pluginID := stringid.GenerateNonCryptoID()
if err := os.MkdirAll(filepath.Join(pm.libRoot, pluginID), 0755); err != nil {
logrus.Debugf("error in MkdirAll: %v", err)
return nil, err
}
func (pm *Manager) pull(ref reference.Named, metaHeader http.Header, authConfig *types.AuthConfig, pluginID string) (types.PluginPrivileges, error) {
pd, err := distribution.Pull(ref, pm.registryService, metaHeader, authConfig)
if err != nil {
logrus.Debugf("error in distribution.Pull(): %v", err)
@ -99,10 +80,42 @@ func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.A
}
pm.pluginStore.Add(p)
pm.pluginEventLogger(pluginID, name, "pull")
pm.pluginEventLogger(pluginID, ref.String(), "pull")
return p.ComputePrivileges(), nil
}
// Pull pulls a plugin and computes the privileges required to install it.
func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.AuthConfig) (types.PluginPrivileges, error) {
ref, err := distribution.GetRef(name)
if err != nil {
logrus.Debugf("error in distribution.GetRef: %v", err)
return nil, err
}
name = ref.String()
if p, _ := pm.pluginStore.GetByName(name); p != nil {
logrus.Debug("plugin already exists")
return nil, fmt.Errorf("%s exists", name)
}
pluginID := stringid.GenerateNonCryptoID()
pluginDir := filepath.Join(pm.libRoot, pluginID)
if err := os.MkdirAll(pluginDir, 0755); err != nil {
logrus.Debugf("error in MkdirAll: %v", err)
return nil, err
}
priv, err := pm.pull(ref, metaHeader, authConfig, pluginID)
if err != nil {
if err := os.RemoveAll(pluginDir); err != nil {
logrus.Warnf("unable to remove %q from failed plugin pull: %v", pluginDir, err)
}
return nil, err
}
return priv, nil
}
// List displays the list of plugins and associated metadata.
func (pm *Manager) List() ([]types.Plugin, error) {
plugins := pm.pluginStore.GetAll()
@ -194,6 +207,18 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.Reader, opti
return err
}
// In case an error happens, remove the created directory.
if err := pm.createFromContext(ctx, pluginID, pluginDir, tarCtx, options); err != nil {
if err := os.RemoveAll(pluginDir); err != nil {
logrus.Warnf("unable to remove %q from failed plugin creation: %v", pluginDir, err)
}
return err
}
return nil
}
func (pm *Manager) createFromContext(ctx context.Context, pluginID, pluginDir string, tarCtx io.Reader, options *types.PluginCreateOptions) error {
if err := chrootarchive.Untar(tarCtx, pluginDir, nil); err != nil {
return err
}
@ -211,7 +236,9 @@ func (pm *Manager) CreateFromContext(ctx context.Context, tarCtx io.Reader, opti
return err
}
pm.pluginStore.Add(p)
if err := pm.pluginStore.Add(p); err != nil {
return err
}
pm.pluginEventLogger(p.GetID(), repoName, "create")

View file

@ -124,7 +124,7 @@ func (pm *Manager) init() error {
return
}
pm.pluginStore.Add(p)
pm.pluginStore.Update(p)
requiresManualRestore := !pm.liveRestore && p.IsEnabled()
if requiresManualRestore {

View file

@ -26,6 +26,7 @@ func (pm *Manager) enable(p *v2.Plugin, force bool) error {
}
p.Lock()
p.Restart = true
p.ExitChan = make(chan bool)
p.Unlock()
if err := pm.containerdClient.Create(p.GetID(), "", "", specs.Spec(*spec), attachToLog(p.GetID())); err != nil {
return err
@ -92,7 +93,6 @@ func (pm *Manager) Shutdown() {
}
if pm.containerdClient != nil && p.IsEnabled() {
p.Lock()
p.ExitChan = make(chan bool)
p.Restart = false
p.Unlock()
shutdownPlugin(p, pm.containerdClient)

View file

@ -98,12 +98,31 @@ func (ps *Store) SetState(p *v2.Plugin, state bool) {
}
// Add adds a plugin to memory and plugindb.
func (ps *Store) Add(p *v2.Plugin) {
// An error will be returned if there is a collision.
func (ps *Store) Add(p *v2.Plugin) error {
ps.Lock()
defer ps.Unlock()
if v, exist := ps.plugins[p.GetID()]; exist {
return fmt.Errorf("plugin %q has the same ID %s as %q", p.Name(), p.GetID(), v.Name())
}
if _, exist := ps.nameToID[p.Name()]; exist {
return fmt.Errorf("plugin %q already exists", p.Name())
}
ps.plugins[p.GetID()] = p
ps.nameToID[p.Name()] = p.GetID()
ps.updatePluginDB()
return nil
}
// Update updates a plugin to memory and plugindb.
func (ps *Store) Update(p *v2.Plugin) {
ps.Lock()
defer ps.Unlock()
ps.plugins[p.GetID()] = p
ps.nameToID[p.Name()] = p.GetID()
ps.updatePluginDB()
ps.Unlock()
}
// Remove removes a plugin from memory and plugindb.

View file

@ -4,6 +4,7 @@ import (
"fmt"
"net"
"os"
"runtime"
"strings"
fopts "github.com/docker/docker/opts"
@ -45,6 +46,12 @@ func ValidateEnv(val string) (string, error) {
func doesEnvExist(name string) bool {
for _, entry := range os.Environ() {
parts := strings.SplitN(entry, "=", 2)
if runtime.GOOS == "windows" {
// Environment variable are case-insensitive on Windows. PaTh, path and PATH are equivalent.
if strings.EqualFold(parts[0], name) {
return true
}
}
if parts[0] == name {
return true
}

View file

@ -3,6 +3,7 @@ package opts
import (
"fmt"
"os"
"runtime"
"strings"
"testing"
)
@ -50,6 +51,10 @@ func TestValidateEnv(t *testing.T) {
" some space before": " some space before",
"some space after ": "some space after ",
}
// Environment variables are case in-sensitive on Windows
if runtime.GOOS == "windows" {
valids["PaTh"] = fmt.Sprintf("PaTh=%v", os.Getenv("PATH"))
}
for value, expected := range valids {
actual, err := ValidateEnv(value)
if err != nil {

View file

@ -6,7 +6,4 @@ import "regexp"
const RestrictedNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.-]`
// RestrictedNamePattern is a regular expression to validate names against the collection of restricted characters.
var RestrictedNamePattern = regexp.MustCompile(`^/?` + RestrictedNameChars + `+$`)
// RestrictedVolumeNamePattern is a regular expression to validate volume names against the collection of restricted characters.
var RestrictedVolumeNamePattern = regexp.MustCompile(`^` + RestrictedNameChars + `+$`)
var RestrictedNamePattern = regexp.MustCompile(`^` + RestrictedNameChars + `+$`)

View file

@ -36,7 +36,7 @@ var (
// volumeNameRegex ensures the name assigned for the volume is valid.
// This name is used to create the bind directory, so we need to avoid characters that
// would make the path to escape the root directory.
volumeNameRegex = utils.RestrictedVolumeNamePattern
volumeNameRegex = utils.RestrictedNamePattern
)
type validationError struct {