Merge pull request #29866 from vieux/1.13.0-rc5-cherrpicks
1.13.0 rc5 cherrypicks
This commit is contained in:
commit
1afd41e4f7
44 changed files with 626 additions and 356 deletions
127
api/swagger.yaml
127
api/swagger.yaml
|
@ -1497,74 +1497,39 @@ definitions:
|
|||
type: "string"
|
||||
example:
|
||||
Id: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078"
|
||||
Name: "tiborvass/no-remove"
|
||||
Name: "tiborvass/sample-volume-plugin"
|
||||
Tag: "latest"
|
||||
Active: true
|
||||
Config:
|
||||
Mounts:
|
||||
- Name: ""
|
||||
Description: ""
|
||||
Settable: null
|
||||
Source: "/data"
|
||||
Destination: "/data"
|
||||
Type: "bind"
|
||||
Options:
|
||||
- "shared"
|
||||
- "rbind"
|
||||
- Name: ""
|
||||
Description: ""
|
||||
Settable: null
|
||||
Source: null
|
||||
Destination: "/foobar"
|
||||
Type: "tmpfs"
|
||||
Options: null
|
||||
Settings:
|
||||
Env:
|
||||
- "DEBUG=1"
|
||||
- "DEBUG=0"
|
||||
Args: null
|
||||
Devices: null
|
||||
Manifest:
|
||||
ManifestVersion: "v0"
|
||||
Description: "A test plugin for Docker"
|
||||
Config:
|
||||
Description: "A sample volume plugin for Docker"
|
||||
Documentation: "https://docs.docker.com/engine/extend/plugins/"
|
||||
Interface:
|
||||
Types:
|
||||
- "docker.volumedriver/1.0"
|
||||
Socket: "plugins.sock"
|
||||
Entrypoint:
|
||||
- "plugin-no-remove"
|
||||
- "/usr/bin/sample-volume-plugin"
|
||||
- "/data"
|
||||
WorkDir: ""
|
||||
User: {}
|
||||
Network:
|
||||
Type: "host"
|
||||
Capabilities: null
|
||||
Mounts:
|
||||
- Name: ""
|
||||
Description: ""
|
||||
Settable: null
|
||||
Source: "/data"
|
||||
Destination: "/data"
|
||||
Type: "bind"
|
||||
Options:
|
||||
- "shared"
|
||||
- "rbind"
|
||||
- Name: ""
|
||||
Description: ""
|
||||
Settable: null
|
||||
Source: null
|
||||
Destination: "/foobar"
|
||||
Type: "tmpfs"
|
||||
Options: null
|
||||
Devices:
|
||||
- Name: "device"
|
||||
Description: "a host device to mount"
|
||||
Settable: null
|
||||
Path: "/dev/cpu_dma_latency"
|
||||
Type: ""
|
||||
Linux:
|
||||
Capabilities: null
|
||||
DeviceCreation: false
|
||||
Devices: null
|
||||
Mounts: null
|
||||
PropagatedMount: "/data"
|
||||
Env:
|
||||
- Name: "DEBUG"
|
||||
Description: "If set, prints debug messages"
|
||||
Settable: null
|
||||
Value: "1"
|
||||
Value: "0"
|
||||
Args:
|
||||
Name: "args"
|
||||
Description: "command line arguments"
|
||||
|
@ -6366,74 +6331,39 @@ paths:
|
|||
$ref: "#/definitions/Plugin"
|
||||
example:
|
||||
- Id: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078"
|
||||
Name: "tiborvass/no-remove"
|
||||
Name: "tiborvass/sample-volume-plugin"
|
||||
Tag: "latest"
|
||||
Active: true
|
||||
Config:
|
||||
Mounts:
|
||||
- Name: ""
|
||||
Description: ""
|
||||
Settable: null
|
||||
Source: "/data"
|
||||
Destination: "/data"
|
||||
Type: "bind"
|
||||
Options:
|
||||
- "shared"
|
||||
- "rbind"
|
||||
- Name: ""
|
||||
Description: ""
|
||||
Settable: null
|
||||
Source: null
|
||||
Destination: "/foobar"
|
||||
Type: "tmpfs"
|
||||
Options: null
|
||||
Settings:
|
||||
Env:
|
||||
- "DEBUG=1"
|
||||
- "DEBUG=0"
|
||||
Args: null
|
||||
Devices: null
|
||||
Manifest:
|
||||
ManifestVersion: "v0"
|
||||
Description: "A test plugin for Docker"
|
||||
Config:
|
||||
Description: "A sample volume plugin for Docker"
|
||||
Documentation: "https://docs.docker.com/engine/extend/plugins/"
|
||||
Interface:
|
||||
Types:
|
||||
- "docker.volumedriver/1.0"
|
||||
Socket: "plugins.sock"
|
||||
Entrypoint:
|
||||
- "plugin-no-remove"
|
||||
- "/usr/bin/sample-volume-plugin"
|
||||
- "/data"
|
||||
WorkDir: ""
|
||||
User: {}
|
||||
Network:
|
||||
Type: "host"
|
||||
Capabilities: null
|
||||
Mounts:
|
||||
- Name: ""
|
||||
Description: ""
|
||||
Settable: null
|
||||
Source: "/data"
|
||||
Destination: "/data"
|
||||
Type: "bind"
|
||||
Options:
|
||||
- "shared"
|
||||
- "rbind"
|
||||
- Name: ""
|
||||
Description: ""
|
||||
Settable: null
|
||||
Source: null
|
||||
Destination: "/foobar"
|
||||
Type: "tmpfs"
|
||||
Options: null
|
||||
Devices:
|
||||
- Name: "device"
|
||||
Description: "a host device to mount"
|
||||
Settable: null
|
||||
Path: "/dev/cpu_dma_latency"
|
||||
Type: ""
|
||||
Linux:
|
||||
Capabilities: null
|
||||
DeviceCreation: false
|
||||
Devices: null
|
||||
Mounts: null
|
||||
PropagatedMount: "/data"
|
||||
Env:
|
||||
- Name: "DEBUG"
|
||||
Description: "If set, prints debug messages"
|
||||
Settable: null
|
||||
Value: "1"
|
||||
Value: "0"
|
||||
Args:
|
||||
Name: "args"
|
||||
Description: "command line arguments"
|
||||
|
@ -6582,6 +6512,7 @@ paths:
|
|||
required: true
|
||||
type: "string"
|
||||
tags: ["Plugin"]
|
||||
/plugins/{name}:
|
||||
delete:
|
||||
summary: "Remove a plugin"
|
||||
operationId: "PluginDelete"
|
||||
|
|
|
@ -297,17 +297,17 @@ func workdir(b *Builder, args []string, attributes map[string]bool, original str
|
|||
}
|
||||
b.runConfig.Image = b.image
|
||||
|
||||
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})
|
||||
// Actually copy the struct
|
||||
workdirConfig := *b.runConfig
|
||||
workdirConfig.Cmd = strslice.StrSlice(append(getShell(b.runConfig), fmt.Sprintf("#(nop) WORKDIR %s", b.runConfig.WorkingDir)))
|
||||
|
||||
container, err := b.docker.ContainerCreate(types.ContainerCreateConfig{Config: &workdirConfig})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
var opts inspectOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "inspect [OPTIONS] PLUGIN|ID [PLUGIN|ID...]",
|
||||
Use: "inspect [OPTIONS] PLUGIN [PLUGIN...]",
|
||||
Short: "Display detailed information on one or more plugins",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
|
|
@ -2,13 +2,14 @@ package secret
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
runconfigopts "github.com/docker/docker/runconfig/opts"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
|
@ -16,6 +17,7 @@ import (
|
|||
|
||||
type createOptions struct {
|
||||
name string
|
||||
file string
|
||||
labels opts.ListOpts
|
||||
}
|
||||
|
||||
|
@ -26,7 +28,7 @@ func newSecretCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
|
||||
cmd := &cobra.Command{
|
||||
Use: "create [OPTIONS] SECRET",
|
||||
Short: "Create a secret using stdin as content",
|
||||
Short: "Create a secret from a file or STDIN as content",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
createOpts.name = args[0]
|
||||
|
@ -35,6 +37,7 @@ func newSecretCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
}
|
||||
flags := cmd.Flags()
|
||||
flags.VarP(&createOpts.labels, "label", "l", "Secret labels")
|
||||
flags.StringVarP(&createOpts.file, "file", "f", "", "Read from a file or STDIN ('-')")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -43,9 +46,23 @@ func runSecretCreate(dockerCli *command.DockerCli, options createOptions) error
|
|||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
|
||||
secretData, err := ioutil.ReadAll(os.Stdin)
|
||||
if options.file == "" {
|
||||
return fmt.Errorf("Please specify either a file name or STDIN ('-') with --file")
|
||||
}
|
||||
|
||||
var in io.Reader = dockerCli.In()
|
||||
if options.file != "-" {
|
||||
file, err := system.OpenSequential(options.file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
in = file
|
||||
defer file.Close()
|
||||
}
|
||||
|
||||
secretData, err := ioutil.ReadAll(in)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading content from STDIN: %v", err)
|
||||
return fmt.Errorf("Error reading content from %q: %v", options.file, err)
|
||||
}
|
||||
|
||||
spec := swarm.SecretSpec{
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/docker/docker/cli/command/idresolver"
|
||||
|
@ -16,7 +15,6 @@ import (
|
|||
)
|
||||
|
||||
type psOptions struct {
|
||||
all bool
|
||||
filter opts.FilterOpt
|
||||
noTrunc bool
|
||||
namespace string
|
||||
|
@ -36,7 +34,6 @@ func newPsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|||
},
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.all, "all", "a", false, "Display all tasks")
|
||||
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate output")
|
||||
flags.BoolVar(&opts.noResolve, "no-resolve", false, "Do not map IDs to Names")
|
||||
flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided")
|
||||
|
@ -51,10 +48,6 @@ func runPS(dockerCli *command.DockerCli, opts psOptions) error {
|
|||
|
||||
filter := opts.filter.Value()
|
||||
filter.Add("label", labelNamespace+"="+opts.namespace)
|
||||
if !opts.all && !filter.Include("desired-state") {
|
||||
filter.Add("desired-state", string(swarm.TaskStateRunning))
|
||||
filter.Add("desired-state", string(swarm.TaskStateAccepted))
|
||||
}
|
||||
|
||||
tasks, err := client.TaskList(ctx, types.TaskListOptions{Filters: filter})
|
||||
if err != nil {
|
||||
|
|
|
@ -59,11 +59,15 @@ func (commonOpts *CommonOptions) InstallFlags(flags *pflag.FlagSet) {
|
|||
|
||||
// TODO use flag flags.String("identity"}, "i", "", "Path to libtrust key file")
|
||||
|
||||
commonOpts.TLSOptions = &tlsconfig.Options{}
|
||||
commonOpts.TLSOptions = &tlsconfig.Options{
|
||||
CAFile: filepath.Join(dockerCertPath, DefaultCaFile),
|
||||
CertFile: filepath.Join(dockerCertPath, DefaultCertFile),
|
||||
KeyFile: filepath.Join(dockerCertPath, DefaultKeyFile),
|
||||
}
|
||||
tlsOptions := commonOpts.TLSOptions
|
||||
flags.StringVar(&tlsOptions.CAFile, "tlscacert", filepath.Join(dockerCertPath, DefaultCaFile), "Trust certs signed only by this CA")
|
||||
flags.StringVar(&tlsOptions.CertFile, "tlscert", filepath.Join(dockerCertPath, DefaultCertFile), "Path to TLS certificate file")
|
||||
flags.StringVar(&tlsOptions.KeyFile, "tlskey", filepath.Join(dockerCertPath, DefaultKeyFile), "Path to TLS key file")
|
||||
flags.Var(opts.NewQuotedString(&tlsOptions.CAFile), "tlscacert", "Trust certs signed only by this CA")
|
||||
flags.Var(opts.NewQuotedString(&tlsOptions.CertFile), "tlscert", "Path to TLS certificate file")
|
||||
flags.Var(opts.NewQuotedString(&tlsOptions.KeyFile), "tlskey", "Path to TLS key file")
|
||||
|
||||
hostOpt := opts.NewNamedListOptsRef("hosts", &commonOpts.Hosts, opts.ValidateHost)
|
||||
flags.VarP(hostOpt, "host", "H", "Daemon socket(s) to connect to")
|
||||
|
|
42
cli/flags/common_test.go
Normal file
42
cli/flags/common_test.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package flags
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/cliconfig"
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func TestCommonOptionsInstallFlags(t *testing.T) {
|
||||
flags := pflag.NewFlagSet("testing", pflag.ContinueOnError)
|
||||
opts := NewCommonOptions()
|
||||
opts.InstallFlags(flags)
|
||||
|
||||
err := flags.Parse([]string{
|
||||
"--tlscacert=\"/foo/cafile\"",
|
||||
"--tlscert=\"/foo/cert\"",
|
||||
"--tlskey=\"/foo/key\"",
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, opts.TLSOptions.CAFile, "/foo/cafile")
|
||||
assert.Equal(t, opts.TLSOptions.CertFile, "/foo/cert")
|
||||
assert.Equal(t, opts.TLSOptions.KeyFile, "/foo/key")
|
||||
}
|
||||
|
||||
func defaultPath(filename string) string {
|
||||
return filepath.Join(cliconfig.ConfigDir(), filename)
|
||||
}
|
||||
|
||||
func TestCommonOptionsInstallFlagsWithDefaults(t *testing.T) {
|
||||
flags := pflag.NewFlagSet("testing", pflag.ContinueOnError)
|
||||
opts := NewCommonOptions()
|
||||
opts.InstallFlags(flags)
|
||||
|
||||
err := flags.Parse([]string{})
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, opts.TLSOptions.CAFile, defaultPath("ca.pem"))
|
||||
assert.Equal(t, opts.TLSOptions.CertFile, defaultPath("cert.pem"))
|
||||
assert.Equal(t, opts.TLSOptions.KeyFile, defaultPath("key.pem"))
|
||||
}
|
|
@ -22,17 +22,20 @@ func (cli *Client) Events(ctx context.Context, options types.EventsOptions) (<-c
|
|||
messages := make(chan events.Message)
|
||||
errs := make(chan error, 1)
|
||||
|
||||
started := make(chan struct{})
|
||||
go func() {
|
||||
defer close(errs)
|
||||
|
||||
query, err := buildEventsQueryParams(cli.version, options)
|
||||
if err != nil {
|
||||
close(started)
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := cli.get(ctx, "/events", query, nil)
|
||||
if err != nil {
|
||||
close(started)
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
|
@ -40,6 +43,7 @@ func (cli *Client) Events(ctx context.Context, options types.EventsOptions) (<-c
|
|||
|
||||
decoder := json.NewDecoder(resp.body)
|
||||
|
||||
close(started)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
@ -61,6 +65,7 @@ func (cli *Client) Events(ctx context.Context, options types.EventsOptions) (<-c
|
|||
}
|
||||
}
|
||||
}()
|
||||
<-started
|
||||
|
||||
return messages, errs
|
||||
}
|
||||
|
|
|
@ -3139,7 +3139,7 @@ _docker_node_ps() {
|
|||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--all -a --filter -f --help --no-resolve --no-trunc" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "--filter -f --help --no-resolve --no-trunc" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
__docker_complete_nodes_plus_self
|
||||
|
@ -3223,10 +3223,13 @@ _docker_plugin_create() {
|
|||
_docker_plugin_disable() {
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "--force -f --help" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
__docker_complete_plugins_installed
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_complete_plugins_installed
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
@ -3243,7 +3246,10 @@ _docker_plugin_enable() {
|
|||
COMPREPLY=( $( compgen -W "--help --timeout" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
__docker_complete_plugins_installed
|
||||
local counter=$(__docker_pos_first_nonflag '--timeout')
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_complete_plugins_installed
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
@ -3266,9 +3272,15 @@ _docker_plugin_inspect() {
|
|||
}
|
||||
|
||||
_docker_plugin_install() {
|
||||
case "$prev" in
|
||||
--alias)
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--disable --grant-all-permissions--help" -- "$cur" ) )
|
||||
COMPREPLY=( $( compgen -W "--alias --disable --grant-all-permissions --help" -- "$cur" ) )
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
@ -3955,7 +3967,6 @@ _docker() {
|
|||
container
|
||||
cp
|
||||
create
|
||||
daemon
|
||||
diff
|
||||
events
|
||||
exec
|
||||
|
|
|
@ -65,11 +65,10 @@ func merge(userConf, imageConf *containertypes.Config) error {
|
|||
if userConf.Labels == nil {
|
||||
userConf.Labels = map[string]string{}
|
||||
}
|
||||
if imageConf.Labels != nil {
|
||||
for l := range userConf.Labels {
|
||||
imageConf.Labels[l] = userConf.Labels[l]
|
||||
for l, v := range imageConf.Labels {
|
||||
if _, ok := userConf.Labels[l]; !ok {
|
||||
userConf.Labels[l] = v
|
||||
}
|
||||
userConf.Labels = imageConf.Labels
|
||||
}
|
||||
|
||||
if len(userConf.Entrypoint) == 0 {
|
||||
|
|
|
@ -67,7 +67,9 @@ func (daemon *Daemon) update(name string, hostConfig *container.HostConfig) erro
|
|||
}
|
||||
|
||||
// if Restart Policy changed, we need to update container monitor
|
||||
container.UpdateMonitor(hostConfig.RestartPolicy)
|
||||
if hostConfig.RestartPolicy.Name != "" {
|
||||
container.UpdateMonitor(hostConfig.RestartPolicy)
|
||||
}
|
||||
|
||||
// If container is not running, update hostConfig struct is enough,
|
||||
// resources will be updated when the container is started again.
|
||||
|
|
|
@ -171,52 +171,49 @@ Config provides the base accessible fields for working with V0 plugin format
|
|||
|
||||
## Example Config
|
||||
|
||||
*Example showing the 'tiborvass/no-remove' plugin config.*
|
||||
*Example showing the 'tiborvass/sample-volume-plugin' 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"
|
||||
}
|
||||
],
|
||||
"linux": {
|
||||
"devices": [
|
||||
{
|
||||
"name": "device",
|
||||
"description": "a host device to mount",
|
||||
"path": "/dev/cpu_dma_latency"
|
||||
}
|
||||
]
|
||||
}
|
||||
"Args": {
|
||||
"Description": "",
|
||||
"Name": "",
|
||||
"Settable": null,
|
||||
"Value": null
|
||||
},
|
||||
"Description": "A sample volume plugin for Docker",
|
||||
"Documentation": "https://docs.docker.com/engine/extend/plugins/",
|
||||
"Entrypoint": [
|
||||
"/usr/bin/sample-volume-plugin",
|
||||
"/data"
|
||||
],
|
||||
"Env": [
|
||||
{
|
||||
"Description": "",
|
||||
"Name": "DEBUG",
|
||||
"Settable": [
|
||||
"value"
|
||||
],
|
||||
"Value": "0"
|
||||
}
|
||||
],
|
||||
"Interface": {
|
||||
"Socket": "plugin.sock",
|
||||
"Types": [
|
||||
"docker.volumedriver/1.0"
|
||||
]
|
||||
},
|
||||
"Linux": {
|
||||
"Capabilities": null,
|
||||
"DeviceCreation": false,
|
||||
"Devices": null
|
||||
},
|
||||
"Mounts": null,
|
||||
"Network": {
|
||||
"Type": ""
|
||||
},
|
||||
"PropagatedMount": "/data",
|
||||
"User": {},
|
||||
"Workdir": ""
|
||||
}
|
||||
```
|
||||
|
|
|
@ -69,8 +69,8 @@ enabled, and use it to create a volume.
|
|||
```bash
|
||||
$ docker plugin ls
|
||||
|
||||
NAME TAG ENABLED
|
||||
vieux/sshfs latest true
|
||||
ID NAME TAG DESCRIPTION ENABLED
|
||||
69553ca1d789 vieux/sshfs latest the `sshfs` plugin true
|
||||
```
|
||||
|
||||
3. Create a volume using the plugin.
|
||||
|
|
|
@ -684,16 +684,18 @@ configuration file or using the `--add-runtime` command line argument.
|
|||
The following is an example adding 2 runtimes via the configuration:
|
||||
|
||||
```json
|
||||
"default-runtime": "runc",
|
||||
"runtimes": {
|
||||
"runc": {
|
||||
"path": "runc"
|
||||
},
|
||||
"custom": {
|
||||
"path": "/usr/local/bin/my-runc-replacement",
|
||||
"runtimeArgs": [
|
||||
"--debug"
|
||||
]
|
||||
{
|
||||
"default-runtime": "runc",
|
||||
"runtimes": {
|
||||
"runc": {
|
||||
"path": "runc"
|
||||
},
|
||||
"custom": {
|
||||
"path": "/usr/local/bin/my-runc-replacement",
|
||||
"runtimeArgs": [
|
||||
"--debug"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -194,8 +194,8 @@ relative to the current time on the client machine:
|
|||
2015-12-23T21:38:25.119625123Z network connect 8b111217944ba0ba844a65b13efcd57dc494932ee2527577758f939315ba2c5b (name=test-event-network-local, container=b4be644031a3d90b400f88ab3d4bdf4dc23adb250e696b6328b85441abe2c54e, type=bridge)
|
||||
|
||||
$ docker events --filter 'type=plugin' (experimental)
|
||||
2016-07-25T17:30:14.825557616Z plugin pull ec7b87f2ce84330fe076e666f17dfc049d2d7ae0b8190763de94e1f2d105993f (name=tiborvass/no-remove:latest)
|
||||
2016-07-25T17:30:14.888127370Z plugin enable ec7b87f2ce84330fe076e666f17dfc049d2d7ae0b8190763de94e1f2d105993f (name=tiborvass/no-remove:latest)
|
||||
2016-07-25T17:30:14.825557616Z plugin pull ec7b87f2ce84330fe076e666f17dfc049d2d7ae0b8190763de94e1f2d105993f (name=tiborvass/sample-volume-plugin:latest)
|
||||
2016-07-25T17:30:14.888127370Z plugin enable ec7b87f2ce84330fe076e666f17dfc049d2d7ae0b8190763de94e1f2d105993f (name=tiborvass/sample-volume-plugin:latest)
|
||||
|
||||
**Format:**
|
||||
|
||||
|
|
|
@ -106,6 +106,7 @@ read the [`dockerd`](dockerd.md) reference page.
|
|||
| [volume create](volume_create.md) | Creates a new volume where containers can consume and store data |
|
||||
| [volume inspect](volume_inspect.md) | Display information about a volume |
|
||||
| [volume ls](volume_ls.md) | Lists all the volumes Docker knows about |
|
||||
| [volume prune](volume_prune.md) | Remove all unused volumes |
|
||||
| [volume rm](volume_rm.md) | Remove one or more volumes |
|
||||
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ Usage: docker node ps [OPTIONS] [NODE...]
|
|||
List tasks running on one or more nodes, defaults to current node.
|
||||
|
||||
Options:
|
||||
-a, --all Display all instances
|
||||
-f, --filter value Filter output based on conditions provided
|
||||
--help Print usage
|
||||
--no-resolve Do not map IDs to Names
|
||||
|
|
|
@ -30,27 +30,27 @@ see [`docker plugin install`](plugin_install.md). Without the `-f` option,
|
|||
a plugin that has references (eg, volumes, networks) cannot be disabled.
|
||||
|
||||
|
||||
The following example shows that the `no-remove` plugin is installed
|
||||
The following example shows that the `sample-volume-plugin` plugin is installed
|
||||
and enabled:
|
||||
|
||||
```bash
|
||||
$ docker plugin ls
|
||||
|
||||
NAME TAG DESCRIPTION ENABLED
|
||||
tiborvass/no-remove latest A test plugin for Docker true
|
||||
ID NAME TAG DESCRIPTION ENABLED
|
||||
69553ca1d123 tiborvass/sample-volume-plugin latest A test plugin for Docker true
|
||||
```
|
||||
|
||||
To disable the plugin, use the following command:
|
||||
|
||||
```bash
|
||||
$ docker plugin disable tiborvass/no-remove
|
||||
$ docker plugin disable tiborvass/sample-volume-plugin
|
||||
|
||||
tiborvass/no-remove
|
||||
tiborvass/sample-volume-plugin
|
||||
|
||||
$ docker plugin ls
|
||||
|
||||
NAME TAG DESCRIPTION ENABLED
|
||||
tiborvass/no-remove latest A test plugin for Docker false
|
||||
ID NAME TAG DESCRIPTION ENABLED
|
||||
69553ca1d123 tiborvass/sample-volume-plugin latest A test plugin for Docker false
|
||||
```
|
||||
|
||||
## Related information
|
||||
|
|
|
@ -29,27 +29,27 @@ Enables a plugin. The plugin must be installed before it can be enabled,
|
|||
see [`docker plugin install`](plugin_install.md).
|
||||
|
||||
|
||||
The following example shows that the `no-remove` plugin is installed,
|
||||
The following example shows that the `sample-volume-plugin` plugin is installed,
|
||||
but disabled:
|
||||
|
||||
```bash
|
||||
$ docker plugin ls
|
||||
|
||||
NAME TAG DESCRIPTION ENABLED
|
||||
tiborvass/no-remove latest A test plugin for Docker false
|
||||
ID NAME TAG DESCRIPTION ENABLED
|
||||
69553ca1d123 tiborvass/sample-volume-plugin latest A test plugin for Docker false
|
||||
```
|
||||
|
||||
To enable the plugin, use the following command:
|
||||
|
||||
```bash
|
||||
$ docker plugin enable tiborvass/no-remove
|
||||
$ docker plugin enable tiborvass/sample-volume-plugin
|
||||
|
||||
tiborvass/no-remove
|
||||
tiborvass/sample-volume-plugin
|
||||
|
||||
$ docker plugin ls
|
||||
|
||||
NAME TAG DESCRIPTION ENABLED
|
||||
tiborvass/no-remove latest A test plugin for Docker true
|
||||
ID NAME TAG DESCRIPTION ENABLED
|
||||
69553ca1d123 tiborvass/sample-volume-plugin latest A test plugin for Docker true
|
||||
```
|
||||
|
||||
## Related information
|
||||
|
|
|
@ -16,7 +16,7 @@ keywords: "plugin, inspect"
|
|||
# plugin inspect
|
||||
|
||||
```markdown
|
||||
Usage: docker plugin inspect [OPTIONS] PLUGIN|ID [PLUGIN|ID...]
|
||||
Usage: docker plugin inspect [OPTIONS] PLUGIN [PLUGIN...]
|
||||
|
||||
Display detailed information on one or more plugins
|
||||
|
||||
|
@ -31,12 +31,12 @@ in a JSON array.
|
|||
Example output:
|
||||
|
||||
```bash
|
||||
$ docker plugin inspect tiborvass/no-remove:latest
|
||||
$ docker plugin inspect tiborvass/sample-volume-plugin:latest
|
||||
```
|
||||
```JSON
|
||||
{
|
||||
"Id": "8c74c978c434745c3ade82f1bc0acf38d04990eaf494fa507c16d9f1daa99c21",
|
||||
"Name": "tiborvass/no-remove:latest",
|
||||
"Name": "tiborvass/sample-volume-plugin:latest",
|
||||
"Enabled": true,
|
||||
"Config": {
|
||||
"Mounts": [
|
||||
|
@ -79,7 +79,7 @@ $ docker plugin inspect tiborvass/no-remove:latest
|
|||
"Socket": "plugins.sock"
|
||||
},
|
||||
"Entrypoint": [
|
||||
"plugin-no-remove",
|
||||
"plugin-sample-volume-plugin",
|
||||
"/data"
|
||||
],
|
||||
"Workdir": "",
|
||||
|
@ -143,7 +143,7 @@ $ docker plugin inspect tiborvass/no-remove:latest
|
|||
|
||||
|
||||
```bash
|
||||
$ docker plugin inspect -f '{{.Id}}' tiborvass/no-remove:latest
|
||||
$ docker plugin inspect -f '{{.Id}}' tiborvass/sample-volume-plugin:latest
|
||||
```
|
||||
```
|
||||
8c74c978c434745c3ade82f1bc0acf38d04990eaf494fa507c16d9f1daa99c21
|
||||
|
|
|
@ -33,20 +33,20 @@ the registry. Note that the minimum required registry version to distribute
|
|||
plugins is 2.3.0
|
||||
|
||||
|
||||
The following example installs `no-remove` plugin and [set](plugin_set.md) it's env variable
|
||||
The following example installs `vieus/sshfs` plugin and [set](plugin_set.md) it's env variable
|
||||
`DEBUG` to 1. Install consists of pulling the plugin from Docker Hub, prompting
|
||||
the user to accept the list of privileges that the plugin needs, settings parameters
|
||||
and enabling the plugin.
|
||||
|
||||
```bash
|
||||
$ docker plugin install tiborvass/no-remove DEBUG=1
|
||||
$ docker plugin install vieux/sshfs DEBUG=1
|
||||
|
||||
Plugin "tiborvass/no-remove" is requesting the following privileges:
|
||||
Plugin "vieux/sshfs" is requesting the following privileges:
|
||||
- network: [host]
|
||||
- mount: [/data]
|
||||
- device: [/dev/cpu_dma_latency]
|
||||
- device: [/dev/fuse]
|
||||
- capabilities: [CAP_SYS_ADMIN]
|
||||
Do you grant the above permissions? [y/N] y
|
||||
tiborvass/no-remove
|
||||
vieux/sshfs
|
||||
```
|
||||
|
||||
After the plugin is installed, it appears in the list of plugins:
|
||||
|
@ -54,8 +54,8 @@ After the plugin is installed, it appears in the list of plugins:
|
|||
```bash
|
||||
$ docker plugin ls
|
||||
|
||||
NAME TAG DESCRIPTION ENABLED
|
||||
tiborvass/no-remove latest A test plugin for Docker true
|
||||
ID NAME TAG DESCRIPTION ENABLED
|
||||
69553ca1d123 vieux/sshfs latest sshFS plugin for Docker true
|
||||
```
|
||||
|
||||
## Related information
|
||||
|
|
|
@ -36,8 +36,8 @@ Example output:
|
|||
```bash
|
||||
$ docker plugin ls
|
||||
|
||||
ID NAME TAG DESCRIPTION ENABLED
|
||||
69553ca1d123 tiborvass/no-remove latest A test plugin for Docker true
|
||||
ID NAME TAG DESCRIPTION ENABLED
|
||||
69553ca1d123 tiborvass/sample-volume-plugin latest A test plugin for Docker true
|
||||
```
|
||||
|
||||
## Related information
|
||||
|
|
|
@ -32,9 +32,8 @@ The following example shows how to push a sample `user/plugin`.
|
|||
```bash
|
||||
|
||||
$ docker plugin ls
|
||||
NAME TAG DESCRIPTION ENABLED
|
||||
user/plugin latest A sample plugin for Docker false
|
||||
|
||||
ID NAME TAG DESCRIPTION ENABLED
|
||||
69553ca1d456 user/plugin latest A sample plugin for Docker false
|
||||
$ docker plugin push user/plugin
|
||||
```
|
||||
|
||||
|
|
|
@ -33,14 +33,14 @@ a plugin using the [`docker plugin disable`](plugin_disable.md) before removing
|
|||
it (or use --force, use of force is not recommended, since it can affect
|
||||
functioning of running containers using the plugin).
|
||||
|
||||
The following example disables and removes the `no-remove:latest` plugin;
|
||||
The following example disables and removes the `sample-volume-plugin:latest` plugin;
|
||||
|
||||
```bash
|
||||
$ docker plugin disable tiborvass/no-remove
|
||||
tiborvass/no-remove
|
||||
$ docker plugin disable tiborvass/sample-volume-plugin
|
||||
tiborvass/sample-volume-plugin
|
||||
|
||||
$ docker plugin rm tiborvass/no-remove:latest
|
||||
tiborvass/no-remove
|
||||
$ docker plugin rm tiborvass/sample-volume-plugin:latest
|
||||
tiborvass/sample-volume-plugin
|
||||
```
|
||||
|
||||
## Related information
|
||||
|
|
|
@ -33,15 +33,15 @@ The settings currently supported are:
|
|||
* args
|
||||
|
||||
The following example change the env variable `DEBUG` on the
|
||||
`no-remove` plugin.
|
||||
`sample-volume-plugin` plugin.
|
||||
|
||||
```bash
|
||||
$ docker plugin inspect -f {{.Settings.Env}} tiborvass/no-remove
|
||||
$ docker plugin inspect -f {{.Settings.Env}} tiborvass/sample-volume-plugin
|
||||
[DEBUG=0]
|
||||
|
||||
$ docker plugin set tiborvass/no-remove DEBUG=1
|
||||
$ docker plugin set tiborvass/sample-volume-plugin DEBUG=1
|
||||
|
||||
$ docker plugin inspect -f {{.Settings.Env}} tiborvass/no-remove
|
||||
$ docker plugin inspect -f {{.Settings.Env}} tiborvass/sample-volume-plugin
|
||||
[DEBUG=1]
|
||||
```
|
||||
|
||||
|
|
|
@ -16,15 +16,17 @@ keywords: ["secret, create"]
|
|||
# secret create
|
||||
|
||||
```Markdown
|
||||
Usage: docker secret create [OPTIONS] SECRET
|
||||
Usage: docker secret create [OPTIONS] SECRET
|
||||
|
||||
Create a secret from a file or STDIN as content
|
||||
|
||||
Create a secret using stdin as content
|
||||
Options:
|
||||
--help Print usage
|
||||
-l, --label list Secret labels (default [])
|
||||
-f, --file string Read from a file or STDIN ('-')
|
||||
--help Print usage
|
||||
-l, --label list Secret labels (default [])
|
||||
```
|
||||
|
||||
Creates a secret using standard input for the secret content. You must run this
|
||||
Creates a secret using standard input or from a file for the secret content. You must run this
|
||||
command on a manager node.
|
||||
|
||||
## Examples
|
||||
|
@ -32,7 +34,18 @@ command on a manager node.
|
|||
### Create a secret
|
||||
|
||||
```bash
|
||||
$ cat secret.json | docker secret create secret.json
|
||||
$ cat secret.json | docker secret create -f - secret.json
|
||||
mhv17xfe3gh6xc4rij5orpfds
|
||||
|
||||
$ docker secret ls
|
||||
ID NAME CREATED UPDATED SIZE
|
||||
mhv17xfe3gh6xc4rij5orpfds secret.json 2016-10-27 23:25:43.909181089 +0000 UTC 2016-10-27 23:25:43.909181089 +0000 UTC 1679
|
||||
```
|
||||
|
||||
### Create a secret with a file
|
||||
|
||||
```bash
|
||||
$ docker secret create --file secret.in secret.json
|
||||
mhv17xfe3gh6xc4rij5orpfds
|
||||
|
||||
$ docker secret ls
|
||||
|
@ -43,7 +56,7 @@ mhv17xfe3gh6xc4rij5orpfds secret.json 2016-10-27 23:25:43.90918108
|
|||
### Create a secret with labels
|
||||
|
||||
```bash
|
||||
$ cat secret.json | docker secret create secret.json --label env=dev --label rev=20161102
|
||||
$ cat secret.json | docker secret create secret.json -f - --label env=dev --label rev=20161102
|
||||
jtn7g6aukl5ky7nr9gvwafoxh
|
||||
|
||||
$ docker secret inspect secret.json
|
||||
|
|
|
@ -21,10 +21,10 @@ Usage: docker stack ps [OPTIONS] STACK
|
|||
List the tasks in the stack
|
||||
|
||||
Options:
|
||||
-a, --all Display all tasks
|
||||
-f, --filter value Filter output based on conditions provided
|
||||
--no-resolve Do not map IDs to Names
|
||||
--no-trunc Do not truncate output
|
||||
-f, --filter filter Filter output based on conditions provided
|
||||
--help Print usage
|
||||
--no-resolve Do not map IDs to Names
|
||||
--no-trunc Do not truncate output
|
||||
```
|
||||
|
||||
Lists the tasks that are running as part of the specified stack. This
|
||||
|
|
|
@ -9,65 +9,26 @@ issues associated with it. If necessary, links are provided to additional
|
|||
documentation on an issue. As an active Docker user and community member,
|
||||
please feel free to provide any feedback on these features you wish.
|
||||
|
||||
## Install Docker experimental
|
||||
## Use Docker experimental
|
||||
|
||||
Unlike the regular Docker binary, the experimental channels is built and
|
||||
updated nightly on https://experimental.docker.com. From one day to the
|
||||
next, new features may appear, while existing experimental features may be
|
||||
refined or entirely removed.
|
||||
Experimental features are now included in the standard Docker binaries as of
|
||||
version 1.13.0.
|
||||
For enabling experimental features, you need to start the Docker daemon with
|
||||
`--experimental` flag.
|
||||
You can also enable the daemon flag via `/etc/docker/daemon.json`. e.g.
|
||||
|
||||
1. Verify that you have `curl` installed.
|
||||
```json
|
||||
{
|
||||
"experimental": true
|
||||
}
|
||||
```
|
||||
|
||||
$ which curl
|
||||
Then make sure the experimental flag is enabled:
|
||||
|
||||
If `curl` isn't installed, install it after updating your manager:
|
||||
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install curl
|
||||
|
||||
2. Get the latest Docker package.
|
||||
|
||||
$ curl -sSL https://experimental.docker.com/ | sh
|
||||
|
||||
The system prompts you for your `sudo` password. Then, it downloads and
|
||||
installs Docker and its dependencies.
|
||||
|
||||
>**Note**: If your company is behind a filtering proxy, you may find that the
|
||||
>`apt-key`
|
||||
>command fails for the Docker repo during installation. To work around this,
|
||||
>add the key directly using the following:
|
||||
>
|
||||
> $ curl -sSL https://experimental.docker.com/gpg | sudo apt-key add -
|
||||
|
||||
3. Verify `docker` is installed correctly.
|
||||
|
||||
$ sudo docker run hello-world
|
||||
|
||||
This command downloads a test image and runs it in a container.
|
||||
|
||||
### Get the Linux binary
|
||||
To download the latest experimental `docker` binary for Linux,
|
||||
use the following URLs:
|
||||
|
||||
https://experimental.docker.com/builds/Linux/i386/docker-latest.tgz
|
||||
|
||||
https://experimental.docker.com/builds/Linux/x86_64/docker-latest.tgz
|
||||
|
||||
After downloading the appropriate binary, you can follow the instructions
|
||||
[here](https://docs.docker.com/engine/installation/binaries/#/get-the-docker-engine-binaries) to run the `docker` daemon.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> 1) You can get the MD5 and SHA256 hashes by appending .md5 and .sha256 to the URLs respectively
|
||||
>
|
||||
> 2) You can get the compressed binaries by appending .tgz to the URLs
|
||||
|
||||
### Build an experimental binary
|
||||
You can also build the experimental binary from the standard development environment by adding
|
||||
`DOCKER_EXPERIMENTAL=1` to the environment where you run `make` to build Docker binaries. For example,
|
||||
to build a Docker binary with the experimental features enabled:
|
||||
|
||||
$ DOCKER_EXPERIMENTAL=1 make binary
|
||||
```bash
|
||||
$ docker version -f '{{.Server.Experimental}}'
|
||||
true
|
||||
```
|
||||
|
||||
## Current experimental features
|
||||
|
||||
|
|
|
@ -184,7 +184,7 @@ Function Validate-DCO($headCommit, $upstreamCommit) {
|
|||
$usernameRegex='[a-zA-Z0-9][a-zA-Z0-9-]+'
|
||||
|
||||
$dcoPrefix="Signed-off-by:"
|
||||
$dcoRegex="^(Docker-DCO-1.1-)?$dcoPrefix ([^<]+) <([^<>@]+@[^<>]+)>( \\(github: ($githubUsernameRegex)\\))?$"
|
||||
$dcoRegex="^(Docker-DCO-1.1-)?$dcoPrefix ([^<]+) <([^<>@]+@[^<>]+)>( \\(github: ($usernameRegex)\\))?$"
|
||||
|
||||
$counts = Invoke-Expression "git diff --numstat $upstreamCommit...$headCommit"
|
||||
if ($LASTEXITCODE -ne 0) { Throw "Failed git diff --numstat" }
|
||||
|
|
|
@ -7357,3 +7357,43 @@ func (s *DockerSuite) TestBuildWindowsEnvCaseInsensitive(c *check.C) {
|
|||
c.Fatalf("Case insensitive environment variables on Windows failed. Got %s", res)
|
||||
}
|
||||
}
|
||||
|
||||
// Test case for 29667
|
||||
func (s *DockerSuite) TestBuildWorkdirImageCmd(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
|
||||
image := "testworkdirimagecmd"
|
||||
dockerfile := `
|
||||
FROM busybox
|
||||
WORKDIR /foo/bar
|
||||
`
|
||||
out, err := buildImage(image, dockerfile, true)
|
||||
c.Assert(err, checker.IsNil, check.Commentf("Output: %s", out))
|
||||
|
||||
out, _ = dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", image)
|
||||
c.Assert(strings.TrimSpace(out), checker.Equals, `["sh"]`)
|
||||
|
||||
image = "testworkdirlabelimagecmd"
|
||||
dockerfile = `
|
||||
FROM busybox
|
||||
WORKDIR /foo/bar
|
||||
LABEL a=b
|
||||
`
|
||||
out, err = buildImage(image, dockerfile, true)
|
||||
c.Assert(err, checker.IsNil, check.Commentf("Output: %s", out))
|
||||
|
||||
out, _ = dockerCmd(c, "inspect", "--format", "{{ json .Config.Cmd }}", image)
|
||||
c.Assert(strings.TrimSpace(out), checker.Equals, `["sh"]`)
|
||||
}
|
||||
|
||||
// Test case for 28902/28090
|
||||
func (s *DockerSuite) TestBuildWorkdirCmd(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
|
||||
dockerFile := `
|
||||
FROM golang:1.7-alpine
|
||||
WORKDIR /
|
||||
`
|
||||
_, err := buildImage("testbuildworkdircmd", dockerFile, false)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
|
|
@ -142,3 +142,16 @@ func (s *DockerSuite) TestCommitChange(c *check.C) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestCommitChangeLabels(c *check.C) {
|
||||
dockerCmd(c, "run", "--name", "test", "--label", "some=label", "busybox", "true")
|
||||
|
||||
imageID, _ := dockerCmd(c, "commit",
|
||||
"--change", "LABEL some=label2",
|
||||
"test", "test-commit")
|
||||
imageID = strings.TrimSpace(imageID)
|
||||
|
||||
c.Assert(inspectField(c, imageID, "Config.Labels"), checker.Equals, "map[some:label2]")
|
||||
// check that container labels didn't change
|
||||
c.Assert(inspectField(c, "test", "Config.Labels"), checker.Equals, "map[some:label]")
|
||||
}
|
||||
|
|
|
@ -54,6 +54,10 @@ func (s *DockerExternalGraphdriverSuite) SetUpTest(c *check.C) {
|
|||
s.d = NewDaemon(c)
|
||||
}
|
||||
|
||||
func (s *DockerExternalGraphdriverSuite) OnTimeout(c *check.C) {
|
||||
s.d.DumpStackAndQuit()
|
||||
}
|
||||
|
||||
func (s *DockerExternalGraphdriverSuite) TearDownTest(c *check.C) {
|
||||
s.d.Stop()
|
||||
s.ds.TearDownTest(c)
|
||||
|
|
|
@ -456,3 +456,11 @@ func (s *DockerSuite) TestInspectUnknownObject(c *check.C) {
|
|||
c.Assert(out, checker.Contains, "Error: No such object: foobar")
|
||||
c.Assert(err.Error(), checker.Contains, "Error: No such object: foobar")
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestInpectInvalidReference(c *check.C) {
|
||||
// This test should work on both Windows and Linux
|
||||
out, _, err := dockerCmdWithError("inspect", "FooBar")
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(out, checker.Contains, "Error: No such object: FooBar")
|
||||
c.Assert(err.Error(), checker.Contains, "Error: No such object: FooBar")
|
||||
}
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
"github.com/go-check/check"
|
||||
|
@ -104,3 +108,33 @@ func (s *DockerSwarmSuite) TestSecretCreateResolve(c *check.C) {
|
|||
c.Assert(out, checker.Not(checker.Contains), id)
|
||||
c.Assert(out, checker.Not(checker.Contains), fake)
|
||||
}
|
||||
|
||||
func (s *DockerSwarmSuite) TestSecretCreateWithFile(c *check.C) {
|
||||
d := s.AddDaemon(c, true, true)
|
||||
|
||||
testFile, err := ioutil.TempFile("", "secretCreateTest")
|
||||
c.Assert(err, checker.IsNil, check.Commentf("failed to create temporary file"))
|
||||
defer os.Remove(testFile.Name())
|
||||
|
||||
testData := "TESTINGDATA"
|
||||
_, err = testFile.Write([]byte(testData))
|
||||
c.Assert(err, checker.IsNil, check.Commentf("failed to write to temporary file"))
|
||||
|
||||
testName := "test_secret"
|
||||
out, err := d.Cmd("secret", "create", "--file", testFile.Name(), testName)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "", check.Commentf(out))
|
||||
|
||||
id := strings.TrimSpace(out)
|
||||
secret := d.getSecret(c, id)
|
||||
c.Assert(secret.Spec.Name, checker.Equals, testName)
|
||||
|
||||
testName = "test_secret_2"
|
||||
out, err = d.Cmd("secret", "create", testName, "-f", testFile.Name())
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "", check.Commentf(out))
|
||||
|
||||
id = strings.TrimSpace(out)
|
||||
secret = d.getSecret(c, id)
|
||||
c.Assert(secret.Spec.Name, checker.Equals, testName)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,10 @@ package main
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/kr/pty"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
|
@ -250,3 +253,31 @@ func (s *DockerSuite) TestUpdateMemoryWithSwapMemory(c *check.C) {
|
|||
|
||||
dockerCmd(c, "update", "--memory", "800M", "--memory-swap", "1000M", name)
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestUpdateNotAffectMonitorRestartPolicy(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux, cpuShare)
|
||||
|
||||
out, _ := dockerCmd(c, "run", "-tid", "--restart=always", "busybox", "sh")
|
||||
id := strings.TrimSpace(string(out))
|
||||
dockerCmd(c, "update", "--cpu-shares", "512", id)
|
||||
|
||||
cpty, tty, err := pty.Open()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cpty.Close()
|
||||
|
||||
cmd := exec.Command(dockerBinary, "attach", id)
|
||||
cmd.Stdin = tty
|
||||
|
||||
c.Assert(cmd.Start(), checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
_, err = cpty.Write([]byte("exit\n"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
c.Assert(cmd.Wait(), checker.IsNil)
|
||||
|
||||
// container should restart again and keep running
|
||||
err = waitInspect(id, "{{.RestartCount}}", "1", 30*time.Second)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(waitRun(id), checker.IsNil)
|
||||
}
|
||||
|
|
|
@ -168,8 +168,8 @@ Lines. For information about JSON Lines, please refer to http://jsonlines.org/ .
|
|||
2015-12-23T21:38:25.119625123Z network connect 8b111217944ba0ba844a65b13efcd57dc494932ee2527577758f939315ba2c5b (name=test-event-network-local, container=b4be644031a3d90b400f88ab3d4bdf4dc23adb250e696b6328b85441abe2c54e, type=bridge)
|
||||
|
||||
$ docker events --filter 'type=plugin' (experimental)
|
||||
2016-07-25T17:30:14.825557616Z plugin pull ec7b87f2ce84330fe076e666f17dfc049d2d7ae0b8190763de94e1f2d105993f (name=tiborvass/no-remove:latest)
|
||||
2016-07-25T17:30:14.888127370Z plugin enable ec7b87f2ce84330fe076e666f17dfc049d2d7ae0b8190763de94e1f2d105993f (name=tiborvass/no-remove:latest)
|
||||
2016-07-25T17:30:14.825557616Z plugin pull ec7b87f2ce84330fe076e666f17dfc049d2d7ae0b8190763de94e1f2d105993f (name=tiborvass/sample-volume-plugin:latest)
|
||||
2016-07-25T17:30:14.888127370Z plugin enable ec7b87f2ce84330fe076e666f17dfc049d2d7ae0b8190763de94e1f2d105993f (name=tiborvass/sample-volume-plugin:latest)
|
||||
|
||||
|
||||
# HISTORY
|
||||
|
|
|
@ -20,6 +20,7 @@ dockerd - Enable daemon mode
|
|||
[**-D**|**--debug**]
|
||||
[**--default-gateway**[=*DEFAULT-GATEWAY*]]
|
||||
[**--default-gateway-v6**[=*DEFAULT-GATEWAY-V6*]]
|
||||
[**--default-runtime**[=*runc*]]
|
||||
[**--default-ulimit**[=*[]*]]
|
||||
[**--disable-legacy-registry**]
|
||||
[**--dns**[=*[]*]]
|
||||
|
@ -84,7 +85,35 @@ following format.
|
|||
# OPTIONS
|
||||
|
||||
**--add-runtime**=[]
|
||||
Set additional OCI compatible runtime.
|
||||
Runtimes can be registered with the daemon either via the
|
||||
configuration file or using the `--add-runtime` command line argument.
|
||||
|
||||
The following is an example adding 2 runtimes via the configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"default-runtime": "runc",
|
||||
"runtimes": {
|
||||
"runc": {
|
||||
"path": "runc"
|
||||
},
|
||||
"custom": {
|
||||
"path": "/usr/local/bin/my-runc-replacement",
|
||||
"runtimeArgs": [
|
||||
"--debug"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is the same example via the command line:
|
||||
|
||||
```bash
|
||||
$ sudo dockerd --add-runtime runc=runc --add-runtime custom=/usr/local/bin/my-runc-replacement
|
||||
```
|
||||
|
||||
**Note**: defining runtime arguments via the command line is not supported.
|
||||
|
||||
**--api-cors-header**=""
|
||||
Set CORS headers in the Engine API. Default is cors disabled. Give urls like
|
||||
|
@ -132,6 +161,9 @@ following format.
|
|||
**--default-gateway-v6**=""
|
||||
IPv6 address of the container default gateway
|
||||
|
||||
**--default-runtime**="runc"
|
||||
Set default runtime if there're more than one specified by `--add-runtime`.
|
||||
|
||||
**--default-ulimit**=[]
|
||||
Default ulimits for containers.
|
||||
|
||||
|
|
37
opts/quotedstring.go
Normal file
37
opts/quotedstring.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package opts
|
||||
|
||||
// QuotedString is a string that may have extra quotes around the value. The
|
||||
// quotes are stripped from the value.
|
||||
type QuotedString struct {
|
||||
value *string
|
||||
}
|
||||
|
||||
// Set sets a new value
|
||||
func (s *QuotedString) Set(val string) error {
|
||||
*s.value = trimQuotes(val)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns the type of the value
|
||||
func (s *QuotedString) Type() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
func (s *QuotedString) String() string {
|
||||
return string(*s.value)
|
||||
}
|
||||
|
||||
func trimQuotes(value string) string {
|
||||
lastIndex := len(value) - 1
|
||||
for _, char := range []byte{'\'', '"'} {
|
||||
if value[0] == char && value[lastIndex] == char {
|
||||
return value[1:lastIndex]
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// NewQuotedString returns a new quoted string option
|
||||
func NewQuotedString(value *string) *QuotedString {
|
||||
return &QuotedString{value: value}
|
||||
}
|
28
opts/quotedstring_test.go
Normal file
28
opts/quotedstring_test.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package opts
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/pkg/testutil/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestQuotedStringSetWithQuotes(t *testing.T) {
|
||||
value := ""
|
||||
qs := NewQuotedString(&value)
|
||||
assert.NilError(t, qs.Set("\"something\""))
|
||||
assert.Equal(t, qs.String(), "something")
|
||||
assert.Equal(t, value, "something")
|
||||
}
|
||||
|
||||
func TestQuotedStringSetWithMismatchedQuotes(t *testing.T) {
|
||||
value := ""
|
||||
qs := NewQuotedString(&value)
|
||||
assert.NilError(t, qs.Set("\"something'"))
|
||||
assert.Equal(t, qs.String(), "\"something'")
|
||||
}
|
||||
|
||||
func TestQuotedStringSetWithNoQuotes(t *testing.T) {
|
||||
value := ""
|
||||
qs := NewQuotedString(&value)
|
||||
assert.NilError(t, qs.Set("something"))
|
||||
assert.Equal(t, qs.String(), "something")
|
||||
}
|
37
pkg/plugins/plugin_test.go
Normal file
37
pkg/plugins/plugin_test.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package plugins
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// regression test for deadlock in handlers
|
||||
func TestPluginAddHandler(t *testing.T) {
|
||||
// make a plugin which is pre-activated
|
||||
p := &Plugin{activateWait: sync.NewCond(&sync.Mutex{})}
|
||||
p.Manifest = &Manifest{Implements: []string{"bananas"}}
|
||||
storage.plugins["qwerty"] = p
|
||||
|
||||
testActive(t, p)
|
||||
Handle("bananas", func(_ string, _ *Client) {})
|
||||
testActive(t, p)
|
||||
}
|
||||
|
||||
func testActive(t *testing.T, p *Plugin) {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
p.waitActive()
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
_, f, l, _ := runtime.Caller(1)
|
||||
t.Fatalf("%s:%d: deadlock in waitActive", filepath.Base(f), l)
|
||||
case <-done:
|
||||
}
|
||||
|
||||
}
|
|
@ -70,12 +70,12 @@ type Plugin struct {
|
|||
// Manifest of the plugin (see above)
|
||||
Manifest *Manifest `json:"-"`
|
||||
|
||||
// error produced by activation
|
||||
activateErr error
|
||||
// specifies if the activation sequence is completed (not if it is successful or not)
|
||||
activated bool
|
||||
// wait for activation to finish
|
||||
activateWait *sync.Cond
|
||||
// error produced by activation
|
||||
activateErr error
|
||||
// keeps track of callback handlers run against this plugin
|
||||
handlersRun bool
|
||||
}
|
||||
|
||||
// BasePath returns the path to which all paths returned by the plugin are relative to.
|
||||
|
@ -112,19 +112,51 @@ func NewLocalPlugin(name, addr string) *Plugin {
|
|||
|
||||
func (p *Plugin) activate() error {
|
||||
p.activateWait.L.Lock()
|
||||
if p.activated {
|
||||
|
||||
if p.activated() {
|
||||
p.runHandlers()
|
||||
p.activateWait.L.Unlock()
|
||||
return p.activateErr
|
||||
}
|
||||
|
||||
p.activateErr = p.activateWithLock()
|
||||
p.activated = true
|
||||
|
||||
p.runHandlers()
|
||||
p.activateWait.L.Unlock()
|
||||
p.activateWait.Broadcast()
|
||||
return p.activateErr
|
||||
}
|
||||
|
||||
// runHandlers runs the registered handlers for the implemented plugin types
|
||||
// This should only be run after activation, and while the activation lock is held.
|
||||
func (p *Plugin) runHandlers() {
|
||||
if !p.activated() {
|
||||
return
|
||||
}
|
||||
|
||||
handlers.RLock()
|
||||
if !p.handlersRun {
|
||||
for _, iface := range p.Manifest.Implements {
|
||||
hdlrs, handled := handlers.extpointHandlers[iface]
|
||||
if !handled {
|
||||
continue
|
||||
}
|
||||
for _, handler := range hdlrs {
|
||||
handler(p.name, p.client)
|
||||
}
|
||||
}
|
||||
p.handlersRun = true
|
||||
}
|
||||
handlers.RUnlock()
|
||||
|
||||
}
|
||||
|
||||
// activated returns if the plugin has already been activated.
|
||||
// This should only be called with the activation lock held
|
||||
func (p *Plugin) activated() bool {
|
||||
return p.Manifest != nil
|
||||
}
|
||||
|
||||
func (p *Plugin) activateWithLock() error {
|
||||
c, err := NewClient(p.Addr, p.TLSConfig)
|
||||
if err != nil {
|
||||
|
@ -138,24 +170,12 @@ func (p *Plugin) activateWithLock() error {
|
|||
}
|
||||
|
||||
p.Manifest = m
|
||||
|
||||
handlers.RLock()
|
||||
for _, iface := range m.Implements {
|
||||
hdlrs, handled := handlers.extpointHandlers[iface]
|
||||
if !handled {
|
||||
continue
|
||||
}
|
||||
for _, handler := range hdlrs {
|
||||
handler(p.name, p.client)
|
||||
}
|
||||
}
|
||||
handlers.RUnlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Plugin) waitActive() error {
|
||||
p.activateWait.L.Lock()
|
||||
for !p.activated {
|
||||
for !p.activated() {
|
||||
p.activateWait.Wait()
|
||||
}
|
||||
p.activateWait.L.Unlock()
|
||||
|
@ -163,7 +183,7 @@ func (p *Plugin) waitActive() error {
|
|||
}
|
||||
|
||||
func (p *Plugin) implements(kind string) bool {
|
||||
if err := p.waitActive(); err != nil {
|
||||
if p.Manifest == nil {
|
||||
return false
|
||||
}
|
||||
for _, driver := range p.Manifest.Implements {
|
||||
|
@ -232,7 +252,7 @@ func Get(name, imp string) (*Plugin, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pl.implements(imp) {
|
||||
if err := pl.waitActive(); err == nil && pl.implements(imp) {
|
||||
logrus.Debugf("%s implements: %s", name, imp)
|
||||
return pl, nil
|
||||
}
|
||||
|
@ -249,9 +269,17 @@ func Handle(iface string, fn func(string, *Client)) {
|
|||
|
||||
hdlrs = append(hdlrs, fn)
|
||||
handlers.extpointHandlers[iface] = hdlrs
|
||||
|
||||
storage.Lock()
|
||||
for _, p := range storage.plugins {
|
||||
p.activated = false
|
||||
p.activateWait.L.Lock()
|
||||
if p.activated() && p.implements(iface) {
|
||||
p.handlersRun = false
|
||||
}
|
||||
p.activateWait.L.Unlock()
|
||||
}
|
||||
storage.Unlock()
|
||||
|
||||
handlers.Unlock()
|
||||
}
|
||||
|
||||
|
@ -292,7 +320,7 @@ func GetAll(imp string) ([]*Plugin, error) {
|
|||
logrus.Error(pl.err)
|
||||
continue
|
||||
}
|
||||
if pl.pl.implements(imp) {
|
||||
if err := pl.pl.waitActive(); err == nil && pl.pl.implements(imp) {
|
||||
out = append(out, pl.pl)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ func (ps *Store) GetV2Plugin(refOrID string) (*v2.Plugin, error) {
|
|||
func (ps *Store) validateName(name string) error {
|
||||
for _, p := range ps.plugins {
|
||||
if p.Name() == name {
|
||||
return errors.Errorf("%v already exists", name)
|
||||
return errors.Errorf("plugin %q already exists", name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -232,7 +232,7 @@ func (ps *Store) resolvePluginID(idOrName string) (string, error) {
|
|||
|
||||
ref, err := reference.ParseNamed(idOrName)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to parse %v", idOrName)
|
||||
return "", errors.WithStack(ErrNotFound(idOrName))
|
||||
}
|
||||
if _, ok := ref.(reference.Canonical); ok {
|
||||
logrus.Warnf("canonical references cannot be resolved: %v", ref.String())
|
||||
|
|
|
@ -3,17 +3,13 @@ package store
|
|||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var volumeBucketName = []byte("volumes")
|
||||
|
||||
type dbEntry struct {
|
||||
Key []byte
|
||||
Value []byte
|
||||
}
|
||||
|
||||
type volumeMetadata struct {
|
||||
Name string
|
||||
Driver string
|
||||
|
@ -67,12 +63,26 @@ func removeMeta(tx *bolt.Tx, name string) error {
|
|||
return errors.Wrap(b.Delete([]byte(name)), "error removing volume metadata")
|
||||
}
|
||||
|
||||
func listEntries(tx *bolt.Tx) []*dbEntry {
|
||||
var entries []*dbEntry
|
||||
// listMeta is used during restore to get the list of volume metadata
|
||||
// from the on-disk database.
|
||||
// Any errors that occur are only logged.
|
||||
func listMeta(tx *bolt.Tx) []volumeMetadata {
|
||||
var ls []volumeMetadata
|
||||
b := tx.Bucket(volumeBucketName)
|
||||
b.ForEach(func(k, v []byte) error {
|
||||
entries = append(entries, &dbEntry{k, v})
|
||||
if len(v) == 0 {
|
||||
// don't try to unmarshal an empty value
|
||||
return nil
|
||||
}
|
||||
|
||||
var m volumeMetadata
|
||||
if err := json.Unmarshal(v, &m); err != nil {
|
||||
// Just log the error
|
||||
logrus.Errorf("Error while reading volume metadata for volume %q: %v", string(k), err)
|
||||
return nil
|
||||
}
|
||||
ls = append(ls, m)
|
||||
return nil
|
||||
})
|
||||
return entries
|
||||
return ls
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
|
@ -17,45 +16,38 @@ import (
|
|||
// It does not probe the available drivers to find anything that may have been added
|
||||
// out of band.
|
||||
func (s *VolumeStore) restore() {
|
||||
var entries []*dbEntry
|
||||
var ls []volumeMetadata
|
||||
s.db.View(func(tx *bolt.Tx) error {
|
||||
entries = listEntries(tx)
|
||||
ls = listMeta(tx)
|
||||
return nil
|
||||
})
|
||||
|
||||
chRemove := make(chan []byte, len(entries))
|
||||
chRemove := make(chan *volumeMetadata, len(ls))
|
||||
var wg sync.WaitGroup
|
||||
for _, entry := range entries {
|
||||
for _, meta := range ls {
|
||||
wg.Add(1)
|
||||
// this is potentially a very slow operation, so do it in a goroutine
|
||||
go func(entry *dbEntry) {
|
||||
go func(meta volumeMetadata) {
|
||||
defer wg.Done()
|
||||
var meta volumeMetadata
|
||||
if len(entry.Value) != 0 {
|
||||
if err := json.Unmarshal(entry.Value, &meta); err != nil {
|
||||
logrus.Errorf("Error while reading volume metadata for volume %q: %v", string(entry.Key), err)
|
||||
// don't return here, we can try with `getVolume` below
|
||||
}
|
||||
}
|
||||
|
||||
var v volume.Volume
|
||||
var err error
|
||||
if meta.Driver != "" {
|
||||
v, err = lookupVolume(meta.Driver, string(entry.Key))
|
||||
v, err = lookupVolume(meta.Driver, meta.Name)
|
||||
if err != nil && err != errNoSuchVolume {
|
||||
logrus.WithError(err).WithField("driver", meta.Driver).WithField("volume", string(entry.Key)).Warn("Error restoring volume")
|
||||
logrus.WithError(err).WithField("driver", meta.Driver).WithField("volume", meta.Name).Warn("Error restoring volume")
|
||||
return
|
||||
}
|
||||
if v == nil {
|
||||
// doesn't exist in the driver, remove it from the db
|
||||
chRemove <- entry.Key
|
||||
chRemove <- &meta
|
||||
return
|
||||
}
|
||||
} else {
|
||||
v, err = s.getVolume(string(entry.Key))
|
||||
v, err = s.getVolume(meta.Name)
|
||||
if err != nil {
|
||||
if err == errNoSuchVolume {
|
||||
chRemove <- entry.Key
|
||||
chRemove <- &meta
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -75,15 +67,15 @@ func (s *VolumeStore) restore() {
|
|||
s.labels[v.Name()] = meta.Labels
|
||||
s.names[v.Name()] = v
|
||||
s.globalLock.Unlock()
|
||||
}(entry)
|
||||
}(meta)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
close(chRemove)
|
||||
s.db.Update(func(tx *bolt.Tx) error {
|
||||
for k := range chRemove {
|
||||
if err := removeMeta(tx, string(k)); err != nil {
|
||||
logrus.Warnf("Error removing stale entry from volume db: %v", err)
|
||||
for meta := range chRemove {
|
||||
if err := removeMeta(tx, meta.Name); err != nil {
|
||||
logrus.WithField("volume", meta.Name).Warnf("Error removing stale entry from volume db: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
Loading…
Reference in a new issue