Merge pull request #28774 from vieux/1.13.0-rc3-cherrypicks
1.13.0-rc3 cherry-picks
This commit is contained in:
commit
005a5428ee
84 changed files with 871 additions and 300 deletions
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
19
cli/cobra.go
19
cli/cobra.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: " \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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] != '/' {
|
||||
|
|
|
@ -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
21
daemon/workdir.go
Normal 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)
|
||||
}
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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-->
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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 [])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 [])
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>:<name|id></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<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>
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:{}]",
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 + `+$`)
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue