Merge component 'cli' from git@github.com:docker/cli master

This commit is contained in:
Andrew Hsu
2017-08-14 16:17:02 +00:00
304 changed files with 10980 additions and 2584 deletions
+1
View File
@@ -1,6 +1,7 @@
# Github code owners
# See https://github.com/blog/2392-introducing-code-owners
cli/command/stack/** @dnephin @vdemeester
cli/compose/** @dnephin @vdemeester
contrib/completion/bash/** @albers
contrib/completion/zsh/** @sdurrheimer
+365
View File
@@ -0,0 +1,365 @@
# Contributing to Docker
Want to hack on Docker? Awesome! We have a contributor's guide that explains
[setting up a Docker development environment and the contribution
process](https://docs.docker.com/opensource/project/who-written-for/).
This page contains information about reporting issues as well as some tips and
guidelines useful to experienced open source contributors. Finally, make sure
you read our [community guidelines](#docker-community-guidelines) before you
start participating.
## Topics
* [Reporting Security Issues](#reporting-security-issues)
* [Design and Cleanup Proposals](#design-and-cleanup-proposals)
* [Reporting Issues](#reporting-other-issues)
* [Quick Contribution Tips and Guidelines](#quick-contribution-tips-and-guidelines)
* [Community Guidelines](#docker-community-guidelines)
## Reporting security issues
The Docker maintainers take security seriously. If you discover a security
issue, please bring it to their attention right away!
Please **DO NOT** file a public issue, instead send your report privately to
[security@docker.com](mailto:security@docker.com).
Security reports are greatly appreciated and we will publicly thank you for it.
We also like to send gifts—if you're into Docker schwag, make sure to let
us know. We currently do not offer a paid security bounty program, but are not
ruling it out in the future.
## Reporting other issues
A great way to contribute to the project is to send a detailed report when you
encounter an issue. We always appreciate a well-written, thorough bug report,
and will thank you for it!
Check that [our issue database](https://github.com/docker/cli/issues)
doesn't already include that problem or suggestion before submitting an issue.
If you find a match, you can use the "subscribe" button to get notified on
updates. Do *not* leave random "+1" or "I have this too" comments, as they
only clutter the discussion, and don't help resolving it. However, if you
have ways to reproduce the issue or have additional information that may help
resolving the issue, please leave a comment.
When reporting issues, always include:
* The output of `docker version`.
* The output of `docker info`.
Also include the steps required to reproduce the problem if possible and
applicable. This information will help us review and fix your issue faster.
When sending lengthy log-files, consider posting them as a gist (https://gist.github.com).
Don't forget to remove sensitive data from your logfiles before posting (you can
replace those parts with "REDACTED").
## Quick contribution tips and guidelines
This section gives the experienced contributor some tips and guidelines.
### Pull requests are always welcome
Not sure if that typo is worth a pull request? Found a bug and know how to fix
it? Do it! We will appreciate it. Any significant improvement should be
documented as [a GitHub issue](https://github.com/docker/cli/issues) before
anybody starts working on it.
We are always thrilled to receive pull requests. We do our best to process them
quickly. If your pull request is not accepted on the first try,
don't get discouraged! Our contributor's guide explains [the review process we
use for simple changes](https://docs.docker.com/opensource/workflow/make-a-contribution/).
### Talking to other Docker users and contributors
<table class="tg">
<col width="45%">
<col width="65%">
<tr>
<td>Forums</td>
<td>
A public forum for users to discuss questions and explore current design patterns and
best practices about Docker and related projects in the Docker Ecosystem. To participate,
just log in with your Docker Hub account on <a href="https://forums.docker.com" target="_blank">https://forums.docker.com</a>.
</td>
</tr>
<tr>
<td>Community Slack</td>
<td>
The Docker Community has a dedicated Slack chat to discuss features and issues. You can sign-up <a href="https://community.docker.com/registrations/groups/4316" target="_blank">with this link</a>.
</td>
</tr>
<tr>
<td>Twitter</td>
<td>
You can follow <a href="https://twitter.com/docker/" target="_blank">Docker's Twitter feed</a>
to get updates on our products. You can also tweet us questions or just
share blogs or stories.
</td>
</tr>
<tr>
<td>Stack Overflow</td>
<td>
Stack Overflow has over 17000 Docker questions listed. We regularly
monitor <a href="https://stackoverflow.com/search?tab=newest&q=docker" target="_blank">Docker questions</a>
and so do many other knowledgeable Docker users.
</td>
</tr>
</table>
### Conventions
Fork the repository and make changes on your fork in a feature branch:
- If it's a bug fix branch, name it XXXX-something where XXXX is the number of
the issue.
- If it's a feature branch, create an enhancement issue to announce
your intentions, and name it XXXX-something where XXXX is the number of the
issue.
Submit unit tests for your changes. Go has a great test framework built in; use
it! Take a look at existing tests for inspiration. [Run the full test
suite](README.md) on your branch before
submitting a pull request.
Update the documentation when creating or modifying features. Test your
documentation changes for clarity, concision, and correctness, as well as a
clean documentation build. See our contributors guide for [our style
guide](https://docs.docker.com/opensource/doc-style) and instructions on [building
the documentation](https://docs.docker.com/opensource/project/test-and-docs/#build-and-test-the-documentation).
Write clean code. Universally formatted code promotes ease of writing, reading,
and maintenance. Always run `gofmt -s -w file.go` on each changed file before
committing your changes. Most editors have plug-ins that do this automatically.
Pull request descriptions should be as clear as possible and include a reference
to all the issues that they address.
Commit messages must start with a capitalized and short summary (max. 50 chars)
written in the imperative, followed by an optional, more detailed explanatory
text which is separated from the summary by an empty line.
Code review comments may be added to your pull request. Discuss, then make the
suggested modifications and push additional commits to your feature branch. Post
a comment after pushing. New commits show up in the pull request automatically,
but the reviewers are notified only when you comment.
Pull requests must be cleanly rebased on top of master without multiple branches
mixed into the PR.
**Git tip**: If your PR no longer merges cleanly, use `rebase master` in your
feature branch to update your pull request rather than `merge master`.
Before you make a pull request, squash your commits into logical units of work
using `git rebase -i` and `git push -f`. A logical unit of work is a consistent
set of patches that should be reviewed together: for example, upgrading the
version of a vendored dependency and taking advantage of its now available new
feature constitute two separate units of work. Implementing a new function and
calling it in another file constitute a single logical unit of work. The very
high majority of submissions should have a single commit, so if in doubt: squash
down to one.
After every commit, make sure the test suite passes. Include documentation
changes in the same pull request so that a revert would remove all traces of
the feature or fix.
Include an issue reference like `Closes #XXXX` or `Fixes #XXXX` in the pull request
description that close an issue. Including references automatically closes the issue
on a merge.
Please do not add yourself to the `AUTHORS` file, as it is regenerated regularly
from the Git history.
Please see the [Coding Style](#coding-style) for further guidelines.
### Merge approval
Docker maintainers use LGTM (Looks Good To Me) in comments on the code review to
indicate acceptance.
A change requires LGTMs from an absolute majority of the maintainers of each
component affected. For example, if a change affects `docs/` and `registry/`, it
needs an absolute majority from the maintainers of `docs/` AND, separately, an
absolute majority of the maintainers of `registry/`.
For more details, see the [MAINTAINERS](MAINTAINERS) page.
### Sign your work
The sign-off is a simple line at the end of the explanation for the patch. Your
signature certifies that you wrote the patch or otherwise have the right to pass
it on as an open-source patch. The rules are pretty simple: if you can certify
the below (from [developercertificate.org](http://developercertificate.org/)):
```
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
```
Then you just add a line to every git commit message:
Signed-off-by: Joe Smith <joe.smith@email.com>
Use your real name (sorry, no pseudonyms or anonymous contributions.)
If you set your `user.name` and `user.email` git configs, you can sign your
commit automatically with `git commit -s`.
### How can I become a maintainer?
The procedures for adding new maintainers are explained in the
global [MAINTAINERS](https://github.com/docker/opensource/blob/master/MAINTAINERS)
file in the [https://github.com/docker/opensource/](https://github.com/docker/opensource/)
repository.
Don't forget: being a maintainer is a time investment. Make sure you
will have time to make yourself available. You don't have to be a
maintainer to make a difference on the project!
## Docker community guidelines
We want to keep the Docker community awesome, growing and collaborative. We need
your help to keep it that way. To help with this we've come up with some general
guidelines for the community as a whole:
* Be nice: Be courteous, respectful and polite to fellow community members:
no regional, racial, gender, or other abuse will be tolerated. We like
nice people way better than mean ones!
* Encourage diversity and participation: Make everyone in our community feel
welcome, regardless of their background and the extent of their
contributions, and do everything possible to encourage participation in
our community.
* Keep it legal: Basically, don't get us in trouble. Share only content that
you own, do not share private or sensitive information, and don't break
the law.
* Stay on topic: Make sure that you are posting to the correct channel and
avoid off-topic discussions. Remember when you update an issue or respond
to an email you are potentially sending to a large number of people. Please
consider this before you update. Also remember that nobody likes spam.
* Don't send email to the maintainers: There's no need to send email to the
maintainers to ask them to investigate an issue or to take a look at a
pull request. Instead of sending an email, GitHub mentions should be
used to ping maintainers to review a pull request, a proposal or an
issue.
### Guideline violations — 3 strikes method
The point of this section is not to find opportunities to punish people, but we
do need a fair way to deal with people who are making our community suck.
1. First occurrence: We'll give you a friendly, but public reminder that the
behavior is inappropriate according to our guidelines.
2. Second occurrence: We will send you a private message with a warning that
any additional violations will result in removal from the community.
3. Third occurrence: Depending on the violation, we may need to delete or ban
your account.
**Notes:**
* Obvious spammers are banned on first occurrence. If we don't do this, we'll
have spam all over the place.
* Violations are forgiven after 6 months of good behavior, and we won't hold a
grudge.
* People who commit minor infractions will get some education, rather than
hammering them in the 3 strikes process.
* The rules apply equally to everyone in the community, no matter how much
you've contributed.
* Extreme violations of a threatening, abusive, destructive or illegal nature
will be addressed immediately and are not subject to 3 strikes or forgiveness.
* Contact abuse@docker.com to report abuse or appeal violations. In the case of
appeals, we know that mistakes happen, and we'll work with you to come up with a
fair solution if there has been a misunderstanding.
## Coding Style
Unless explicitly stated, we follow all coding guidelines from the Go
community. While some of these standards may seem arbitrary, they somehow seem
to result in a solid, consistent codebase.
It is possible that the code base does not currently comply with these
guidelines. We are not looking for a massive PR that fixes this, since that
goes against the spirit of the guidelines. All new contributions should make a
best effort to clean up and make the code base better than they left it.
Obviously, apply your best judgement. Remember, the goal here is to make the
code base easier for humans to navigate and understand. Always keep that in
mind when nudging others to comply.
The rules:
1. All code should be formatted with `gofmt -s`.
2. All code should pass the default levels of
[`golint`](https://github.com/golang/lint).
3. All code should follow the guidelines covered in [Effective
Go](http://golang.org/doc/effective_go.html) and [Go Code Review
Comments](https://github.com/golang/go/wiki/CodeReviewComments).
4. Comment the code. Tell us the why, the history and the context.
5. Document _all_ declarations and methods, even private ones. Declare
expectations, caveats and anything else that may be important. If a type
gets exported, having the comments already there will ensure it's ready.
6. Variable name length should be proportional to its context and no longer.
`noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`.
In practice, short methods will have short variable names and globals will
have longer names.
7. No underscores in package names. If you need a compound name, step back,
and re-examine why you need a compound name. If you still think you need a
compound name, lose the underscore.
8. No utils or helpers packages. If a function is not general enough to
warrant its own package, it has not been written generally enough to be a
part of a util package. Just leave it unexported and well-documented.
9. All tests should run with `go test` and outside tooling should not be
required. No, we don't need another unit testing framework. Assertion
packages are acceptable if they provide _real_ incremental value.
10. Even though we call these "rules" above, they are actually just
guidelines. Since you've read all the rules, you now know that.
If you are having trouble getting into the mood of idiomatic Go, we recommend
reading through [Effective Go](https://golang.org/doc/effective_go.html). The
[Go Blog](https://blog.golang.org) is also a great resource. Drinking the
kool-aid is a lot easier than going thirsty.
+18 -19
View File
@@ -3,62 +3,61 @@
#
all: binary
# remove build artifacts
.PHONY: clean
clean:
clean: ## remove build artifacts
rm -rf ./build/* cli/winresources/rsrc_* ./man/man[1-9] docs/yaml/gen
# run go test
# the "-tags daemon" part is temporary
.PHONY: test
test:
test: ## run go test
./scripts/test/unit $(shell go list ./... | grep -v '/vendor/')
.PHONY: test-coverage
test-coverage:
test-coverage: ## run test coverage
./scripts/test/unit-with-coverage $(shell go list ./... | grep -v '/vendor/')
.PHONY: lint
lint:
lint: ## run all the lint tools
gometalinter --config gometalinter.json ./...
.PHONY: binary
binary:
binary: ## build executable for Linux
@echo "WARNING: binary creates a Linux executable. Use cross for macOS or Windows."
./scripts/build/binary
.PHONY: cross
cross:
cross: ## build executable for macOS and Windows
./scripts/build/cross
.PHONY: dynbinary
dynbinary:
dynbinary: ## build dynamically linked binary
./scripts/build/dynbinary
.PHONY: watch
watch:
watch: ## monitor file changes and run go test
./scripts/test/watch
# Check vendor matches vendor.conf
vendor: vendor.conf
vendor: vendor.conf ## check that vendor matches vendor.conf
vndr 2> /dev/null
scripts/validate/check-git-diff vendor
## generate man pages from go source and markdown
.PHONY: manpages
manpages:
manpages: ## generate man pages from go source and markdown
scripts/docs/generate-man.sh
## generate documentation YAML files consumed by docs repo
.PHONY: yamldocs
yamldocs:
yamldocs: ## generate documentation YAML files consumed by docs repo
scripts/docs/generate-yaml.sh
## Shellcheck validation
.PHONY: shellcheck
shellcheck:
shellcheck: ## run shellcheck validation
scripts/validate/shellcheck
.PHONY: help
help: ## print this help
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
cli/compose/schema/bindata.go: cli/compose/schema/data/*.json
go generate github.com/docker/cli/cli/compose/schema
+6
View File
@@ -29,6 +29,12 @@ Run all linting:
$ make -f docker.Makefile lint
```
List all the available targets:
```
$ make help
```
### In-container development environment
Start an interactive development environment:
+1 -1
View File
@@ -1 +1 @@
17.07.0-dev
17.08.0-dev
@@ -20,11 +20,11 @@ func TestCheckpointCreateErrors(t *testing.T) {
}{
{
args: []string{"too-few-arguments"},
expectedError: "requires exactly 2 argument(s)",
expectedError: "requires exactly 2 arguments",
},
{
args: []string{"too", "many", "arguments"},
expectedError: "requires exactly 2 argument(s)",
expectedError: "requires exactly 2 arguments",
},
{
args: []string{"foo", "bar"},
@@ -20,11 +20,11 @@ func TestCheckpointRemoveErrors(t *testing.T) {
}{
{
args: []string{"too-few-arguments"},
expectedError: "requires exactly 2 argument(s)",
expectedError: "requires exactly 2 arguments",
},
{
args: []string{"too", "many", "arguments"},
expectedError: "requires exactly 2 argument(s)",
expectedError: "requires exactly 2 arguments",
},
{
args: []string{"foo", "bar"},
+6
View File
@@ -2,9 +2,11 @@ package command
import (
"io"
"net"
"net/http"
"os"
"runtime"
"time"
"github.com/docker/cli/cli"
cliconfig "github.com/docker/cli/cli/config"
@@ -214,6 +216,10 @@ func newHTTPClient(host string, tlsOptions *tlsconfig.Options) (*http.Client, er
}
tr := &http.Transport{
TLSClientConfig: config,
DialContext: (&net.Dialer{
KeepAlive: 30 * time.Second,
Timeout: 30 * time.Second,
}).DialContext,
}
proto, addr, _, err := client.ParseHost(host)
if err != nil {
@@ -26,10 +26,10 @@ func TestConfigCreateErrors(t *testing.T) {
}{
{
args: []string{"too_few"},
expectedError: "requires exactly 2 argument(s)",
expectedError: "requires exactly 2 arguments",
},
{args: []string{"too", "many", "arguments"},
expectedError: "requires exactly 2 argument(s)",
expectedError: "requires exactly 2 arguments",
},
{
args: []string{"name", filepath.Join("testdata", configDataFile)},
@@ -20,7 +20,7 @@ func TestConfigRemoveErrors(t *testing.T) {
}{
{
args: []string{},
expectedError: "requires at least 1 argument(s).",
expectedError: "requires at least 1 argument.",
},
{
args: []string{"foo"},
+1 -1
View File
@@ -7,8 +7,8 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/cli/opts"
"github.com/docker/cli/templates"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/templates"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)
@@ -7,7 +7,7 @@ import (
"text/tabwriter"
"text/template"
"github.com/docker/docker/pkg/templates"
"github.com/docker/cli/templates"
"github.com/pkg/errors"
)
@@ -79,10 +79,13 @@ func (c *historyContext) ID() string {
}
func (c *historyContext) CreatedAt() string {
return units.HumanDuration(time.Now().UTC().Sub(time.Unix(c.h.Created, 0)))
return time.Unix(c.h.Created, 0).Format(time.RFC3339)
}
func (c *historyContext) CreatedSince() string {
if !c.human {
return c.CreatedAt()
}
created := units.HumanDuration(time.Now().UTC().Sub(time.Unix(c.h.Created, 0)))
return created + " ago"
}
@@ -51,17 +51,21 @@ func TestHistoryContext_ID(t *testing.T) {
}
func TestHistoryContext_CreatedSince(t *testing.T) {
unixTime := time.Now().AddDate(0, 0, -7).Unix()
expected := "7 days ago"
var ctx historyContext
cases := []historyCase{
{
historyContext{
h: image.HistoryResponseItem{Created: unixTime},
h: image.HistoryResponseItem{Created: time.Now().AddDate(0, 0, -7).Unix()},
trunc: false,
human: true,
}, expected, ctx.CreatedSince,
}, "7 days ago", ctx.CreatedSince,
},
{
historyContext{
h: image.HistoryResponseItem{Created: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix()},
trunc: false,
human: false,
}, "2009-11-10T23:00:00Z", ctx.CreatedSince,
},
}
@@ -518,11 +518,11 @@ func (c *serviceContext) Image() string {
}
func (c *serviceContext) Ports() string {
if c.service.Spec.EndpointSpec == nil || c.service.Spec.EndpointSpec.Ports == nil {
if c.service.Endpoint.Ports == nil {
return ""
}
ports := []string{}
for _, pConfig := range c.service.Spec.EndpointSpec.Ports {
for _, pConfig := range c.service.Endpoint.Ports {
if pConfig.PublishMode == swarm.PortConfigPublishModeIngress {
ports = append(ports, fmt.Sprintf("*:%d->%d/%s",
pConfig.PublishedPort,
@@ -96,14 +96,14 @@ bar
ID: "id_baz",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "baz"},
EndpointSpec: &swarm.EndpointSpec{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8080,
Protocol: "tcp",
},
},
Endpoint: swarm.Endpoint{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8080,
Protocol: "tcp",
},
},
},
@@ -112,14 +112,14 @@ bar
ID: "id_bar",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "bar"},
EndpointSpec: &swarm.EndpointSpec{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8080,
Protocol: "tcp",
},
},
Endpoint: swarm.Endpoint{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8080,
Protocol: "tcp",
},
},
},
@@ -152,14 +152,14 @@ func TestServiceContextWriteJSON(t *testing.T) {
ID: "id_baz",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "baz"},
EndpointSpec: &swarm.EndpointSpec{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8080,
Protocol: "tcp",
},
},
Endpoint: swarm.Endpoint{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8080,
Protocol: "tcp",
},
},
},
@@ -168,14 +168,14 @@ func TestServiceContextWriteJSON(t *testing.T) {
ID: "id_bar",
Spec: swarm.ServiceSpec{
Annotations: swarm.Annotations{Name: "bar"},
EndpointSpec: &swarm.EndpointSpec{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8080,
Protocol: "tcp",
},
},
Endpoint: swarm.Endpoint{
Ports: []swarm.PortConfig{
{
PublishMode: "ingress",
PublishedPort: 80,
TargetPort: 8080,
Protocol: "tcp",
},
},
},
@@ -68,3 +68,37 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) {
sort.Strings(actual)
assert.Equal(t, []string{dockerfileName, ".dockerignore", "foo"}, actual)
}
// TestRunBuildFromLocalGitHubDirNonExistingRepo tests that build contexts
// starting with `github.com/` are special-cased, and the build command attempts
// to clone the remote repo.
func TestRunBuildFromGitHubSpecialCase(t *testing.T) {
cmd := NewBuildCommand(&command.DockerCli{})
cmd.SetArgs([]string{"github.com/docker/no-such-repository"})
cmd.SetOutput(ioutil.Discard)
err := cmd.Execute()
assert.Error(t, err)
assert.Contains(t, err.Error(), "unable to prepare context: unable to 'git clone'")
}
// TestRunBuildFromLocalGitHubDirNonExistingRepo tests that a local directory
// starting with `github.com` takes precedence over the `github.com` special
// case.
func TestRunBuildFromLocalGitHubDir(t *testing.T) {
tmpDir, err := ioutil.TempDir("", "docker-build-from-local-dir-")
require.NoError(t, err)
defer os.RemoveAll(tmpDir)
buildDir := filepath.Join(tmpDir, "github.com", "docker", "no-such-repository")
err = os.MkdirAll(buildDir, 0777)
require.NoError(t, err)
err = ioutil.WriteFile(filepath.Join(buildDir, "Dockerfile"), []byte("FROM busybox\n"), 0644)
require.NoError(t, err)
client := test.NewFakeCli(&fakeClient{})
cmd := NewBuildCommand(client)
cmd.SetArgs([]string{buildDir})
cmd.SetOutput(ioutil.Discard)
err = cmd.Execute()
require.NoError(t, err)
}
@@ -25,7 +25,7 @@ func TestNewHistoryCommandErrors(t *testing.T) {
{
name: "wrong-args",
args: []string{},
expectedError: "requires exactly 1 argument(s).",
expectedError: "requires exactly 1 argument.",
},
{
name: "client-error",
@@ -24,7 +24,7 @@ func TestNewImportCommandErrors(t *testing.T) {
{
name: "wrong-args",
args: []string{},
expectedError: "requires at least 1 argument(s).",
expectedError: "requires at least 1 argument.",
},
{
name: "import-failed",
@@ -22,7 +22,7 @@ func TestNewInspectCommandErrors(t *testing.T) {
{
name: "wrong-args",
args: []string{},
expectedError: "requires at least 1 argument(s).",
expectedError: "requires at least 1 argument.",
},
}
for _, tc := range testCases {
@@ -25,7 +25,7 @@ func TestNewImagesCommandErrors(t *testing.T) {
{
name: "wrong-args",
args: []string{"arg1", "arg2"},
expectedError: "requires at most 1 argument(s).",
expectedError: "requires at most 1 argument.",
},
{
name: "failed-list",
@@ -27,7 +27,7 @@ func TestNewLoadCommandErrors(t *testing.T) {
{
name: "wrong-args",
args: []string{"arg"},
expectedError: "accepts no argument(s).",
expectedError: "accepts no arguments.",
},
{
name: "input-to-terminal",
@@ -25,7 +25,7 @@ func TestNewPruneCommandErrors(t *testing.T) {
{
name: "wrong-args",
args: []string{"something"},
expectedError: "accepts no argument(s).",
expectedError: "accepts no arguments.",
},
{
name: "prune-error",
@@ -19,7 +19,7 @@ func TestNewPullCommandErrors(t *testing.T) {
}{
{
name: "wrong-args",
expectedError: "requires exactly 1 argument(s).",
expectedError: "requires exactly 1 argument.",
args: []string{},
},
{
@@ -23,7 +23,7 @@ func TestNewPushCommandErrors(t *testing.T) {
{
name: "wrong-args",
args: []string{},
expectedError: "requires exactly 1 argument(s).",
expectedError: "requires exactly 1 argument.",
},
{
name: "invalid-name",
@@ -29,7 +29,7 @@ func TestNewRemoveCommandErrors(t *testing.T) {
}{
{
name: "wrong args",
expectedError: "requires at least 1 argument(s).",
expectedError: "requires at least 1 argument.",
},
{
name: "ImageRemove fail",
+16
View File
@@ -2,6 +2,8 @@ package image
import (
"io"
"os"
"path/filepath"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
@@ -41,6 +43,10 @@ func runSave(dockerCli command.Cli, opts saveOptions) error {
return errors.New("cowardly refusing to save to a terminal. Use the -o flag or redirect")
}
if err := validateOutputPath(opts.output); err != nil {
return errors.Wrap(err, "failed to save image")
}
responseBody, err := dockerCli.Client().ImageSave(context.Background(), opts.images)
if err != nil {
return err
@@ -54,3 +60,13 @@ func runSave(dockerCli command.Cli, opts saveOptions) error {
return command.CopyToFile(opts.output, responseBody)
}
func validateOutputPath(path string) error {
dir := filepath.Dir(path)
if dir != "" && dir != "." {
if _, err := os.Stat(dir); os.IsNotExist(err) {
return errors.Errorf("unable to validate output path: directory %q does not exist", dir)
}
}
return nil
}
@@ -26,7 +26,7 @@ func TestNewSaveCommandErrors(t *testing.T) {
{
name: "wrong args",
args: []string{},
expectedError: "requires at least 1 argument(s).",
expectedError: "requires at least 1 argument.",
},
{
name: "output to terminal",
@@ -43,6 +43,11 @@ func TestNewSaveCommandErrors(t *testing.T) {
return ioutil.NopCloser(strings.NewReader("")), errors.Errorf("error saving image")
},
},
{
name: "output directory does not exist",
args: []string{"-o", "fakedir/out.tar", "arg1"},
expectedError: "failed to save image: unable to validate output path: directory \"fakedir\" does not exist",
},
}
for _, tc := range testCases {
cli := test.NewFakeCli(&fakeClient{imageSaveFunc: tc.imageSaveFunc})
+1 -1
View File
@@ -15,7 +15,7 @@ func TestCliNewTagCommandErrors(t *testing.T) {
{"image1"},
{"image1", "image2", "image3"},
}
expectedError := "\"tag\" requires exactly 2 argument(s)."
expectedError := "\"tag\" requires exactly 2 arguments."
for _, args := range testCases {
cmd := NewTagCommand(test.NewFakeCli(&fakeClient{}))
cmd.SetArgs(args)
@@ -9,7 +9,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/cli/cli"
"github.com/docker/docker/pkg/templates"
"github.com/docker/cli/templates"
"github.com/pkg/errors"
)
@@ -5,7 +5,7 @@ import (
"strings"
"testing"
"github.com/docker/docker/pkg/templates"
"github.com/docker/cli/templates"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -2,13 +2,16 @@ package network
import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
"golang.org/x/net/context"
)
type fakeClient struct {
client.Client
networkCreateFunc func(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error)
networkCreateFunc func(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error)
networkConnectFunc func(ctx context.Context, networkID, container string, config *network.EndpointSettings) error
networkDisconnectFunc func(ctx context.Context, networkID, container string, force bool) error
}
func (c *fakeClient) NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error) {
@@ -17,3 +20,17 @@ func (c *fakeClient) NetworkCreate(ctx context.Context, name string, options typ
}
return types.NetworkCreateResponse{}, nil
}
func (c *fakeClient) NetworkConnect(ctx context.Context, networkID, container string, config *network.EndpointSettings) error {
if c.networkConnectFunc != nil {
return c.networkConnectFunc(ctx, networkID, container, config)
}
return nil
}
func (c *fakeClient) NetworkDisconnect(ctx context.Context, networkID, container string, force bool) error {
if c.networkDisconnectFunc != nil {
return c.networkDisconnectFunc(ctx, networkID, container, force)
}
return nil
}
@@ -0,0 +1,70 @@
package network
import (
"io/ioutil"
"testing"
"github.com/docker/cli/cli/internal/test"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/pkg/testutil"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
)
func TestNetworkConnectErrors(t *testing.T) {
testCases := []struct {
args []string
networkConnectFunc func(ctx context.Context, networkID, container string, config *network.EndpointSettings) error
expectedError string
}{
{
expectedError: "requires exactly 2 arguments",
},
{
args: []string{"toto", "titi"},
networkConnectFunc: func(ctx context.Context, networkID, container string, config *network.EndpointSettings) error {
return errors.Errorf("error connecting network")
},
expectedError: "error connecting network",
},
}
for _, tc := range testCases {
cmd := newConnectCommand(
test.NewFakeCli(&fakeClient{
networkConnectFunc: tc.networkConnectFunc,
}),
)
cmd.SetArgs(tc.args)
cmd.SetOutput(ioutil.Discard)
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
}
}
func TestNetworkConnectWithFlags(t *testing.T) {
expectedOpts := []network.IPAMConfig{
{
"192.168.4.0/24",
"192.168.4.0/24",
"192.168.4.1/24",
map[string]string{},
},
}
cli := test.NewFakeCli(&fakeClient{
networkConnectFunc: func(ctx context.Context, networkID, container string, config *network.EndpointSettings) error {
assert.Equal(t, expectedOpts, config.IPAMConfig, "not expected driver error")
return nil
},
})
args := []string{"banana"}
cmd := newCreateCommand(cli)
cmd.SetArgs(args)
cmd.Flags().Set("driver", "foo")
cmd.Flags().Set("ip-range", "192.168.4.0/24")
cmd.Flags().Set("gateway", "192.168.4.1/24")
cmd.Flags().Set("subnet", "192.168.4.0/24")
assert.NoError(t, cmd.Execute())
}
@@ -0,0 +1,41 @@
package network
import (
"io/ioutil"
"testing"
"github.com/docker/cli/cli/internal/test"
"github.com/docker/docker/pkg/testutil"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
func TestNetworkDisconnectErrors(t *testing.T) {
testCases := []struct {
args []string
networkDisconnectFunc func(ctx context.Context, networkID, container string, force bool) error
expectedError string
}{
{
expectedError: "requires exactly 2 arguments",
},
{
args: []string{"toto", "titi"},
networkDisconnectFunc: func(ctx context.Context, networkID, container string, force bool) error {
return errors.Errorf("error disconnecting network")
},
expectedError: "error disconnecting network",
},
}
for _, tc := range testCases {
cmd := newDisconnectCommand(
test.NewFakeCli(&fakeClient{
networkDisconnectFunc: tc.networkDisconnectFunc,
}),
)
cmd.SetArgs(tc.args)
cmd.SetOutput(ioutil.Discard)
testutil.ErrorContains(t, cmd.Execute(), tc.expectedError)
}
}
@@ -27,10 +27,10 @@ func TestSecretCreateErrors(t *testing.T) {
}{
{
args: []string{"too_few"},
expectedError: "requires exactly 2 argument(s)",
expectedError: "requires exactly 2 arguments",
},
{args: []string{"too", "many", "arguments"},
expectedError: "requires exactly 2 argument(s)",
expectedError: "requires exactly 2 arguments",
},
{
args: []string{"name", filepath.Join("testdata", secretDataFile)},
@@ -20,7 +20,7 @@ func TestSecretRemoveErrors(t *testing.T) {
}{
{
args: []string{},
expectedError: "requires at least 1 argument(s).",
expectedError: "requires at least 1 argument.",
},
{
args: []string{"foo"},
@@ -0,0 +1,35 @@
package service
import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
"golang.org/x/net/context"
)
type fakeClient struct {
client.Client
serviceListFunc func(context.Context, types.ServiceListOptions) ([]swarm.Service, error)
}
func (f *fakeClient) ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
if f.serviceListFunc != nil {
return f.serviceListFunc(ctx, options)
}
return nil, nil
}
func (f *fakeClient) TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
return nil, nil
}
func (f *fakeClient) NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) {
return nil, nil
}
func newService(id string, name string) swarm.Service {
return swarm.Service{
ID: id,
Spec: swarm.ServiceSpec{Annotations: swarm.Annotations{Name: name}},
}
}
+12 -2
View File
@@ -2,6 +2,9 @@ package service
import (
"fmt"
"sort"
"vbom.ml/util/sortorder"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
@@ -20,7 +23,7 @@ type listOptions struct {
filter opts.FilterOpt
}
func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
func newListCommand(dockerCli command.Cli) *cobra.Command {
options := listOptions{filter: opts.NewFilterOpt()}
cmd := &cobra.Command{
@@ -41,7 +44,13 @@ func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
return cmd
}
func runList(dockerCli *command.DockerCli, options listOptions) error {
type byName []swarm.Service
func (n byName) Len() int { return len(n) }
func (n byName) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
func (n byName) Less(i, j int) bool { return sortorder.NaturalLess(n[i].Spec.Name, n[j].Spec.Name) }
func runList(dockerCli command.Cli, options listOptions) error {
ctx := context.Background()
client := dockerCli.Client()
@@ -51,6 +60,7 @@ func runList(dockerCli *command.DockerCli, options listOptions) error {
return err
}
sort.Sort(byName(services))
info := map[string]formatter.ServiceListInfo{}
if len(services) > 0 && !options.quiet {
// only non-empty services and not quiet, should we call TaskList and NodeList api
@@ -0,0 +1,32 @@
package service
import (
"testing"
"golang.org/x/net/context"
"github.com/docker/cli/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/pkg/testutil"
"github.com/docker/docker/pkg/testutil/golden"
"github.com/stretchr/testify/assert"
)
func TestServiceListOrder(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
serviceListFunc: func(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
return []swarm.Service{
newService("a57dbe8", "service-1-foo"),
newService("a57dbdd", "service-10-foo"),
newService("aaaaaaa", "service-2-foo"),
}, nil
},
})
cmd := newListCommand(cli)
cmd.Flags().Set("format", "{{.Name}}")
assert.NoError(t, cmd.Execute())
actual := cli.OutBuffer().String()
expected := golden.Get(t, []byte(actual), "service-list-sort.golden")
testutil.EqualNormalizedString(t, testutil.RemoveSpace, actual, string(expected))
}
@@ -10,35 +10,11 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/client"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/context"
)
type fakeClient struct {
client.Client
serviceListFunc func(context.Context, types.ServiceListOptions) ([]swarm.Service, error)
}
func (f *fakeClient) ServiceList(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
if f.serviceListFunc != nil {
return f.serviceListFunc(ctx, options)
}
return nil, nil
}
func (f *fakeClient) TaskList(ctx context.Context, options types.TaskListOptions) ([]swarm.Task, error) {
return nil, nil
}
func newService(id string, name string) swarm.Service {
return swarm.Service{
ID: id,
Spec: swarm.ServiceSpec{Annotations: swarm.Annotations{Name: name}},
}
}
func TestCreateFilter(t *testing.T) {
client := &fakeClient{
serviceListFunc: func(ctx context.Context, options types.ServiceListOptions) ([]swarm.Service, error) {
@@ -0,0 +1,3 @@
service-1-foo
service-2-foo
service-10-foo
@@ -171,13 +171,18 @@ func validateExternalNetworks(
externalNetworks []string,
) error {
for _, networkName := range externalNetworks {
if !container.NetworkMode(networkName).IsUserDefined() {
// Networks that are not user defined always exist on all nodes as
// local-scoped networks, so there's no need to inspect them.
continue
}
network, err := client.NetworkInspect(ctx, networkName, types.NetworkInspectOptions{})
switch {
case dockerclient.IsErrNotFound(err):
return errors.Errorf("network %q is declared as external, but could not be found. You need to create a swarm-scoped network before the stack is deployed", networkName)
case err != nil:
return err
case container.NetworkMode(networkName).IsUserDefined() && network.Scope != "swarm":
case network.Scope != "swarm":
return errors.Errorf("network %q is declared as external, but it is not in the right scope: %q instead of \"swarm\"", networkName, network.Scope)
}
}
@@ -56,7 +56,8 @@ func TestValidateExternalNetworks(t *testing.T) {
expectedMsg: "Unexpected",
},
{
network: "host",
inspectError: errors.New("host net does not exist on swarm classic"),
network: "host",
},
{
network: "user",
@@ -1,2 +1,2 @@
ID NAME MODE REPLICAS IMAGE PORTS
id-foo name-foo replicated 0/2 busybox:latest *:0->3232/tcp
id-foo name-foo replicated 0/2 busybox:latest *:30000->3232/tcp
@@ -21,7 +21,7 @@ func TestSwarmLeaveErrors(t *testing.T) {
{
name: "too-many-args",
args: []string{"foo"},
expectedError: "accepts no argument(s)",
expectedError: "accepts no arguments",
},
{
name: "leave-failed",
@@ -29,7 +29,7 @@ func TestSwarmUnlockKeyErrors(t *testing.T) {
{
name: "too-many-args",
args: []string{"foo"},
expectedError: "accepts no argument(s)",
expectedError: "accepts no arguments",
},
{
name: "swarm-inspect-rotate-failed",
@@ -25,7 +25,7 @@ func TestSwarmUnlockErrors(t *testing.T) {
{
name: "too-many-args",
args: []string{"foo"},
expectedError: "accepts no argument(s)",
expectedError: "accepts no arguments",
},
{
name: "is-not-part-of-a-swarm",
@@ -30,7 +30,7 @@ func TestSwarmUpdateErrors(t *testing.T) {
{
name: "too-many-args",
args: []string{"foo"},
expectedError: "accepts no argument(s)",
expectedError: "accepts no arguments",
},
{
name: "swarm-inspect-error",
+1 -1
View File
@@ -12,10 +12,10 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/opts"
"github.com/docker/cli/templates"
"github.com/docker/docker/api/types"
eventtypes "github.com/docker/docker/api/types/events"
"github.com/docker/docker/pkg/jsonlog"
"github.com/docker/docker/pkg/templates"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)
+1 -1
View File
@@ -9,9 +9,9 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/debug"
"github.com/docker/cli/templates"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/pkg/templates"
"github.com/docker/go-units"
"github.com/spf13/cobra"
"golang.org/x/net/context"
@@ -67,6 +67,10 @@ func runBuildCachePrune(dockerCli command.Cli, _ opts.FilterOpt) (uint64, string
}
func runPrune(dockerCli command.Cli, options pruneOptions) error {
// TODO version this once "until" filter is supported for volumes
if options.pruneVolumes && options.filter.Value().Include("until") {
return fmt.Errorf(`ERROR: The "until" filter is not supported with "--volumes"`)
}
if versions.LessThan(dockerCli.Client().ClientVersion(), "1.31") {
options.pruneBuildCache = false
}
+1 -1
View File
@@ -8,8 +8,8 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/templates"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/templates"
"github.com/spf13/cobra"
)
@@ -31,7 +31,7 @@ func TestVolumeCreateErrors(t *testing.T) {
},
{
args: []string{"too", "many"},
expectedError: "requires at most 1 argument(s)",
expectedError: "requires at most 1 argument",
},
{
volumeCreateFunc: func(createBody volumetypes.VolumesCreateBody) (types.Volume, error) {
@@ -142,6 +142,7 @@ func Service(
User: service.User,
Mounts: mounts,
StopGracePeriod: service.StopGracePeriod,
StopSignal: service.StopSignal,
TTY: service.Tty,
OpenStdin: service.StdinOpen,
Secrets: secrets,
@@ -454,6 +455,7 @@ func convertUpdateConfig(source *composetypes.UpdateConfig) *swarm.UpdateConfig
FailureAction: source.FailureAction,
Monitor: source.Monitor,
MaxFailureRatio: source.MaxFailureRatio,
Order: source.Order,
}
}
@@ -343,3 +343,21 @@ func TestConvertCredentialSpec(t *testing.T) {
assert.Error(t, err)
assert.Nil(t, swarmSpec)
}
func TestConvertUpdateConfigOrder(t *testing.T) {
// test default behavior
updateConfig := convertUpdateConfig(&composetypes.UpdateConfig{})
assert.Equal(t, "", updateConfig.Order)
// test start-first
updateConfig = convertUpdateConfig(&composetypes.UpdateConfig{
Order: "start-first",
})
assert.Equal(t, updateConfig.Order, "start-first")
// test stop-first
updateConfig = convertUpdateConfig(&composetypes.UpdateConfig{
Order: "stop-first",
})
assert.Equal(t, updateConfig.Order, "stop-first")
}
@@ -74,6 +74,10 @@ func convertVolumeToMount(
return result, nil
}
if stackVolume.Name != "" {
result.Source = stackVolume.Name
}
result.VolumeOptions.Labels = AddStackLabel(namespace, stackVolume.Labels)
if stackVolume.Driver != "" || stackVolume.DriverOpts != nil {
result.VolumeOptions.DriverConfig = &mount.Driver{
@@ -98,6 +98,53 @@ func TestConvertVolumeToMountNamedVolume(t *testing.T) {
assert.Equal(t, expected, mount)
}
func TestConvertVolumeToMountNamedVolumeWithNameCustomizd(t *testing.T) {
stackVolumes := volumes{
"normal": composetypes.VolumeConfig{
Name: "user_specified_name",
Driver: "vsphere",
DriverOpts: map[string]string{
"opt": "value",
},
Labels: map[string]string{
"something": "labeled",
},
},
}
namespace := NewNamespace("foo")
expected := mount.Mount{
Type: mount.TypeVolume,
Source: "user_specified_name",
Target: "/foo",
ReadOnly: true,
VolumeOptions: &mount.VolumeOptions{
Labels: map[string]string{
LabelNamespace: "foo",
"something": "labeled",
},
DriverConfig: &mount.Driver{
Name: "vsphere",
Options: map[string]string{
"opt": "value",
},
},
NoCopy: true,
},
}
config := composetypes.ServiceVolumeConfig{
Type: "volume",
Source: "normal",
Target: "/foo",
ReadOnly: true,
Volume: &composetypes.ServiceVolumeVolume{
NoCopy: true,
},
}
mount, err := convertVolumeToMount(config, stackVolumes, namespace)
assert.NoError(t, err)
assert.Equal(t, expected, mount)
}
func TestConvertVolumeToMountNamedVolumeExternal(t *testing.T) {
stackVolumes := volumes{
"outside": composetypes.VolumeConfig{
@@ -1,4 +1,4 @@
version: "3.3"
version: "3.4"
services:
foo:
@@ -31,6 +31,7 @@ services:
failure_action: continue
monitor: 60s
max_failure_ratio: 0.3
order: start-first
resources:
limits:
cpus: '0.001'
@@ -280,6 +281,15 @@ volumes:
foo: "bar"
baz: 1
another-volume:
name: "user_specified_name"
driver: vsphere
driver_opts:
# Values can be strings or numbers
foo: "bar"
baz: 1
external-volume:
# Specifies that a pre-existing volume called "external-volume"
# can be referred to within this file as "external-volume"
@@ -288,5 +298,12 @@ volumes:
other-external-volume:
# Specifies that a pre-existing volume called "my-cool-volume"
# can be referred to within this file as "other-external-volume"
# This example uses the deprecated "volume.external.name" (replaced by "volume.name")
external:
name: my-cool-volume
external-volume3:
# Specifies that a pre-existing volume called "this-is-volume3"
# can be referred to within this file as "external-volume3"
name: this-is-volume3
external: true
@@ -444,6 +444,12 @@ func LoadVolumes(source map[string]interface{}) (map[string]types.VolumeConfig,
if volume.External.Name == "" {
volume.External.Name = name
volumes[name] = volume
} else {
logrus.Warnf("volume %s: volume.external.name is deprecated in favor of volume.name", name)
if volume.Name != "" {
return nil, errors.Errorf("volume %s: volume.external.name and volume.name conflict; only use volume.name", name)
}
}
}
}
@@ -628,6 +628,22 @@ volumes:
assert.Contains(t, err.Error(), "external_volume")
}
func TestInvalidExternalNameAndNameCombination(t *testing.T) {
_, err := loadYAML(`
version: "3.4"
volumes:
external_volume:
name: user_specified_name
external:
name: external_name
`)
assert.Error(t, err)
fmt.Println(err)
assert.Contains(t, err.Error(), "volume.external.name and volume.name conflict; only use volume.name")
assert.Contains(t, err.Error(), "external_volume")
}
func durationPtr(value time.Duration) *time.Duration {
return &value
}
@@ -671,6 +687,7 @@ func TestFullExample(t *testing.T) {
FailureAction: "continue",
Monitor: time.Duration(60 * time.Second),
MaxFailureRatio: 0.3,
Order: "start-first",
},
Resources: types.Resources{
Limits: &types.Resource{
@@ -983,6 +1000,14 @@ func TestFullExample(t *testing.T) {
"baz": "1",
},
},
"another-volume": {
Name: "user_specified_name",
Driver: "vsphere",
DriverOpts: map[string]string{
"foo": "bar",
"baz": "1",
},
},
"external-volume": {
External: types.External{
Name: "external-volume",
@@ -995,6 +1020,13 @@ func TestFullExample(t *testing.T) {
External: true,
},
},
"external-volume3": {
Name: "this-is-volume3",
External: types.External{
Name: "external-volume3",
External: true,
},
},
}
assert.Equal(t, expectedVolumeConfig, config.Volumes)
File diff suppressed because one or more lines are too long
@@ -0,0 +1,538 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "config_schema_v3.4.json",
"type": "object",
"required": ["version"],
"properties": {
"version": {
"type": "string"
},
"services": {
"id": "#/properties/services",
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9._-]+$": {
"$ref": "#/definitions/service"
}
},
"additionalProperties": false
},
"networks": {
"id": "#/properties/networks",
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9._-]+$": {
"$ref": "#/definitions/network"
}
}
},
"volumes": {
"id": "#/properties/volumes",
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9._-]+$": {
"$ref": "#/definitions/volume"
}
},
"additionalProperties": false
},
"secrets": {
"id": "#/properties/secrets",
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9._-]+$": {
"$ref": "#/definitions/secret"
}
},
"additionalProperties": false
},
"configs": {
"id": "#/properties/configs",
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9._-]+$": {
"$ref": "#/definitions/config"
}
},
"additionalProperties": false
}
},
"additionalProperties": false,
"definitions": {
"service": {
"id": "#/definitions/service",
"type": "object",
"properties": {
"deploy": {"$ref": "#/definitions/deployment"},
"build": {
"oneOf": [
{"type": "string"},
{
"type": "object",
"properties": {
"context": {"type": "string"},
"dockerfile": {"type": "string"},
"args": {"$ref": "#/definitions/list_or_dict"},
"labels": {"$ref": "#/definitions/list_or_dict"},
"cache_from": {"$ref": "#/definitions/list_of_strings"}
},
"additionalProperties": false
}
]
},
"cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"cgroup_parent": {"type": "string"},
"command": {
"oneOf": [
{"type": "string"},
{"type": "array", "items": {"type": "string"}}
]
},
"configs": {
"type": "array",
"items": {
"oneOf": [
{"type": "string"},
{
"type": "object",
"properties": {
"source": {"type": "string"},
"target": {"type": "string"},
"uid": {"type": "string"},
"gid": {"type": "string"},
"mode": {"type": "number"}
}
}
]
}
},
"container_name": {"type": "string"},
"credential_spec": {"type": "object", "properties": {
"file": {"type": "string"},
"registry": {"type": "string"}
}},
"depends_on": {"$ref": "#/definitions/list_of_strings"},
"devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"dns": {"$ref": "#/definitions/string_or_list"},
"dns_search": {"$ref": "#/definitions/string_or_list"},
"domainname": {"type": "string"},
"entrypoint": {
"oneOf": [
{"type": "string"},
{"type": "array", "items": {"type": "string"}}
]
},
"env_file": {"$ref": "#/definitions/string_or_list"},
"environment": {"$ref": "#/definitions/list_or_dict"},
"expose": {
"type": "array",
"items": {
"type": ["string", "number"],
"format": "expose"
},
"uniqueItems": true
},
"external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
"healthcheck": {"$ref": "#/definitions/healthcheck"},
"hostname": {"type": "string"},
"image": {"type": "string"},
"ipc": {"type": "string"},
"labels": {"$ref": "#/definitions/list_or_dict"},
"links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"logging": {
"type": "object",
"properties": {
"driver": {"type": "string"},
"options": {
"type": "object",
"patternProperties": {
"^.+$": {"type": ["string", "number", "null"]}
}
}
},
"additionalProperties": false
},
"mac_address": {"type": "string"},
"network_mode": {"type": "string"},
"networks": {
"oneOf": [
{"$ref": "#/definitions/list_of_strings"},
{
"type": "object",
"patternProperties": {
"^[a-zA-Z0-9._-]+$": {
"oneOf": [
{
"type": "object",
"properties": {
"aliases": {"$ref": "#/definitions/list_of_strings"},
"ipv4_address": {"type": "string"},
"ipv6_address": {"type": "string"}
},
"additionalProperties": false
},
{"type": "null"}
]
}
},
"additionalProperties": false
}
]
},
"pid": {"type": ["string", "null"]},
"ports": {
"type": "array",
"items": {
"oneOf": [
{"type": "number", "format": "ports"},
{"type": "string", "format": "ports"},
{
"type": "object",
"properties": {
"mode": {"type": "string"},
"target": {"type": "integer"},
"published": {"type": "integer"},
"protocol": {"type": "string"}
},
"additionalProperties": false
}
]
},
"uniqueItems": true
},
"privileged": {"type": "boolean"},
"read_only": {"type": "boolean"},
"restart": {"type": "string"},
"security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"shm_size": {"type": ["number", "string"]},
"secrets": {
"type": "array",
"items": {
"oneOf": [
{"type": "string"},
{
"type": "object",
"properties": {
"source": {"type": "string"},
"target": {"type": "string"},
"uid": {"type": "string"},
"gid": {"type": "string"},
"mode": {"type": "number"}
}
}
]
}
},
"sysctls": {"$ref": "#/definitions/list_or_dict"},
"stdin_open": {"type": "boolean"},
"stop_grace_period": {"type": "string", "format": "duration"},
"stop_signal": {"type": "string"},
"tmpfs": {"$ref": "#/definitions/string_or_list"},
"tty": {"type": "boolean"},
"ulimits": {
"type": "object",
"patternProperties": {
"^[a-z]+$": {
"oneOf": [
{"type": "integer"},
{
"type":"object",
"properties": {
"hard": {"type": "integer"},
"soft": {"type": "integer"}
},
"required": ["soft", "hard"],
"additionalProperties": false
}
]
}
}
},
"user": {"type": "string"},
"userns_mode": {"type": "string"},
"volumes": {
"type": "array",
"items": {
"oneOf": [
{"type": "string"},
{
"type": "object",
"required": ["type"],
"properties": {
"type": {"type": "string"},
"source": {"type": "string"},
"target": {"type": "string"},
"read_only": {"type": "boolean"},
"consistency": {"type": "string"},
"bind": {
"type": "object",
"properties": {
"propagation": {"type": "string"}
}
},
"volume": {
"type": "object",
"properties": {
"nocopy": {"type": "boolean"}
}
}
}
}
],
"uniqueItems": true
}
},
"working_dir": {"type": "string"}
},
"additionalProperties": false
},
"healthcheck": {
"id": "#/definitions/healthcheck",
"type": "object",
"additionalProperties": false,
"properties": {
"disable": {"type": "boolean"},
"interval": {"type": "string"},
"retries": {"type": "number"},
"test": {
"oneOf": [
{"type": "string"},
{"type": "array", "items": {"type": "string"}}
]
},
"timeout": {"type": "string"}
}
},
"deployment": {
"id": "#/definitions/deployment",
"type": ["object", "null"],
"properties": {
"mode": {"type": "string"},
"endpoint_mode": {"type": "string"},
"replicas": {"type": "integer"},
"labels": {"$ref": "#/definitions/list_or_dict"},
"update_config": {
"type": "object",
"properties": {
"parallelism": {"type": "integer"},
"delay": {"type": "string", "format": "duration"},
"failure_action": {"type": "string"},
"monitor": {"type": "string", "format": "duration"},
"max_failure_ratio": {"type": "number"},
"order": {"type": "string", "enum": [
"start-first", "stop-first"
]}
},
"additionalProperties": false
},
"resources": {
"type": "object",
"properties": {
"limits": {"$ref": "#/definitions/resource"},
"reservations": {"$ref": "#/definitions/resource"}
}
},
"restart_policy": {
"type": "object",
"properties": {
"condition": {"type": "string"},
"delay": {"type": "string", "format": "duration"},
"max_attempts": {"type": "integer"},
"window": {"type": "string", "format": "duration"}
},
"additionalProperties": false
},
"placement": {
"type": "object",
"properties": {
"constraints": {"type": "array", "items": {"type": "string"}},
"preferences": {
"type": "array",
"items": {
"type": "object",
"properties": {
"spread": {"type": "string"}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"resource": {
"id": "#/definitions/resource",
"type": "object",
"properties": {
"cpus": {"type": "string"},
"memory": {"type": "string"}
},
"additionalProperties": false
},
"network": {
"id": "#/definitions/network",
"type": ["object", "null"],
"properties": {
"driver": {"type": "string"},
"driver_opts": {
"type": "object",
"patternProperties": {
"^.+$": {"type": ["string", "number"]}
}
},
"ipam": {
"type": "object",
"properties": {
"driver": {"type": "string"},
"config": {
"type": "array",
"items": {
"type": "object",
"properties": {
"subnet": {"type": "string"}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
},
"external": {
"type": ["boolean", "object"],
"properties": {
"name": {"type": "string"}
},
"additionalProperties": false
},
"internal": {"type": "boolean"},
"attachable": {"type": "boolean"},
"labels": {"$ref": "#/definitions/list_or_dict"}
},
"additionalProperties": false
},
"volume": {
"id": "#/definitions/volume",
"type": ["object", "null"],
"properties": {
"name": {"type": "string"},
"driver": {"type": "string"},
"driver_opts": {
"type": "object",
"patternProperties": {
"^.+$": {"type": ["string", "number"]}
}
},
"external": {
"type": ["boolean", "object"],
"properties": {
"name": {"type": "string"}
},
"additionalProperties": false
},
"labels": {"$ref": "#/definitions/list_or_dict"}
},
"additionalProperties": false
},
"secret": {
"id": "#/definitions/secret",
"type": "object",
"properties": {
"file": {"type": "string"},
"external": {
"type": ["boolean", "object"],
"properties": {
"name": {"type": "string"}
}
},
"labels": {"$ref": "#/definitions/list_or_dict"}
},
"additionalProperties": false
},
"config": {
"id": "#/definitions/config",
"type": "object",
"properties": {
"file": {"type": "string"},
"external": {
"type": ["boolean", "object"],
"properties": {
"name": {"type": "string"}
}
},
"labels": {"$ref": "#/definitions/list_or_dict"}
},
"additionalProperties": false
},
"string_or_list": {
"oneOf": [
{"type": "string"},
{"$ref": "#/definitions/list_of_strings"}
]
},
"list_of_strings": {
"type": "array",
"items": {"type": "string"},
"uniqueItems": true
},
"list_or_dict": {
"oneOf": [
{
"type": "object",
"patternProperties": {
".+": {
"type": ["string", "number", "null"]
}
},
"additionalProperties": false
},
{"type": "array", "items": {"type": "string"}, "uniqueItems": true}
]
},
"constraints": {
"service": {
"id": "#/definitions/constraints/service",
"anyOf": [
{"required": ["build"]},
{"required": ["image"]}
],
"properties": {
"build": {
"required": ["context"]
}
}
}
}
}
}
+3 -1
View File
@@ -21,7 +21,6 @@ var UnsupportedProperties = []string{
"restart",
"security_opt",
"shm_size",
"stop_signal",
"sysctls",
"tmpfs",
"userns_mode",
@@ -187,6 +186,7 @@ type UpdateConfig struct {
FailureAction string `mapstructure:"failure_action"`
Monitor time.Duration
MaxFailureRatio float32 `mapstructure:"max_failure_ratio"`
Order string
}
// Resources the resource limits and reservations
@@ -305,6 +305,7 @@ type IPAMPool struct {
// VolumeConfig for a volume
type VolumeConfig struct {
Name string
Driver string
DriverOpts map[string]string `mapstructure:"driver_opts"`
External External
@@ -313,6 +314,7 @@ type VolumeConfig struct {
// External identifies a Volume or Network as a reference to a resource that is
// not managed, and should already exist.
// External.name is deprecated and replaced by Volume.name
type External struct {
Name string
External bool
@@ -64,5 +64,11 @@ func ServiceImage(image string) func(*swarm.Service) {
func ServicePort(port swarm.PortConfig) func(*swarm.Service) {
return func(service *swarm.Service) {
service.Spec.EndpointSpec.Ports = append(service.Spec.EndpointSpec.Ports, port)
assignedPort := port
if assignedPort.PublishedPort == 0 {
assignedPort.PublishedPort = 30000
}
service.Endpoint.Ports = append(service.Endpoint.Ports, assignedPort)
}
}
+16 -5
View File
@@ -18,7 +18,7 @@ func NoArgs(cmd *cobra.Command, args []string) error {
}
return errors.Errorf(
"\"%s\" accepts no argument(s).\nSee '%s --help'.\n\nUsage: %s\n\n%s",
"%q accepts no arguments.\nSee '%s --help'.\n\nUsage: %s\n\n%s",
cmd.CommandPath(),
cmd.CommandPath(),
cmd.UseLine(),
@@ -33,9 +33,10 @@ func RequiresMinArgs(min int) cobra.PositionalArgs {
return nil
}
return errors.Errorf(
"\"%s\" requires at least %d argument(s).\nSee '%s --help'.\n\nUsage: %s\n\n%s",
"%q requires at least %d %s.\nSee '%s --help'.\n\nUsage: %s\n\n%s",
cmd.CommandPath(),
min,
pluralize("argument", min),
cmd.CommandPath(),
cmd.UseLine(),
cmd.Short,
@@ -50,9 +51,10 @@ func RequiresMaxArgs(max int) cobra.PositionalArgs {
return nil
}
return errors.Errorf(
"\"%s\" requires at most %d argument(s).\nSee '%s --help'.\n\nUsage: %s\n\n%s",
"%q requires at most %d %s.\nSee '%s --help'.\n\nUsage: %s\n\n%s",
cmd.CommandPath(),
max,
pluralize("argument", max),
cmd.CommandPath(),
cmd.UseLine(),
cmd.Short,
@@ -67,10 +69,11 @@ func RequiresRangeArgs(min int, max int) cobra.PositionalArgs {
return nil
}
return errors.Errorf(
"\"%s\" requires at least %d and at most %d argument(s).\nSee '%s --help'.\n\nUsage: %s\n\n%s",
"%q requires at least %d and at most %d %s.\nSee '%s --help'.\n\nUsage: %s\n\n%s",
cmd.CommandPath(),
min,
max,
pluralize("argument", max),
cmd.CommandPath(),
cmd.UseLine(),
cmd.Short,
@@ -85,12 +88,20 @@ func ExactArgs(number int) cobra.PositionalArgs {
return nil
}
return errors.Errorf(
"\"%s\" requires exactly %d argument(s).\nSee '%s --help'.\n\nUsage: %s\n\n%s",
"%q requires exactly %d %s.\nSee '%s --help'.\n\nUsage: %s\n\n%s",
cmd.CommandPath(),
number,
pluralize("argument", number),
cmd.CommandPath(),
cmd.UseLine(),
cmd.Short,
)
}
}
func pluralize(word string, number int) string {
if number == 1 {
return word
}
return word + "s"
}
+141
View File
@@ -0,0 +1,141 @@
package cli
import (
"errors"
"io/ioutil"
"testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRequiresNoArgs(t *testing.T) {
testCases := []testCase{
{
validateFunc: NoArgs,
expectedError: "no error",
},
{
args: []string{"foo"},
validateFunc: NoArgs,
expectedError: "accepts no arguments.",
},
}
runTestCases(t, testCases)
}
func TestRequiresMinArgs(t *testing.T) {
testCases := []testCase{
{
validateFunc: RequiresMinArgs(0),
expectedError: "no error",
},
{
validateFunc: RequiresMinArgs(1),
expectedError: "at least 1 argument.",
},
{
args: []string{"foo"},
validateFunc: RequiresMinArgs(2),
expectedError: "at least 2 arguments.",
},
}
runTestCases(t, testCases)
}
func TestRequiresMaxArgs(t *testing.T) {
testCases := []testCase{
{
validateFunc: RequiresMaxArgs(0),
expectedError: "no error",
},
{
args: []string{"foo", "bar"},
validateFunc: RequiresMaxArgs(1),
expectedError: "at most 1 argument.",
},
{
args: []string{"foo", "bar", "baz"},
validateFunc: RequiresMaxArgs(2),
expectedError: "at most 2 arguments.",
},
}
runTestCases(t, testCases)
}
func TestRequiresRangeArgs(t *testing.T) {
testCases := []testCase{
{
validateFunc: RequiresRangeArgs(0, 0),
expectedError: "no error",
},
{
validateFunc: RequiresRangeArgs(0, 1),
expectedError: "no error",
},
{
args: []string{"foo", "bar"},
validateFunc: RequiresRangeArgs(0, 1),
expectedError: "at most 1 argument.",
},
{
args: []string{"foo", "bar", "baz"},
validateFunc: RequiresRangeArgs(0, 2),
expectedError: "at most 2 arguments.",
},
{
validateFunc: RequiresRangeArgs(1, 2),
expectedError: "at least 1 ",
},
}
runTestCases(t, testCases)
}
func TestExactArgs(t *testing.T) {
testCases := []testCase{
{
validateFunc: ExactArgs(0),
expectedError: "no error",
},
{
validateFunc: ExactArgs(1),
expectedError: "exactly 1 argument.",
},
{
validateFunc: ExactArgs(2),
expectedError: "exactly 2 arguments.",
},
}
runTestCases(t, testCases)
}
type testCase struct {
args []string
validateFunc cobra.PositionalArgs
expectedError string
}
func runTestCases(t *testing.T, testCases []testCase) {
for _, tc := range testCases {
cmd := newDummyCommand(tc.validateFunc)
cmd.SetArgs(tc.args)
cmd.SetOutput(ioutil.Discard)
err := cmd.Execute()
require.Error(t, err, "Expected an error: %s", tc.expectedError)
assert.Contains(t, err.Error(), tc.expectedError)
}
}
func newDummyCommand(validationFunc cobra.PositionalArgs) *cobra.Command {
cmd := &cobra.Command{
Use: "dummy",
Args: validationFunc,
RunE: func(cmd *cobra.Command, args []string) error {
return errors.New("no error")
},
}
return cmd
}
+207 -17
View File
@@ -29,6 +29,7 @@
# For several commands, the amount of completions can be configured by
# setting environment variables.
#
# DOCKER_COMPLETION_SHOW_CONFIG_IDS
# DOCKER_COMPLETION_SHOW_CONTAINER_IDS
# DOCKER_COMPLETION_SHOW_NETWORK_IDS
# DOCKER_COMPLETION_SHOW_NODE_IDS
@@ -70,6 +71,42 @@ __docker_q() {
docker ${host:+-H "$host"} ${config:+--config "$config"} 2>/dev/null "$@"
}
# __docker_configs returns a list of configs. Additional options to
# `docker config ls` may be specified in order to filter the list, e.g.
# `__docker_configs --filter label=stage=production`.
# By default, only names are returned.
# Set DOCKER_COMPLETION_SHOW_CONFIG_IDS=yes to also complete IDs.
# An optional first option `--id|--name` may be used to limit the
# output to the IDs or names of matching items. This setting takes
# precedence over the environment setting.
__docker_configs() {
local format
if [ "$1" = "--id" ] ; then
format='{{.ID}}'
shift
elif [ "$1" = "--name" ] ; then
format='{{.Name}}'
shift
elif [ "$DOCKER_COMPLETION_SHOW_CONFIG_IDS" = yes ] ; then
format='{{.ID}} {{.Name}}'
else
format='{{.Name}}'
fi
__docker_q config ls --format "$format" "$@"
}
# __docker_complete_configs applies completion of configs based on the current value
# of `$cur` or the value of the optional first option `--cur`, if given.
__docker_complete_configs() {
local current="$cur"
if [ "$1" = "--cur" ] ; then
current="$2"
shift 2
fi
COMPREPLY=( $(compgen -W "$(__docker_configs "$@")" -- "$current") )
}
# __docker_containers returns a list of containers. Additional options to
# `docker ps` may be specified in order to filter the list, e.g.
# `__docker_containers --filter status=running`
@@ -791,7 +828,7 @@ __docker_complete_log_options() {
local common_options2="env env-regex labels"
# awslogs does not implement the $common_options2.
local awslogs_options="$common_options1 awslogs-create-group awslogs-group awslogs-region awslogs-stream tag"
local awslogs_options="$common_options1 awslogs-create-group awslogs-datetime-format awslogs-group awslogs-multiline-pattern awslogs-region awslogs-stream tag"
local fluentd_options="$common_options1 $common_options2 fluentd-address fluentd-async-connect fluentd-buffer-limit fluentd-retry-wait fluentd-max-retries tag"
local gcplogs_options="$common_options1 $common_options2 gcp-log-cmd gcp-meta-id gcp-meta-name gcp-meta-zone gcp-project"
@@ -1111,6 +1148,117 @@ _docker_checkpoint_rm() {
}
_docker_config() {
local subcommands="
create
inspect
ls
rm
"
local aliases="
list
remove
"
__docker_subcommands "$subcommands $aliases" && return
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
;;
*)
COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
;;
esac
}
_docker_config_create() {
case "$prev" in
--label|-l)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help --label -l" -- "$cur" ) )
;;
*)
local counter=$(__docker_pos_first_nonflag '--label|-l')
if [ "$cword" -eq "$((counter + 1))" ]; then
_filedir
fi
;;
esac
}
_docker_config_inspect() {
case "$prev" in
--format|-f)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--format -f --help --pretty" -- "$cur" ) )
;;
*)
__docker_complete_configs
;;
esac
}
_docker_config_list() {
_docker_config_ls
}
_docker_config_ls() {
local key=$(__docker_map_key_of_current_option '--filter|-f')
case "$key" in
id)
__docker_complete_configs --cur "${cur##*=}" --id
return
;;
name)
__docker_complete_configs --cur "${cur##*=}" --name
return
;;
esac
case "$prev" in
--filter|-f)
COMPREPLY=( $( compgen -S = -W "id label name" -- "$cur" ) )
__docker_nospace
return
;;
--format)
return
;;
esac
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--format --filter -f --help --quiet -q" -- "$cur" ) )
;;
esac
}
_docker_config_remove() {
_docker_config_rm
}
_docker_config_rm() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
;;
*)
__docker_complete_configs
;;
esac
}
_docker_container() {
local subcommands="
attach
@@ -2086,6 +2234,10 @@ _docker_daemon() {
COMPREPLY=( $( compgen -W "ext4 xfs" -- "${cur##*=}" ) )
return
;;
dm.libdm_log_level)
COMPREPLY=( $( compgen -W "2 3 4 5 6 7" -- "${cur##*=}" ) )
return
;;
esac
case "$prev" in
@@ -2127,6 +2279,7 @@ _docker_daemon() {
dm.blocksize
dm.directlvm_device
dm.fs
dm.libdm_log_level
dm.loopdatasize
dm.loopmetadatasize
dm.min_free_space
@@ -2284,12 +2437,14 @@ _docker_image_build() {
--cpu-period
--cpu-quota
--file -f
--iidfile
--label
--memory -m
--memory-swap
--network
--shm-size
--tag -t
--target
--ulimit
"
__docker_daemon_os_is windows && options_with_args+="
@@ -2328,7 +2483,7 @@ _docker_image_build() {
__docker_complete_image_repos_and_tags
return
;;
--file|-f)
--file|-f|--iidfile)
_filedir
return
;;
@@ -2356,6 +2511,19 @@ _docker_image_build() {
__docker_complete_image_repos_and_tags
return
;;
--target)
local context_pos=$( __docker_pos_first_nonflag "$( __docker_to_alternatives "$options_with_args" )" )
local context="${words[$context_pos]}"
context="${context:-.}"
local file="$( __docker_value_of_option '--file|f' )"
local default_file="${context%/}/Dockerfile"
local dockerfile="${file:-$default_file}"
local targets="$( sed -n 's/^FROM .\+ AS \(.\+\)/\1/p' "$dockerfile" 2>/dev/null )"
COMPREPLY=( $( compgen -W "$targets" -- "$cur" ) )
return
;;
$(__docker_to_extglob "$options_with_args") )
return
;;
@@ -2430,7 +2598,7 @@ _docker_image_inspect() {
_docker_image_load() {
case "$prev" in
--input|-i)
--input|-i|"<")
_filedir
return
;;
@@ -2561,7 +2729,7 @@ _docker_image_rmi() {
_docker_image_save() {
case "$prev" in
--output|-o)
--output|-o|">")
_filedir
return
;;
@@ -2702,7 +2870,7 @@ _docker_login() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help --password -p --username -u" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--help --password -p --password-stdin --username -u" -- "$cur" ) )
;;
esac
}
@@ -3050,7 +3218,7 @@ _docker_service_rm() {
_docker_service_scale() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--detach=false -d=false --help" -- "$cur" ) )
;;
*)
__docker_complete_services
@@ -3093,10 +3261,7 @@ _docker_service_ps() {
COMPREPLY=( $( compgen -W "--filter -f --format --help --no-resolve --no-trunc --quiet -q" -- "$cur" ) )
;;
*)
local counter=$(__docker_pos_first_nonflag '--filter|-f')
if [ "$cword" -eq "$counter" ]; then
__docker_complete_services
fi
__docker_complete_services
;;
esac
}
@@ -3152,7 +3317,7 @@ _docker_service_update_and_create() {
"
local boolean_options="
--detach -d
--detach=false -d=false
--help
--no-healthcheck
--read-only
@@ -3164,6 +3329,7 @@ _docker_service_update_and_create() {
if [ "$subcommand" = "create" ] ; then
options_with_args="$options_with_args
--config
--constraint
--container-label
--dns
@@ -3181,6 +3347,10 @@ _docker_service_update_and_create() {
"
case "$prev" in
--config)
__docker_complete_configs
return
;;
--env-file)
_filedir
return
@@ -3215,6 +3385,8 @@ _docker_service_update_and_create() {
if [ "$subcommand" = "update" ] ; then
options_with_args="$options_with_args
--args
--config-add
--config-rm
--constraint-add
--constraint-rm
--container-label-add
@@ -3242,6 +3414,10 @@ _docker_service_update_and_create() {
"
case "$prev" in
--config-add|--config-rm)
__docker_complete_configs
return
;;
--group-add|--group-rm)
COMPREPLY=( $(compgen -g -- "$cur") )
return
@@ -3834,7 +4010,7 @@ _docker_plugin_ls() {
local key=$(__docker_map_key_of_current_option '--filter|-f')
case "$key" in
capability)
COMPREPLY=( $( compgen -W "authz ipamdriver networkdriver volumedriver" -- "${cur##*=}" ) )
COMPREPLY=( $( compgen -W "authz ipamdriver logdriver metricscollector networkdriver volumedriver" -- "${cur##*=}" ) )
return
;;
enabled)
@@ -3999,6 +4175,12 @@ _docker_secret_create() {
-*)
COMPREPLY=( $( compgen -W "--help --label -l" -- "$cur" ) )
;;
*)
local counter=$(__docker_pos_first_nonflag '--label|-l')
if [ "$cword" -eq "$((counter + 1))" ]; then
_filedir
fi
;;
esac
}
@@ -4011,7 +4193,7 @@ _docker_secret_inspect() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--format -f --help" -- "$cur" ) )
COMPREPLY=( $( compgen -W "--format -f --help --pretty" -- "$cur" ) )
;;
*)
__docker_complete_secrets
@@ -4055,6 +4237,10 @@ _docker_secret_ls() {
}
_docker_secret_remove() {
_docker_secret_rm
}
_docker_secret_rm() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
@@ -4065,10 +4251,6 @@ _docker_secret_remove() {
esac
}
_docker_secret_rm() {
_docker_secret_remove
}
_docker_search() {
@@ -4149,6 +4331,12 @@ _docker_stack_deploy() {
__docker_daemon_is_experimental && options+=" --bundle-file"
COMPREPLY=( $( compgen -W "$options" -- "$cur" ) )
;;
*)
local counter=$(__docker_pos_first_nonflag '--compose-file|-c|--bundle-file')
if [ "$cword" -eq "$counter" ]; then
__docker_complete_stacks
fi
;;
esac
}
@@ -4226,6 +4414,7 @@ _docker_stack_rm() {
;;
*)
__docker_complete_stacks
;;
esac
}
@@ -4613,6 +4802,7 @@ _docker() {
shopt -s extglob
local management_commands=(
config
container
image
network
@@ -229,6 +229,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from load' -s i -l input -d
complete -c docker -f -n '__fish_docker_no_subcommand' -a login -d 'Log in to a Docker registry server'
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -l help -d 'Print usage'
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s p -l password -d 'Password'
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -l password-stdin -d 'Read password from stdin'
complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s u -l username -d 'Username'
# logout
+17 -14
View File
@@ -221,18 +221,19 @@ __docker_get_log_options() {
integer ret=1
local log_driver=${opt_args[--log-driver]:-"all"}
local -a common_options awslogs_options fluentd_options gelf_options journald_options json_file_options logentries_options syslog_options splunk_options
local -a common_options common_options2 awslogs_options fluentd_options gelf_options journald_options json_file_options logentries_options syslog_options splunk_options
common_options=("max-buffer-size" "mode")
awslogs_options=($common_options "awslogs-region" "awslogs-group" "awslogs-stream" "awslogs-create-group")
fluentd_options=($common_options "env" "fluentd-address" "fluentd-async-connect" "fluentd-buffer-limit" "fluentd-retry-wait" "fluentd-max-retries" "labels" "tag")
gcplogs_options=($common_options "env" "gcp-log-cmd" "gcp-project" "labels")
gelf_options=($common_options "env" "gelf-address" "gelf-compression-level" "gelf-compression-type" "labels" "tag")
journald_options=($common_options "env" "labels" "tag")
json_file_options=($common_options "env" "labels" "max-file" "max-size")
logentries_options=($common_options "logentries-token")
syslog_options=($common_options "env" "labels" "syslog-address" "syslog-facility" "syslog-format" "syslog-tls-ca-cert" "syslog-tls-cert" "syslog-tls-key" "syslog-tls-skip-verify" "tag")
splunk_options=($common_options "env" "labels" "splunk-caname" "splunk-capath" "splunk-format" "splunk-gzip" "splunk-gzip-level" "splunk-index" "splunk-insecureskipverify" "splunk-source" "splunk-sourcetype" "splunk-token" "splunk-url" "splunk-verify-connection" "tag")
common_options2=("env" "env-regex" "labels")
awslogs_options=($common_options "awslogs-create-group" "awslogs-datetime-format" "awslogs-group" "awslogs-multiline-pattern" "awslogs-region" "awslogs-stream" "tag")
fluentd_options=($common_options $common_options2 "fluentd-address" "fluentd-async-connect" "fluentd-buffer-limit" "fluentd-retry-wait" "fluentd-max-retries" "tag")
gcplogs_options=($common_options $common_options2 "gcp-log-cmd" "gcp-meta-id" "gcp-meta-name" "gcp-meta-zone" "gcp-project")
gelf_options=($common_options $common_options2 "gelf-address" "gelf-compression-level" "gelf-compression-type" "tag")
journald_options=($common_options $common_options2 "tag")
json_file_options=($common_options $common_options2 "max-file" "max-size")
logentries_options=($common_options $common_options2 "logentries-token" "tag")
syslog_options=($common_options $common_options2 "syslog-address" "syslog-facility" "syslog-format" "syslog-tls-ca-cert" "syslog-tls-cert" "syslog-tls-key" "syslog-tls-skip-verify" "tag")
splunk_options=($common_options $common_options2 "splunk-caname" "splunk-capath" "splunk-format" "splunk-gzip" "splunk-gzip-level" "splunk-index" "splunk-insecureskipverify" "splunk-source" "splunk-sourcetype" "splunk-token" "splunk-url" "splunk-verify-connection" "tag")
[[ $log_driver = (awslogs|all) ]] && _describe -t awslogs-options "awslogs options" awslogs_options "$@" && ret=0
[[ $log_driver = (fluentd|all) ]] && _describe -t fluentd-options "fluentd options" fluentd_options "$@" && ret=0
@@ -1520,7 +1521,7 @@ __docker_plugin_complete_ls_filters() {
if compset -P '*='; then
case "${${words[-1]%=*}#*=}" in
(capability)
opts=('authz' 'ipamdriver' 'networkdriver' 'volumedriver')
opts=('authz' 'ipamdriver' 'logdriver' 'metricscollector' 'networkdriver' 'volumedriver')
_describe -t capability-opts "capability options" opts && ret=0
;;
(enabled)
@@ -1960,7 +1961,6 @@ __docker_service_subcommand() {
"($help)*--mount=[Attach a filesystem mount to the service]:mount: "
"($help)*--network=[Network attachments]:network: "
"($help)--no-healthcheck[Disable any container-specified HEALTHCHECK]"
"($help)*"{-p=,--publish=}"[Publish a port as a node port]:port: "
"($help)--read-only[Mount the container's root filesystem as read only]"
"($help)--replicas=[Number of tasks]:replicas: "
"($help)--reserve-cpu=[Reserve CPUs]:value: "
@@ -2001,7 +2001,7 @@ __docker_service_subcommand() {
"($help)--mode=[Service Mode]:mode:(global replicated)" \
"($help)--name=[Service name]:name: " \
"($help)*--placement-pref=[Add a placement preference]:pref:__docker_service_complete_placement_pref" \
"($help)*--publish=[Publish a port]:port: " \
"($help)*"{-p=,--publish=}"[Publish a port as a node port]:port: " \
"($help -): :__docker_complete_images" \
"($help -):command: _command_names -e" \
"($help -)*::arguments: _normal" && ret=0
@@ -2040,6 +2040,7 @@ __docker_service_subcommand() {
(scale)
_arguments $(__docker_arguments) \
$opts_help \
"($help -d --detach)"{-d=false,--detach=false}"[Disable detached mode]" \
"($help -)*:service:->values" && ret=0
case $state in
(values)
@@ -2376,7 +2377,8 @@ __docker_system_subcommand() {
$opts_help \
"($help -a --all)"{-a,--all}"[Remove all unused data, not just dangling ones]" \
"($help)*--filter=[Filter values]:filter:__docker_complete_prune_filters" \
"($help -f --force)"{-f,--force}"[Do not prompt for confirmation]" && ret=0
"($help -f --force)"{-f,--force}"[Do not prompt for confirmation]" \
"($help)--volumes=[Remove all unused volumes]" && ret=0
;;
(help)
_arguments $(__docker_arguments) ":subcommand:__docker_volume_commands" && ret=0
@@ -2761,6 +2763,7 @@ __docker_subcommand() {
_arguments $(__docker_arguments) -A '-*' \
$opts_help \
"($help -p --password)"{-p=,--password=}"[Password]:password: " \
"($help)--password-stdin[Read password from stdin]" \
"($help -u --user)"{-u=,--user=}"[Username]:username: " \
"($help -)1:server: " && ret=0
;;
+2 -1
View File
@@ -24,6 +24,7 @@ RUN go get -d github.com/dnephin/filewatcher && \
go build -v -o /usr/bin/filewatcher . && \
rm -rf /go/src/* /go/pkg/* /go/bin/*
ENV CGO_ENABLED=0
ENV CGO_ENABLED=0 \
PATH=$PATH:/go/src/github.com/docker/cli/build
WORKDIR /go/src/github.com/docker/cli
CMD sh
+2 -2
View File
@@ -1,7 +1,7 @@
# The non-reference docs have been moved!
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
+2 -2
View File
@@ -5,8 +5,8 @@ description: "Deprecated Features."
keywords: "docker, documentation, about, technology, deprecate"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
+2 -2
View File
@@ -4,8 +4,8 @@ keywords: "API, Usage, plugins, documentation, developer, amazon, ebs, rexray, v
title: Volume plugin for Amazon EBS
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
+2 -2
View File
@@ -4,8 +4,8 @@ description: "How develop and use a plugin with the managed plugin system"
keywords: "API, Usage, plugins, documentation, developer"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
+4 -4
View File
@@ -4,8 +4,8 @@ keywords: "API, Usage, plugins, documentation, developer"
title: Managed plugin system
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -189,13 +189,13 @@ Stdout of a plugin is redirected to dockerd logs. Such entries have a
corresponding log entries in the docker daemon logs.
```bash
$ docker plugin install tiborvass/sample-volume-plugins
$ docker plugin install tiborvass/sample-volume-plugin
INFO[0036] Starting... Found 0 volumes on startup plugin=f52a3df433b9aceee436eaada0752f5797aab1de47e5485f1690a073b860ff62
```
```bash
$ docker volume create -d tiborvass/sample-volume-plugins samplevol
$ docker volume create -d tiborvass/sample-volume-plugin samplevol
INFO[0193] Create Called... Ensuring directory /data/samplevol exists on host... plugin=f52a3df433b9aceee436eaada0752f5797aab1de47e5485f1690a073b860ff62
INFO[0193] open /var/lib/docker/plugin-data/local-persist.json: no such file or directory plugin=f52a3df433b9aceee436eaada0752f5797aab1de47e5485f1690a073b860ff62
+40 -39
View File
@@ -6,8 +6,8 @@ description: "How to add additional functionality to Docker with plugins extensi
keywords: "Examples, Usage, plugins, docker, documentation, user guide"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -47,49 +47,50 @@ The sections below provide an inexhaustive overview of available plugins.
### Network plugins
Plugin | Description
----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
[Contiv Networking](https://github.com/contiv/netplugin) | An open source network plugin to provide infrastructure and security policies for a multi-tenant micro services deployment, while providing an integration to physical network for non-container workload. Contiv Networking implements the remote driver and IPAM APIs available in Docker 1.9 onwards.
[Kuryr Network Plugin](https://github.com/openstack/kuryr) | A network plugin is developed as part of the OpenStack Kuryr project and implements the Docker networking (libnetwork) remote driver API by utilizing Neutron, the OpenStack networking service. It includes an IPAM driver as well.
[Weave Network Plugin](https://www.weave.works/docs/net/latest/introducing-weave/) | A network plugin that creates a virtual network that connects your Docker containers - across multiple hosts or clouds and enables automatic discovery of applications. Weave networks are resilient, partition tolerant, secure and work in partially connected networks, and other adverse environments - all configured with delightful simplicity.
| Plugin | Description |
|:-----------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Contiv Networking](https://github.com/contiv/netplugin) | An open source network plugin to provide infrastructure and security policies for a multi-tenant micro services deployment, while providing an integration to physical network for non-container workload. Contiv Networking implements the remote driver and IPAM APIs available in Docker 1.9 onwards. |
| [Kuryr Network Plugin](https://github.com/openstack/kuryr) | A network plugin is developed as part of the OpenStack Kuryr project and implements the Docker networking (libnetwork) remote driver API by utilizing Neutron, the OpenStack networking service. It includes an IPAM driver as well. |
| [Weave Network Plugin](https://www.weave.works/docs/net/latest/introducing-weave/) | A network plugin that creates a virtual network that connects your Docker containers - across multiple hosts or clouds and enables automatic discovery of applications. Weave networks are resilient, partition tolerant, secure and work in partially connected networks, and other adverse environments - all configured with delightful simplicity. |
### Volume plugins
Plugin | Description
----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
[Azure File Storage plugin](https://github.com/Azure/azurefile-dockervolumedriver) | Lets you mount Microsoft [Azure File Storage](https://azure.microsoft.com/blog/azure-file-storage-now-generally-available/) shares to Docker containers as volumes using the SMB 3.0 protocol. [Learn more](https://azure.microsoft.com/blog/persistent-docker-volumes-with-azure-file-storage/).
[BeeGFS Volume Plugin](https://github.com/RedCoolBeans/docker-volume-beegfs) | An open source volume plugin to create persistent volumes in a BeeGFS parallel file system.
[Blockbridge plugin](https://github.com/blockbridge/blockbridge-docker-volume) | A volume plugin that provides access to an extensible set of container-based persistent storage options. It supports single and multi-host Docker environments with features that include tenant isolation, automated provisioning, encryption, secure deletion, snapshots and QoS.
[Contiv Volume Plugin](https://github.com/contiv/volplugin) | An open source volume plugin that provides multi-tenant, persistent, distributed storage with intent based consumption. It has support for Ceph and NFS.
[Convoy plugin](https://github.com/rancher/convoy) | A volume plugin for a variety of storage back-ends including device mapper and NFS. It's a simple standalone executable written in Go and provides the framework to support vendor-specific extensions such as snapshots, backups and restore.
[DigitalOcean Block Storage plugin](https://github.com/omallo/docker-volume-plugin-dostorage) | Integrates DigitalOcean's [block storage solution](https://www.digitalocean.com/products/storage/) into the Docker ecosystem by automatically attaching a given block storage volume to a DigitalOcean droplet and making the contents of the volume available to Docker containers running on that droplet.
[DRBD plugin](https://www.drbd.org/en/supported-projects/docker) | A volume plugin that provides highly available storage replicated by [DRBD](https://www.drbd.org). Data written to the docker volume is replicated in a cluster of DRBD nodes.
[Flocker plugin](https://clusterhq.com/docker-plugin/) | A volume plugin that provides multi-host portable volumes for Docker, enabling you to run databases and other stateful containers and move them around across a cluster of machines.
[Fuxi Volume Plugin](https://github.com/openstack/fuxi) | A volume plugin that is developed as part of the OpenStack Kuryr project and implements the Docker volume plugin API by utilizing Cinder, the OpenStack block storage service.
[gce-docker plugin](https://github.com/mcuadros/gce-docker) | A volume plugin able to attach, format and mount Google Compute [persistent-disks](https://cloud.google.com/compute/docs/disks/persistent-disks).
[GlusterFS plugin](https://github.com/calavera/docker-volume-glusterfs) | A volume plugin that provides multi-host volumes management for Docker using GlusterFS.
[Horcrux Volume Plugin](https://github.com/muthu-r/horcrux) | A volume plugin that allows on-demand, version controlled access to your data. Horcrux is an open-source plugin, written in Go, and supports SCP, [Minio](https://www.minio.io) and Amazon S3.
[HPE 3Par Volume Plugin](https://github.com/hpe-storage/python-hpedockerplugin/) | A volume plugin that supports HPE 3Par and StoreVirtual iSCSI storage arrays.
[IPFS Volume Plugin](http://github.com/vdemeester/docker-volume-ipfs) | An open source volume plugin that allows using an [ipfs](https://ipfs.io/) filesystem as a volume.
[Keywhiz plugin](https://github.com/calavera/docker-volume-keywhiz) | A plugin that provides credentials and secret management using Keywhiz as a central repository.
[Local Persist Plugin](https://github.com/CWSpear/local-persist) | A volume plugin that extends the default `local` driver's functionality by allowing you specify a mountpoint anywhere on the host, which enables the files to *always persist*, even if the volume is removed via `docker volume rm`.
[NetApp Plugin](https://github.com/NetApp/netappdvp) (nDVP) | A volume plugin that provides direct integration with the Docker ecosystem for the NetApp storage portfolio. The nDVP package supports the provisioning and management of storage resources from the storage platform to Docker hosts, with a robust framework for adding additional platforms in the future.
[Netshare plugin](https://github.com/ContainX/docker-volume-netshare) | A volume plugin that provides volume management for NFS 3/4, AWS EFS and CIFS file systems.
[Nimble Storage Volume Plugin](https://connect.nimblestorage.com/community/app-integration/docker)| A volume plug-in that integrates with Nimble Storage Unified Flash Fabric arrays. The plug-in abstracts array volume capabilities to the Docker administrator to allow self-provisioning of secure multi-tenant volumes and clones.
[OpenStorage Plugin](https://github.com/libopenstorage/openstorage) | A cluster-aware volume plugin that provides volume management for file and block storage solutions. It implements a vendor neutral specification for implementing extensions such as CoS, encryption, and snapshots. It has example drivers based on FUSE, NFS, NBD and EBS to name a few.
[Portworx Volume Plugin](https://github.com/portworx/px-dev) | A volume plugin that turns any server into a scale-out converged compute/storage node, providing container granular storage and highly available volumes across any node, using a shared-nothing storage backend that works with any docker scheduler.
[Quobyte Volume Plugin](https://github.com/quobyte/docker-volume) | A volume plugin that connects Docker to [Quobyte](http://www.quobyte.com/containers)'s data center file system, a general-purpose scalable and fault-tolerant storage platform.
[REX-Ray plugin](https://github.com/emccode/rexray) | A volume plugin which is written in Go and provides advanced storage functionality for many platforms including VirtualBox, EC2, Google Compute Engine, OpenStack, and EMC.
[Virtuozzo Storage and Ploop plugin](https://github.com/virtuozzo/docker-volume-ploop) | A volume plugin with support for Virtuozzo Storage distributed cloud file system as well as ploop devices.
[VMware vSphere Storage Plugin](https://github.com/vmware/docker-volume-vsphere) | Docker Volume Driver for vSphere enables customers to address persistent storage requirements for Docker containers in vSphere environments.
| Plugin | Description |
|:---------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Azure File Storage plugin](https://github.com/Azure/azurefile-dockervolumedriver) | Lets you mount Microsoft [Azure File Storage](https://azure.microsoft.com/blog/azure-file-storage-now-generally-available/) shares to Docker containers as volumes using the SMB 3.0 protocol. [Learn more](https://azure.microsoft.com/blog/persistent-docker-volumes-with-azure-file-storage/). |
| [BeeGFS Volume Plugin](https://github.com/RedCoolBeans/docker-volume-beegfs) | An open source volume plugin to create persistent volumes in a BeeGFS parallel file system. |
| [Blockbridge plugin](https://github.com/blockbridge/blockbridge-docker-volume) | A volume plugin that provides access to an extensible set of container-based persistent storage options. It supports single and multi-host Docker environments with features that include tenant isolation, automated provisioning, encryption, secure deletion, snapshots and QoS. |
| [Contiv Volume Plugin](https://github.com/contiv/volplugin) | An open source volume plugin that provides multi-tenant, persistent, distributed storage with intent based consumption. It has support for Ceph and NFS. |
| [Convoy plugin](https://github.com/rancher/convoy) | A volume plugin for a variety of storage back-ends including device mapper and NFS. It's a simple standalone executable written in Go and provides the framework to support vendor-specific extensions such as snapshots, backups and restore. |
| [DigitalOcean Block Storage plugin](https://github.com/omallo/docker-volume-plugin-dostorage) | Integrates DigitalOcean's [block storage solution](https://www.digitalocean.com/products/storage/) into the Docker ecosystem by automatically attaching a given block storage volume to a DigitalOcean droplet and making the contents of the volume available to Docker containers running on that droplet. |
| [DRBD plugin](https://www.drbd.org/en/supported-projects/docker) | A volume plugin that provides highly available storage replicated by [DRBD](https://www.drbd.org). Data written to the docker volume is replicated in a cluster of DRBD nodes. |
| [Flocker plugin](https://clusterhq.com/docker-plugin/) | A volume plugin that provides multi-host portable volumes for Docker, enabling you to run databases and other stateful containers and move them around across a cluster of machines. |
| [Fuxi Volume Plugin](https://github.com/openstack/fuxi) | A volume plugin that is developed as part of the OpenStack Kuryr project and implements the Docker volume plugin API by utilizing Cinder, the OpenStack block storage service. |
| [gce-docker plugin](https://github.com/mcuadros/gce-docker) | A volume plugin able to attach, format and mount Google Compute [persistent-disks](https://cloud.google.com/compute/docs/disks/persistent-disks). |
| [GlusterFS plugin](https://github.com/calavera/docker-volume-glusterfs) | A volume plugin that provides multi-host volumes management for Docker using GlusterFS. |
| [Horcrux Volume Plugin](https://github.com/muthu-r/horcrux) | A volume plugin that allows on-demand, version controlled access to your data. Horcrux is an open-source plugin, written in Go, and supports SCP, [Minio](https://www.minio.io) and Amazon S3. |
| [HPE 3Par Volume Plugin](https://github.com/hpe-storage/python-hpedockerplugin/) | A volume plugin that supports HPE 3Par and StoreVirtual iSCSI storage arrays. |
| [Infinit volume plugin](https://infinit.sh/documentation/docker/volume-plugin) | A volume plugin that makes it easy to mount and manage Infinit volumes using Docker. |
| [IPFS Volume Plugin](http://github.com/vdemeester/docker-volume-ipfs) | An open source volume plugin that allows using an [ipfs](https://ipfs.io/) filesystem as a volume. |
| [Keywhiz plugin](https://github.com/calavera/docker-volume-keywhiz) | A plugin that provides credentials and secret management using Keywhiz as a central repository. |
| [Local Persist Plugin](https://github.com/CWSpear/local-persist) | A volume plugin that extends the default `local` driver's functionality by allowing you specify a mountpoint anywhere on the host, which enables the files to *always persist*, even if the volume is removed via `docker volume rm`. |
| [NetApp Plugin](https://github.com/NetApp/netappdvp) (nDVP) | A volume plugin that provides direct integration with the Docker ecosystem for the NetApp storage portfolio. The nDVP package supports the provisioning and management of storage resources from the storage platform to Docker hosts, with a robust framework for adding additional platforms in the future. |
| [Netshare plugin](https://github.com/ContainX/docker-volume-netshare) | A volume plugin that provides volume management for NFS 3/4, AWS EFS and CIFS file systems. |
| [Nimble Storage Volume Plugin](https://connect.nimblestorage.com/community/app-integration/docker) | A volume plug-in that integrates with Nimble Storage Unified Flash Fabric arrays. The plug-in abstracts array volume capabilities to the Docker administrator to allow self-provisioning of secure multi-tenant volumes and clones. |
| [OpenStorage Plugin](https://github.com/libopenstorage/openstorage) | A cluster-aware volume plugin that provides volume management for file and block storage solutions. It implements a vendor neutral specification for implementing extensions such as CoS, encryption, and snapshots. It has example drivers based on FUSE, NFS, NBD and EBS to name a few. |
| [Portworx Volume Plugin](https://github.com/portworx/px-dev) | A volume plugin that turns any server into a scale-out converged compute/storage node, providing container granular storage and highly available volumes across any node, using a shared-nothing storage backend that works with any docker scheduler. |
| [Quobyte Volume Plugin](https://github.com/quobyte/docker-volume) | A volume plugin that connects Docker to [Quobyte](http://www.quobyte.com/containers)'s data center file system, a general-purpose scalable and fault-tolerant storage platform. |
| [REX-Ray plugin](https://github.com/emccode/rexray) | A volume plugin which is written in Go and provides advanced storage functionality for many platforms including VirtualBox, EC2, Google Compute Engine, OpenStack, and EMC. |
| [Virtuozzo Storage and Ploop plugin](https://github.com/virtuozzo/docker-volume-ploop) | A volume plugin with support for Virtuozzo Storage distributed cloud file system as well as ploop devices. |
| [VMware vSphere Storage Plugin](https://github.com/vmware/docker-volume-vsphere) | Docker Volume Driver for vSphere enables customers to address persistent storage requirements for Docker containers in vSphere environments. |
### Authorization plugins
Plugin | Description
------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
[Casbin AuthZ Plugin](https://github.com/casbin/casbin-authz-plugin) | An authorization plugin based on [Casbin](https://github.com/casbin/casbin), which supports access control models like ACL, RBAC, ABAC. The access control model can be customized. The policy can be persisted into file or DB.
[HBM plugin](https://github.com/kassisol/hbm) | An authorization plugin that prevents from executing commands with certains parameters.
[Twistlock AuthZ Broker](https://github.com/twistlock/authz) | A basic extendable authorization plugin that runs directly on the host or inside a container. This plugin allows you to define user policies that it evaluates during authorization. Basic authorization is provided if Docker daemon is started with the --tlsverify flag (username is extracted from the certificate common name).
| Plugin | Description |
|:---------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Casbin AuthZ Plugin](https://github.com/casbin/casbin-authz-plugin) | An authorization plugin based on [Casbin](https://github.com/casbin/casbin), which supports access control models like ACL, RBAC, ABAC. The access control model can be customized. The policy can be persisted into file or DB. |
| [HBM plugin](https://github.com/kassisol/hbm) | An authorization plugin that prevents from executing commands with certains parameters. |
| [Twistlock AuthZ Broker](https://github.com/twistlock/authz) | A basic extendable authorization plugin that runs directly on the host or inside a container. This plugin allows you to define user policies that it evaluates during authorization. Basic authorization is provided if Docker daemon is started with the --tlsverify flag (username is extracted from the certificate common name). |
## Troubleshooting a plugin
+2 -2
View File
@@ -4,8 +4,8 @@ description: "How to write Docker plugins extensions "
keywords: "API, Usage, plugins, documentation, developer"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -6,8 +6,8 @@ redirect_from:
- "/engine/extend/authorization/"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -5,8 +5,8 @@ keywords: "Examples, Usage, storage, image, docker, data, graph, plugin, api"
advisory: experimental
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -4,8 +4,8 @@ description: "Log driver plugins."
keywords: "Examples, Usage, plugins, docker, documentation, user guide, logging"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -4,8 +4,8 @@ description: "Metrics plugins."
keywords: "Examples, Usage, plugins, docker, documentation, user guide, metrics"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -4,8 +4,8 @@ description: "Network driver plugins."
keywords: "Examples, Usage, plugins, docker, documentation, user guide"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -4,8 +4,8 @@ keywords: "API, Usage, plugins, documentation, developer"
title: Plugins and Services
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
+2 -2
View File
@@ -4,8 +4,8 @@ description: "How to manage data with external volume plugins"
keywords: "Examples, Usage, volume, docker, data, volumes, plugin, api"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
+2 -2
View File
@@ -6,8 +6,8 @@ redirect_from:
- /reference/builder/
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -4,8 +4,8 @@ description: "The attach command description and usage"
keywords: "attach, running, container"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -4,8 +4,8 @@ description: "The build command description and usage"
keywords: "build, docker, image"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -4,8 +4,8 @@ description: "Docker's CLI command description and usage"
keywords: "Docker, Docker documentation, CLI, command line"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -4,8 +4,8 @@ description: "The commit command description and usage"
keywords: "commit, file, changes"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -5,8 +5,8 @@ description: "The container command description and usage"
keywords: "container"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -4,8 +4,8 @@ description: "Remove all stopped containers"
keywords: container, prune, delete, remove
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -4,8 +4,8 @@ description: "The cp command description and usage"
keywords: "copy, container, files, folders"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -28,6 +28,7 @@ container source to stdout.
Options:
-L, --follow-link Always follow symbol link in SRC_PATH
-a, --archive Archive mode (copy all uid/gid information)
--help Print usage
```
@@ -53,7 +54,9 @@ copied recursively with permissions preserved if possible. Ownership is set to
the user and primary group at the destination. For example, files copied to a
container are created with `UID:GID` of the root user. Files copied to the local
machine are created with the `UID:GID` of the user which invoked the `docker cp`
command. If you specify the `-L` option, `docker cp` follows any symbolic link
command. However, if you specify the `-a` option, `docker cp` sets the ownership
to the user and primary group at the source.
If you specify the `-L` option, `docker cp` follows any symbolic link
in the `SRC_PATH`. `docker cp` does *not* create parent directories for
`DEST_PATH` if they do not exist.
@@ -102,11 +105,11 @@ running `tar` in `docker exec`. Both of the following examples do the same thing
in different ways (consider `SRC_PATH` and `DEST_PATH` are directories):
```bash
$ docker exec foo tar Ccf $(dirname SRC_PATH) - $(basename SRC_PATH) | tar Cxf DEST_PATH -
$ docker exec CONTAINER tar Ccf $(dirname SRC_PATH) - $(basename SRC_PATH) | tar Cxf DEST_PATH -
```
```bash
$ tar Ccf $(dirname SRC_PATH) - $(basename SRC_PATH) | docker exec -i foo tar Cxf DEST_PATH -
$ tar Ccf $(dirname SRC_PATH) - $(basename SRC_PATH) | docker exec -i CONTAINER tar Cxf DEST_PATH -
```
Using `-` as the `SRC_PATH` streams the contents of `STDIN` as a tar archive.
@@ -4,8 +4,8 @@ description: "The create command description and usage"
keywords: "docker, create, container"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -5,8 +5,8 @@ keywords: "stack, deploy"
advisory: "experimental"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -4,8 +4,8 @@ description: "The diff command description and usage"
keywords: "list, changed, files, container"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -5,8 +5,8 @@ description: "The daemon command description and usage"
keywords: "container, daemon, runtime"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -4,7 +4,7 @@ description: "The events command description and usage"
keywords: "events, container, report"
---
<!-- This file is maintained within the docker/docker Github
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/moby/moby/. Make all
pull requests against that repo. If you see this file in
another repository, consider it read-only there, as it will
@@ -275,6 +275,12 @@ $ docker events --since '10m'
2017-01-05T00:36:09.840186338+08:00 container die 0fdb...ff37 (exitCode=143, image=alpine:latest, name=test)
2017-01-05T00:36:09.880113663+08:00 network disconnect e2e...29e2 (container=0fdb...ff37, name=bridge, type=bridge)
2017-01-05T00:36:09.890214053+08:00 container stop 0fdb...ff37 (image=alpine:latest, name=test)
$ docker events --since '2017-01-05T00:35:30' --until '2017-01-05T00:36:05'
2017-01-05T00:35:41.241772953+08:00 volume create testVol (driver=local)
2017-01-05T00:35:58.859401177+08:00 container create d9cd...4d70 (image=alpine:latest, name=test)
2017-01-05T00:36:04.703631903+08:00 network connect e2e1...29e2 (container=0fdb...ff37, name=bridge, type=bridge)
2017-01-05T00:36:04.795031609+08:00 container start 0fdb...ff37 (image=alpine:latest, name=test)
```
### Filter events by criteria
@@ -4,8 +4,8 @@ description: "The exec command description and usage"
keywords: "command, container, run, execute"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -4,8 +4,8 @@ description: "The export command description and usage"
keywords: "export, file, system, container"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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
@@ -4,8 +4,8 @@ description: "The history command description and usage"
keywords: "docker, image, history"
---
<!-- This file is maintained within the docker/docker Github
repository at https://github.com/docker/docker/. Make all
<!-- This file is maintained within the docker/cli Github
repository at https://github.com/docker/cli/. 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

Some files were not shown because too many files have changed in this diff Show More