Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4debf411d1 | |||
| 5e6ce1bde1 | |||
| 5428301e3f | |||
| 1cbc218c05 | |||
| 2f6b5ada71 | |||
| d8e07c9c47 | |||
| 5f1b610fc3 | |||
| c105cd3ac2 | |||
| 62b2963b80 | |||
| 71f2b0d109 | |||
| 617bc98c8d | |||
| 29cf629222 | |||
| 4caf4de039 | |||
| 950ecd42fd | |||
| 6ab4781bd0 | |||
| e8852e8ed2 | |||
| 4e097c643d | |||
| 01f9332618 | |||
| 4cd8d5cf47 | |||
| 21c12847bf | |||
| 22e1f2cbfa | |||
| 68abf14c15 | |||
| 85a5ee4cb0 | |||
| 9e1e07657a |
2
.github/workflows/e2e.yml
vendored
2
.github/workflows/e2e.yml
vendored
@ -59,6 +59,6 @@ jobs:
|
||||
TESTFLAGS: -coverprofile=/tmp/coverage/coverage.txt
|
||||
-
|
||||
name: Send to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
file: ./build/coverage/coverage.txt
|
||||
|
||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@ -31,7 +31,7 @@ jobs:
|
||||
targets: test-coverage
|
||||
-
|
||||
name: Send to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
file: ./build/coverage/coverage.txt
|
||||
|
||||
@ -73,7 +73,7 @@ jobs:
|
||||
shell: bash
|
||||
-
|
||||
name: Send to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
file: /tmp/coverage.txt
|
||||
working-directory: ${{ env.GOPATH }}/src/github.com/docker/cli
|
||||
|
||||
@ -9,7 +9,7 @@ ARG XX_VERSION=1.2.1
|
||||
ARG GOVERSIONINFO_VERSION=v1.3.0
|
||||
ARG GOTESTSUM_VERSION=v1.10.0
|
||||
ARG BUILDX_VERSION=0.12.1
|
||||
ARG COMPOSE_VERSION=v2.24.0
|
||||
ARG COMPOSE_VERSION=v2.24.2
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
||||
|
||||
@ -65,6 +65,7 @@ func ConnectAndWait(cb func()) {
|
||||
_, err := conn.Read(b)
|
||||
if errors.Is(err, io.EOF) {
|
||||
cb()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
133
cli-plugins/socket/socket_test.go
Normal file
133
cli-plugins/socket/socket_test.go
Normal file
@ -0,0 +1,133 @@
|
||||
package socket
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/poll"
|
||||
)
|
||||
|
||||
func TestSetupConn(t *testing.T) {
|
||||
t.Run("updates conn when connected", func(t *testing.T) {
|
||||
var conn *net.UnixConn
|
||||
listener, err := SetupConn(&conn)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, listener != nil, "returned nil listener but no error")
|
||||
addr, err := net.ResolveUnixAddr("unix", listener.Addr().String())
|
||||
assert.NilError(t, err, "failed to resolve listener address")
|
||||
|
||||
_, err = net.DialUnix("unix", nil, addr)
|
||||
assert.NilError(t, err, "failed to dial returned listener")
|
||||
|
||||
pollConnNotNil(t, &conn)
|
||||
})
|
||||
|
||||
t.Run("allows reconnects", func(t *testing.T) {
|
||||
var conn *net.UnixConn
|
||||
listener, err := SetupConn(&conn)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, listener != nil, "returned nil listener but no error")
|
||||
addr, err := net.ResolveUnixAddr("unix", listener.Addr().String())
|
||||
assert.NilError(t, err, "failed to resolve listener address")
|
||||
|
||||
otherConn, err := net.DialUnix("unix", nil, addr)
|
||||
assert.NilError(t, err, "failed to dial returned listener")
|
||||
|
||||
otherConn.Close()
|
||||
|
||||
_, err = net.DialUnix("unix", nil, addr)
|
||||
assert.NilError(t, err, "failed to redial listener")
|
||||
})
|
||||
|
||||
t.Run("does not leak sockets to local directory", func(t *testing.T) {
|
||||
var conn *net.UnixConn
|
||||
listener, err := SetupConn(&conn)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, listener != nil, "returned nil listener but no error")
|
||||
checkDirNoPluginSocket(t)
|
||||
|
||||
addr, err := net.ResolveUnixAddr("unix", listener.Addr().String())
|
||||
assert.NilError(t, err, "failed to resolve listener address")
|
||||
_, err = net.DialUnix("unix", nil, addr)
|
||||
assert.NilError(t, err, "failed to dial returned listener")
|
||||
checkDirNoPluginSocket(t)
|
||||
})
|
||||
}
|
||||
|
||||
func checkDirNoPluginSocket(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
files, err := os.ReadDir(".")
|
||||
assert.NilError(t, err, "failed to list files in dir to check for leaked sockets")
|
||||
|
||||
for _, f := range files {
|
||||
info, err := f.Info()
|
||||
assert.NilError(t, err, "failed to check file info")
|
||||
// check for a socket with `docker_cli_` in the name (from `SetupConn()`)
|
||||
if strings.Contains(f.Name(), "docker_cli_") && info.Mode().Type() == fs.ModeSocket {
|
||||
t.Fatal("found socket in a local directory")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectAndWait(t *testing.T) {
|
||||
t.Run("calls cancel func on EOF", func(t *testing.T) {
|
||||
var conn *net.UnixConn
|
||||
listener, err := SetupConn(&conn)
|
||||
assert.NilError(t, err, "failed to setup listener")
|
||||
|
||||
done := make(chan struct{})
|
||||
t.Setenv(EnvKey, listener.Addr().String())
|
||||
cancelFunc := func() {
|
||||
done <- struct{}{}
|
||||
}
|
||||
ConnectAndWait(cancelFunc)
|
||||
pollConnNotNil(t, &conn)
|
||||
conn.Close()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
t.Fatal("cancel function not closed after 10ms")
|
||||
}
|
||||
})
|
||||
|
||||
// TODO: this test cannot be executed with `t.Parallel()`, due to
|
||||
// relying on goroutine numbers to ensure correct behaviour
|
||||
t.Run("connect goroutine exits after EOF", func(t *testing.T) {
|
||||
var conn *net.UnixConn
|
||||
listener, err := SetupConn(&conn)
|
||||
assert.NilError(t, err, "failed to setup listener")
|
||||
t.Setenv(EnvKey, listener.Addr().String())
|
||||
numGoroutines := runtime.NumGoroutine()
|
||||
|
||||
ConnectAndWait(func() {})
|
||||
assert.Equal(t, runtime.NumGoroutine(), numGoroutines+1)
|
||||
|
||||
pollConnNotNil(t, &conn)
|
||||
conn.Close()
|
||||
poll.WaitOn(t, func(t poll.LogT) poll.Result {
|
||||
if runtime.NumGoroutine() > numGoroutines+1 {
|
||||
return poll.Continue("waiting for connect goroutine to exit")
|
||||
}
|
||||
return poll.Success()
|
||||
}, poll.WithDelay(1*time.Millisecond), poll.WithTimeout(10*time.Millisecond))
|
||||
})
|
||||
}
|
||||
|
||||
func pollConnNotNil(t *testing.T, conn **net.UnixConn) {
|
||||
t.Helper()
|
||||
|
||||
poll.WaitOn(t, func(t poll.LogT) poll.Result {
|
||||
if *conn == nil {
|
||||
return poll.Continue("waiting for conn to not be nil")
|
||||
}
|
||||
return poll.Success()
|
||||
}, poll.WithDelay(1*time.Millisecond), poll.WithTimeout(10*time.Millisecond))
|
||||
}
|
||||
@ -60,6 +60,7 @@ The sections below provide an overview of available third-party plugins.
|
||||
| [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](https://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. |
|
||||
| [Linode Volume Plugin](https://github.com/linode/docker-volume-linode) | A plugin that adds the ability to manage Linode Block Storage as Docker Volumes from within a Linode. |
|
||||
| [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. |
|
||||
|
||||
@ -10,105 +10,8 @@ aliases:
|
||||
- /engine/reference/commandline/engine_update/
|
||||
---
|
||||
|
||||
<!-- 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
|
||||
requests which include edits to this file in other repositories
|
||||
will be rejected.
|
||||
-->
|
||||
|
||||
# docker
|
||||
|
||||
To list available commands, either run `docker` with no parameters
|
||||
or execute `docker help`:
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
The base command for the Docker CLI.
|
||||
|
||||
### Subcommands
|
||||
|
||||
| Name | Description |
|
||||
|:------------------------------|:------------------------------------------------------------------------------|
|
||||
| [`attach`](attach.md) | Attach local standard input, output, and error streams to a running container |
|
||||
| [`build`](build.md) | Build an image from a Dockerfile |
|
||||
| [`builder`](builder.md) | Manage builds |
|
||||
| [`checkpoint`](checkpoint.md) | Manage checkpoints |
|
||||
| [`commit`](commit.md) | Create a new image from a container's changes |
|
||||
| [`config`](config.md) | Manage Swarm configs |
|
||||
| [`container`](container.md) | Manage containers |
|
||||
| [`context`](context.md) | Manage contexts |
|
||||
| [`cp`](cp.md) | Copy files/folders between a container and the local filesystem |
|
||||
| [`create`](create.md) | Create a new container |
|
||||
| [`diff`](diff.md) | Inspect changes to files or directories on a container's filesystem |
|
||||
| [`events`](events.md) | Get real time events from the server |
|
||||
| [`exec`](exec.md) | Execute a command in a running container |
|
||||
| [`export`](export.md) | Export a container's filesystem as a tar archive |
|
||||
| [`history`](history.md) | Show the history of an image |
|
||||
| [`image`](image.md) | Manage images |
|
||||
| [`images`](images.md) | List images |
|
||||
| [`import`](import.md) | Import the contents from a tarball to create a filesystem image |
|
||||
| [`info`](info.md) | Display system-wide information |
|
||||
| [`inspect`](inspect.md) | Return low-level information on Docker objects |
|
||||
| [`kill`](kill.md) | Kill one or more running containers |
|
||||
| [`load`](load.md) | Load an image from a tar archive or STDIN |
|
||||
| [`login`](login.md) | Log in to a registry |
|
||||
| [`logout`](logout.md) | Log out from a registry |
|
||||
| [`logs`](logs.md) | Fetch the logs of a container |
|
||||
| [`manifest`](manifest.md) | Manage Docker image manifests and manifest lists |
|
||||
| [`network`](network.md) | Manage networks |
|
||||
| [`node`](node.md) | Manage Swarm nodes |
|
||||
| [`pause`](pause.md) | Pause all processes within one or more containers |
|
||||
| [`plugin`](plugin.md) | Manage plugins |
|
||||
| [`port`](port.md) | List port mappings or a specific mapping for the container |
|
||||
| [`ps`](ps.md) | List containers |
|
||||
| [`pull`](pull.md) | Download an image from a registry |
|
||||
| [`push`](push.md) | Upload an image to a registry |
|
||||
| [`rename`](rename.md) | Rename a container |
|
||||
| [`restart`](restart.md) | Restart one or more containers |
|
||||
| [`rm`](rm.md) | Remove one or more containers |
|
||||
| [`rmi`](rmi.md) | Remove one or more images |
|
||||
| [`run`](run.md) | Create and run a new container from an image |
|
||||
| [`save`](save.md) | Save one or more images to a tar archive (streamed to STDOUT by default) |
|
||||
| [`search`](search.md) | Search Docker Hub for images |
|
||||
| [`secret`](secret.md) | Manage Swarm secrets |
|
||||
| [`service`](service.md) | Manage Swarm services |
|
||||
| [`stack`](stack.md) | Manage Swarm stacks |
|
||||
| [`start`](start.md) | Start one or more stopped containers |
|
||||
| [`stats`](stats.md) | Display a live stream of container(s) resource usage statistics |
|
||||
| [`stop`](stop.md) | Stop one or more running containers |
|
||||
| [`swarm`](swarm.md) | Manage Swarm |
|
||||
| [`system`](system.md) | Manage Docker |
|
||||
| [`tag`](tag.md) | Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE |
|
||||
| [`top`](top.md) | Display the running processes of a container |
|
||||
| [`trust`](trust.md) | Manage trust on Docker images |
|
||||
| [`unpause`](unpause.md) | Unpause all processes within one or more containers |
|
||||
| [`update`](update.md) | Update configuration of one or more containers |
|
||||
| [`version`](version.md) | Show the Docker version information |
|
||||
| [`volume`](volume.md) | Manage volumes |
|
||||
| [`wait`](wait.md) | Block until one or more containers stop, then print their exit codes |
|
||||
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:---------------------------------|:---------|:-------------------------|:--------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `--config` | `string` | `/root/.docker` | Location of client config files |
|
||||
| `-c`, `--context` | `string` | | Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with `docker context use`) |
|
||||
| `-D`, `--debug` | | | Enable debug mode |
|
||||
| [`-H`](#host), [`--host`](#host) | `list` | | Daemon socket to connect to |
|
||||
| `-l`, `--log-level` | `string` | `info` | Set the logging level (`debug`, `info`, `warn`, `error`, `fatal`) |
|
||||
| `--tls` | | | Use TLS; implied by --tlsverify |
|
||||
| `--tlscacert` | `string` | `/root/.docker/ca.pem` | Trust certs signed only by this CA |
|
||||
| `--tlscert` | `string` | `/root/.docker/cert.pem` | Path to TLS certificate file |
|
||||
| `--tlskey` | `string` | `/root/.docker/key.pem` | Path to TLS key file |
|
||||
| `--tlsverify` | | | Use TLS and verify the remote |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
## Description
|
||||
The base command for the Docker CLI is `docker`. For information about the
|
||||
available flags and subcommands, refer to the [CLI reference](docker.md)
|
||||
|
||||
Depending on your Docker system configuration, you may be required to preface
|
||||
each `docker` command with `sudo`. To avoid having to use `sudo` with the
|
||||
@ -124,7 +27,7 @@ The following list of environment variables are supported by the `docker` comman
|
||||
line:
|
||||
|
||||
| Variable | Description |
|
||||
|:------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| :---------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `DOCKER_API_VERSION` | Override the negotiated API version to use for debugging (e.g. `1.19`) |
|
||||
| `DOCKER_CERT_PATH` | Location of your authentication keys. This variable is used both by the `docker` CLI and the [`dockerd` daemon](dockerd.md) |
|
||||
| `DOCKER_CONFIG` | The location of your client configuration files. |
|
||||
@ -136,7 +39,7 @@ line:
|
||||
| `DOCKER_HOST` | Daemon socket to connect to. |
|
||||
| `DOCKER_TLS` | Enable TLS for connections made by the `docker` CLI (equivalent of the `--tls` command-line option). Set to a non-empty value to enable TLS. Note that TLS is enabled automatically if any of the other TLS options are set. |
|
||||
| `DOCKER_TLS_VERIFY` | When set Docker uses TLS and verifies the remote. This variable is used both by the `docker` CLI and the [`dockerd` daemon](dockerd.md) |
|
||||
| `BUILDKIT_PROGRESS` | Set type of progress output (`auto`, `plain`, `tty`) when [building](build.md) with [BuildKit backend](https://docs.docker.com/build/buildkit/). Use plain to show container output (default `auto`). |
|
||||
| `BUILDKIT_PROGRESS` | Set type of progress output (`auto`, `plain`, `tty`) when [building](image_build.md) with [BuildKit backend](https://docs.docker.com/build/buildkit/). Use plain to show container output (default `auto`). |
|
||||
|
||||
Because Docker is developed using Go, you can also use any environment
|
||||
variables used by the Go runtime. In particular, you may find these useful:
|
||||
@ -215,15 +118,15 @@ if no `--format` flag is provided.
|
||||
| Property | Description |
|
||||
| :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `configFormat` | Custom default format for `docker config ls` output. See [`docker config ls`](config_ls.md#format) for a list of supported formatting directives. |
|
||||
| `imagesFormat` | Custom default format for `docker images` / `docker image ls` output. See [`docker images`](images.md#format) for a list of supported formatting directives. |
|
||||
| `imagesFormat` | Custom default format for `docker images` / `docker image ls` output. See [`docker images`](image_ls.md#format) for a list of supported formatting directives. |
|
||||
| `networksFormat` | Custom default format for `docker network ls` output. See [`docker network ls`](network_ls.md#format) for a list of supported formatting directives. |
|
||||
| `nodesFormat` | Custom default format for `docker node ls` output. See [`docker node ls`](node_ls.md#format) for a list of supported formatting directives. |
|
||||
| `pluginsFormat` | Custom default format for `docker plugin ls` output. See [`docker plugin ls`](plugin_ls.md#format) for a list of supported formatting directives. |
|
||||
| `psFormat` | Custom default format for `docker ps` / `docker container ps` output. See [`docker ps`](ps.md#format) for a list of supported formatting directives. |
|
||||
| `psFormat` | Custom default format for `docker ps` / `docker container ps` output. See [`docker ps`](container_ls.md#format) for a list of supported formatting directives. |
|
||||
| `secretFormat` | Custom default format for `docker secret ls` output. See [`docker secret ls`](secret_ls.md#format) for a list of supported formatting directives. |
|
||||
| `serviceInspectFormat` | Custom default format for `docker service inspect` output. See [`docker service inspect`](service_inspect.md#format) for a list of supported formatting directives. |
|
||||
| `servicesFormat` | Custom default format for `docker service ls` output. See [`docker service ls`](service_ls.md#format) for a list of supported formatting directives. |
|
||||
| `statsFormat` | Custom default format for `docker stats` output. See [`docker stats`](stats.md#format) for a list of supported formatting directives. |
|
||||
| `statsFormat` | Custom default format for `docker stats` output. See [`docker stats`](container_stats.md#format) for a list of supported formatting directives. |
|
||||
| `tasksFormat` | Custom default format for `docker stack ps` output. See [`docker stack ps`](stack_ps.md#format) for a list of supported formatting directives. |
|
||||
| `volumesFormat` | Custom default format for `docker volume ls` output. See [`docker volume ls`](volume_ls.md#format) for a list of supported formatting directives. |
|
||||
|
||||
|
||||
@ -858,6 +858,38 @@ PS C:\> docker run --device=class/86E0D1E0-8089-11D0-9CE4-08003E301F73 mcr.micro
|
||||
> The `--device` option is only supported on process-isolated Windows containers,
|
||||
> and produces an error if the container isolation is `hyperv`.
|
||||
|
||||
#### CDI devices
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> This is experimental feature and as such doesn't represent a stable API.
|
||||
|
||||
Container Device Interface (CDI) is a
|
||||
[standardized](https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md)
|
||||
mechanism for container runtimes to create containers which are able to
|
||||
interact with third party devices.
|
||||
|
||||
With CDI, device configurations are defined using a JSON file. In addition to
|
||||
enabling the container to interact with the device node, it also lets you
|
||||
specify additional configuration for the device, such as kernel modules, host
|
||||
libraries, and environment variables.
|
||||
|
||||
You can reference a CDI device with the `--device` flag using the
|
||||
fully-qualified name of the device, as shown in the following example:
|
||||
|
||||
```console
|
||||
$ docker run --device=vendor.com/class=device-name --rm -it ubuntu
|
||||
```
|
||||
|
||||
This starts an `ubuntu` container with access to the specified CDI device,
|
||||
`vendor.com/class=device-name`, assuming that:
|
||||
|
||||
- A valid CDI specification (JSON file) for the requested device is available
|
||||
on the system running the daemon, in one of the configured CDI specification
|
||||
directories.
|
||||
- The CDI feature has been enabled on the daemon side, see [Enable CDI
|
||||
devices](dockerd.md#enable-cdi-devices).
|
||||
|
||||
### <a name="attach"></a> Attach to STDIN/STDOUT/STDERR (-a, --attach)
|
||||
|
||||
The `--attach` (or `-a`) flag tells `docker run` to bind to the container's
|
||||
@ -900,7 +932,7 @@ $ cat somefile | docker run -i -a stdin mybuilder dobuild
|
||||
> Linux: it ignores any signal with the default action. So, the process
|
||||
> doesn't terminate on `SIGINT` or `SIGTERM` unless it's coded to do so.
|
||||
|
||||
See also [the `docker cp` command](cp.md).
|
||||
See also [the `docker cp` command](container_cp.md).
|
||||
|
||||
### <a name="interactive"></a> Keep STDIN open (-i, --interactive)
|
||||
|
||||
@ -1016,6 +1048,11 @@ the required device when it is added.
|
||||
The `--gpus` flag allows you to access NVIDIA GPU resources. First you need to
|
||||
install the [nvidia-container-runtime](https://nvidia.github.io/nvidia-container-runtime/).
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> You can also specify a GPU as a CDI device with the `--device` flag, see
|
||||
> [CDI devices](#cdi-devices).
|
||||
|
||||
Read [Specify a container's resources](https://docs.docker.com/config/containers/resource_constraints/)
|
||||
for more information.
|
||||
|
||||
@ -1060,8 +1097,8 @@ This runs the `redis` container with a restart policy of **always**.
|
||||
If the container exits, Docker restarts it.
|
||||
|
||||
When a restart policy is active on a container, it shows as either `Up` or
|
||||
`Restarting` in [`docker ps`](ps.md). It can also be useful to use [`docker
|
||||
events`](events.md) to see the restart policy in effect.
|
||||
`Restarting` in [`docker ps`](container_ls.md). It can also be useful to use
|
||||
[`docker events`](system_events.md) to see the restart policy in effect.
|
||||
|
||||
An increasing delay (double the previous delay, starting at 100 milliseconds)
|
||||
is added before each restart to prevent flooding the server. This means the
|
||||
@ -1092,8 +1129,8 @@ restart limit is only valid for the **on-failure** policy.
|
||||
#### Inspect container restarts
|
||||
|
||||
The number of (attempted) restarts for a container can be obtained using the
|
||||
[`docker inspect`](commandline/inspect.md) command. For example, to get the
|
||||
number of restarts for container "my-container";
|
||||
[`docker inspect`](inspect.md) command. For example, to get the number of
|
||||
restarts for container "my-container";
|
||||
|
||||
```console
|
||||
$ docker inspect -f "{{ .RestartCount }}" my-container
|
||||
|
||||
86
docs/reference/commandline/docker.md
Normal file
86
docs/reference/commandline/docker.md
Normal file
@ -0,0 +1,86 @@
|
||||
# docker
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
The base command for the Docker CLI.
|
||||
|
||||
### Subcommands
|
||||
|
||||
| Name | Description |
|
||||
|:------------------------------|:------------------------------------------------------------------------------|
|
||||
| [`attach`](attach.md) | Attach local standard input, output, and error streams to a running container |
|
||||
| [`build`](build.md) | Build an image from a Dockerfile |
|
||||
| [`builder`](builder.md) | Manage builds |
|
||||
| [`checkpoint`](checkpoint.md) | Manage checkpoints |
|
||||
| [`commit`](commit.md) | Create a new image from a container's changes |
|
||||
| [`config`](config.md) | Manage Swarm configs |
|
||||
| [`container`](container.md) | Manage containers |
|
||||
| [`context`](context.md) | Manage contexts |
|
||||
| [`cp`](cp.md) | Copy files/folders between a container and the local filesystem |
|
||||
| [`create`](create.md) | Create a new container |
|
||||
| [`diff`](diff.md) | Inspect changes to files or directories on a container's filesystem |
|
||||
| [`events`](events.md) | Get real time events from the server |
|
||||
| [`exec`](exec.md) | Execute a command in a running container |
|
||||
| [`export`](export.md) | Export a container's filesystem as a tar archive |
|
||||
| [`history`](history.md) | Show the history of an image |
|
||||
| [`image`](image.md) | Manage images |
|
||||
| [`images`](images.md) | List images |
|
||||
| [`import`](import.md) | Import the contents from a tarball to create a filesystem image |
|
||||
| [`info`](info.md) | Display system-wide information |
|
||||
| [`inspect`](inspect.md) | Return low-level information on Docker objects |
|
||||
| [`kill`](kill.md) | Kill one or more running containers |
|
||||
| [`load`](load.md) | Load an image from a tar archive or STDIN |
|
||||
| [`login`](login.md) | Log in to a registry |
|
||||
| [`logout`](logout.md) | Log out from a registry |
|
||||
| [`logs`](logs.md) | Fetch the logs of a container |
|
||||
| [`manifest`](manifest.md) | Manage Docker image manifests and manifest lists |
|
||||
| [`network`](network.md) | Manage networks |
|
||||
| [`node`](node.md) | Manage Swarm nodes |
|
||||
| [`pause`](pause.md) | Pause all processes within one or more containers |
|
||||
| [`plugin`](plugin.md) | Manage plugins |
|
||||
| [`port`](port.md) | List port mappings or a specific mapping for the container |
|
||||
| [`ps`](ps.md) | List containers |
|
||||
| [`pull`](pull.md) | Download an image from a registry |
|
||||
| [`push`](push.md) | Upload an image to a registry |
|
||||
| [`rename`](rename.md) | Rename a container |
|
||||
| [`restart`](restart.md) | Restart one or more containers |
|
||||
| [`rm`](rm.md) | Remove one or more containers |
|
||||
| [`rmi`](rmi.md) | Remove one or more images |
|
||||
| [`run`](run.md) | Create and run a new container from an image |
|
||||
| [`save`](save.md) | Save one or more images to a tar archive (streamed to STDOUT by default) |
|
||||
| [`search`](search.md) | Search Docker Hub for images |
|
||||
| [`secret`](secret.md) | Manage Swarm secrets |
|
||||
| [`service`](service.md) | Manage Swarm services |
|
||||
| [`stack`](stack.md) | Manage Swarm stacks |
|
||||
| [`start`](start.md) | Start one or more stopped containers |
|
||||
| [`stats`](stats.md) | Display a live stream of container(s) resource usage statistics |
|
||||
| [`stop`](stop.md) | Stop one or more running containers |
|
||||
| [`swarm`](swarm.md) | Manage Swarm |
|
||||
| [`system`](system.md) | Manage Docker |
|
||||
| [`tag`](tag.md) | Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE |
|
||||
| [`top`](top.md) | Display the running processes of a container |
|
||||
| [`trust`](trust.md) | Manage trust on Docker images |
|
||||
| [`unpause`](unpause.md) | Unpause all processes within one or more containers |
|
||||
| [`update`](update.md) | Update configuration of one or more containers |
|
||||
| [`version`](version.md) | Show the Docker version information |
|
||||
| [`volume`](volume.md) | Manage volumes |
|
||||
| [`wait`](wait.md) | Block until one or more containers stop, then print their exit codes |
|
||||
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------------|:---------|:-------------------------|:--------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `--config` | `string` | `/root/.docker` | Location of client config files |
|
||||
| `-c`, `--context` | `string` | | Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with `docker context use`) |
|
||||
| `-D`, `--debug` | | | Enable debug mode |
|
||||
| `-H`, `--host` | `list` | | Daemon socket to connect to |
|
||||
| `-l`, `--log-level` | `string` | `info` | Set the logging level (`debug`, `info`, `warn`, `error`, `fatal`) |
|
||||
| `--tls` | | | Use TLS; implied by --tlsverify |
|
||||
| `--tlscacert` | `string` | `/root/.docker/ca.pem` | Trust certs signed only by this CA |
|
||||
| `--tlscert` | `string` | `/root/.docker/cert.pem` | Path to TLS certificate file |
|
||||
| `--tlskey` | `string` | `/root/.docker/key.pem` | Path to TLS key file |
|
||||
| `--tlsverify` | | | Use TLS and verify the remote |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@ -29,6 +29,7 @@ Options:
|
||||
--authorization-plugin list Authorization plugins to load
|
||||
--bip string Specify network bridge IP
|
||||
-b, --bridge string Attach containers to a network bridge
|
||||
--cdi-spec-dir list CDI specification directories to use
|
||||
--cgroup-parent string Set parent cgroup for all containers
|
||||
--config-file string Daemon configuration file (default "/etc/docker/daemon.json")
|
||||
--containerd string containerd grpc address
|
||||
@ -848,12 +849,52 @@ flag for the dockerd command line interface, or the `host-gateway-ip` key in
|
||||
the daemon configuration file.
|
||||
|
||||
```console
|
||||
$ dockerd --host-gateway-ip 192.0.2.0
|
||||
$ cat > /etc/docker/daemon.json
|
||||
{ "host-gateway-ip": "192.0.2.0" }
|
||||
$ sudo systemctl restart docker
|
||||
$ docker run -it --add-host host.docker.internal:host-gateway \
|
||||
busybox ping host.docker.internal
|
||||
PING host.docker.internal (192.0.2.0): 56 data bytes
|
||||
```
|
||||
|
||||
### Enable CDI devices
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> This is experimental feature and as such doesn't represent a stable API.
|
||||
>
|
||||
> This feature isn't enabled by default. To this feature, set `features.cdi` to
|
||||
> `true` in the `daemon.json` configuration file.
|
||||
|
||||
Container Device Interface (CDI) is a
|
||||
[standardized](https://github.com/cncf-tags/container-device-interface/blob/main/SPEC.md)
|
||||
mechanism for container runtimes to create containers which are able to
|
||||
interact with third party devices.
|
||||
|
||||
The Docker daemon supports running containers with CDI devices if the requested
|
||||
device specifications are available on the filesystem of the daemon.
|
||||
|
||||
The default specification directors are:
|
||||
|
||||
- `/etc/cdi/` for static CDI Specs
|
||||
- `/var/run/cdi` for generated CDI Specs
|
||||
|
||||
Alternatively, you can set custom locations for CDI specifications using the
|
||||
`cdi-spec-dirs` option in the `daemon.json` configuration file, or the
|
||||
`--cdi-spec-dir` flag for the `dockerd` CLI.
|
||||
|
||||
```json
|
||||
{
|
||||
"features": {
|
||||
"cdi": true
|
||||
},
|
||||
"cdi-spec-dirs": ["/etc/cdi/", "/var/run/cdi"]
|
||||
}
|
||||
```
|
||||
|
||||
When CDI is enabled for a daemon, you can view the configured CDI specification
|
||||
directories using the `docker info` command.
|
||||
|
||||
### Miscellaneous options
|
||||
|
||||
IP masquerading uses address translation to allow containers without a public
|
||||
@ -1033,6 +1074,7 @@ The following is a full example of the allowed configuration options on Linux:
|
||||
"fixed-cidr": "",
|
||||
"fixed-cidr-v6": "",
|
||||
"group": "",
|
||||
"host-gateway-ip": "",
|
||||
"hosts": [],
|
||||
"proxies": {
|
||||
"http-proxy": "http://proxy.example.com:80",
|
||||
@ -1142,6 +1184,7 @@ The following is a full example of the allowed configuration options on Windows:
|
||||
"features": {},
|
||||
"fixed-cidr": "",
|
||||
"group": "",
|
||||
"host-gateway-ip": "",
|
||||
"hosts": [],
|
||||
"insecure-registries": [],
|
||||
"labels": [],
|
||||
|
||||
@ -321,7 +321,7 @@ $ docker build -t vieux/apache:2.0 .
|
||||
This examples builds in the same way as the previous example, but it then tags the resulting
|
||||
image. The repository name will be `vieux/apache` and the tag `2.0`.
|
||||
|
||||
[Read more about valid tags](tag.md).
|
||||
[Read more about valid tags](image_tag.md).
|
||||
|
||||
You can apply multiple tags to an image. For example, you can apply the `latest`
|
||||
tag to a newly built image and add another tag that references a specific
|
||||
|
||||
@ -81,7 +81,7 @@ Status: Downloaded newer image for debian:bookworm
|
||||
docker.io/library/debian:bookworm
|
||||
```
|
||||
|
||||
To see which images are present locally, use the [`docker images`](images.md)
|
||||
To see which images are present locally, use the [`docker images`](image_ls.md)
|
||||
command:
|
||||
|
||||
```console
|
||||
|
||||
@ -23,8 +23,8 @@ Upload an image to a registry
|
||||
Use `docker image push` to share your images to the [Docker Hub](https://hub.docker.com)
|
||||
registry or to a self-hosted one.
|
||||
|
||||
Refer to the [`docker image tag`](tag.md) reference for more information about valid
|
||||
image and tag names.
|
||||
Refer to the [`docker image tag`](image_tag.md) reference for more information
|
||||
about valid image and tag names.
|
||||
|
||||
Killing the `docker image push` process, for example by pressing `CTRL-c` while it is
|
||||
running in a terminal, terminates the push operation.
|
||||
@ -46,9 +46,9 @@ this via the `--max-concurrent-uploads` daemon option. See the
|
||||
|
||||
### Push a new image to a registry
|
||||
|
||||
First save the new image by finding the container ID (using [`docker container ls`](ps.md))
|
||||
and then committing it to a new image name. Note that only `a-z0-9-_.` are
|
||||
allowed when naming images:
|
||||
First save the new image by finding the container ID (using [`docker container
|
||||
ls`](container_ls.md)) and then committing it to a new image name. Note that
|
||||
only `a-z0-9-_.` are allowed when naming images:
|
||||
|
||||
```console
|
||||
$ docker container commit c16378f943fe rhel-httpd:latest
|
||||
|
||||
@ -26,7 +26,7 @@ removed.
|
||||
|
||||
This does not remove images from a registry. You cannot remove an image of a
|
||||
running container unless you use the `-f` option. To see all images on a host
|
||||
use the [`docker image ls`](images.md) command.
|
||||
use the [`docker image ls`](image_ls.md) command.
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@ -47,17 +47,17 @@ information about the `overlay2` storage driver is shown:
|
||||
```console
|
||||
$ docker info
|
||||
|
||||
Client: Docker Engine - Community
|
||||
Version: 24.0.0
|
||||
Client:
|
||||
Version: 25.0.0
|
||||
Context: default
|
||||
Debug Mode: false
|
||||
Plugins:
|
||||
buildx: Docker Buildx (Docker Inc.)
|
||||
Version: v0.10.4
|
||||
Path: /usr/libexec/docker/cli-plugins/docker-buildx
|
||||
Version: v0.12.1
|
||||
Path: /usr/local/libexec/docker/cli-plugins/docker-buildx
|
||||
compose: Docker Compose (Docker Inc.)
|
||||
Version: v2.17.2
|
||||
Path: /usr/libexec/docker/cli-plugins/docker-compose
|
||||
Version: v2.24.1
|
||||
Path: /usr/local/libexec/docker/cli-plugins/docker-compose
|
||||
|
||||
Server:
|
||||
Containers: 14
|
||||
@ -65,15 +65,11 @@ Server:
|
||||
Paused: 1
|
||||
Stopped: 10
|
||||
Images: 52
|
||||
Server Version: 23.0.3
|
||||
Storage Driver: overlay2
|
||||
Backing Filesystem: extfs
|
||||
Supports d_type: true
|
||||
Using metacopy: false
|
||||
Native Overlay Diff: true
|
||||
userxattr: false
|
||||
Server Version: 25.0.0
|
||||
Storage Driver: overlayfs
|
||||
driver-type: io.containerd.snapshotter.v1
|
||||
Logging Driver: json-file
|
||||
Cgroup Driver: systemd
|
||||
Cgroup Driver: cgroupfs
|
||||
Cgroup Version: 2
|
||||
Plugins:
|
||||
Volume: local
|
||||
@ -83,33 +79,31 @@ Server:
|
||||
/etc/cdi
|
||||
/var/run/cdi
|
||||
Swarm: inactive
|
||||
Runtimes: io.containerd.runc.v2 runc
|
||||
Runtimes: runc io.containerd.runc.v2
|
||||
Default Runtime: runc
|
||||
Init Binary: docker-init
|
||||
containerd version: 2806fc1057397dbaeefbea0e4e17bddfbd388f38
|
||||
runc version: v1.1.5-0-gf19387a
|
||||
containerd version: 71909c1814c544ac47ab91d2e8b84718e517bb99
|
||||
runc version: v1.1.11-0-g4bccb38
|
||||
init version: de40ad0
|
||||
Security Options:
|
||||
apparmor
|
||||
seccomp
|
||||
Profile: builtin
|
||||
cgroupns
|
||||
Kernel Version: 5.15.0-25-generic
|
||||
Operating System: Ubuntu 22.04 LTS
|
||||
Kernel Version: 6.5.11-linuxkit
|
||||
Operating System: Alpine Linux v3.19
|
||||
OSType: linux
|
||||
Architecture: x86_64
|
||||
CPUs: 1
|
||||
Total Memory: 991.7 MiB
|
||||
Name: ip-172-30-0-91.ec2.internal
|
||||
ID: 4cee4408-10d2-4e17-891c-a41736ac4536
|
||||
Architecture: aarch64
|
||||
CPUs: 10
|
||||
Total Memory: 7.663GiB
|
||||
Name: 4a7ed206a70d
|
||||
ID: c20f7230-59a2-4824-a2f4-fda71c982ee6
|
||||
Docker Root Dir: /var/lib/docker
|
||||
Debug Mode: false
|
||||
Username: gordontheturtle
|
||||
Experimental: false
|
||||
Insecure Registries:
|
||||
myinsecurehost:5000
|
||||
127.0.0.0/8
|
||||
Live Restore Enabled: false
|
||||
Product License: Community Engine
|
||||
```
|
||||
|
||||
### <a name="format"></a> Format the output (--format)
|
||||
|
||||
123
e2e/cli-plugins/plugins/presocket/main.go
Normal file
123
e2e/cli-plugins/plugins/presocket/main.go
Normal file
@ -0,0 +1,123 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli-plugins/plugin"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func main() {
|
||||
plugin.Run(RootCmd, manager.Metadata{
|
||||
SchemaVersion: "0.1.0",
|
||||
Vendor: "Docker Inc.",
|
||||
Version: "test",
|
||||
})
|
||||
}
|
||||
|
||||
func RootCmd(dockerCli command.Cli) *cobra.Command {
|
||||
cmd := cobra.Command{
|
||||
Use: "presocket",
|
||||
Short: "testing plugin that does not connect to the socket",
|
||||
// override PersistentPreRunE so that the plugin default
|
||||
// PersistentPreRunE doesn't run, simulating a plugin built
|
||||
// with a pre-socket-communication version of the CLI
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.AddCommand(&cobra.Command{
|
||||
Use: "test-no-socket",
|
||||
Short: "test command that runs until it receives a SIGINT",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
go func() {
|
||||
<-cmd.Context().Done()
|
||||
fmt.Fprintln(dockerCli.Out(), "context cancelled")
|
||||
os.Exit(2)
|
||||
}()
|
||||
signalCh := make(chan os.Signal, 10)
|
||||
signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
for range signalCh {
|
||||
fmt.Fprintln(dockerCli.Out(), "received SIGINT")
|
||||
}
|
||||
}()
|
||||
<-time.After(3 * time.Second)
|
||||
fmt.Fprintln(dockerCli.Err(), "exit after 3 seconds")
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
cmd.AddCommand(&cobra.Command{
|
||||
Use: "test-socket",
|
||||
Short: "test command that runs until it receives a SIGINT",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return plugin.PersistentPreRunE(cmd, args)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
go func() {
|
||||
<-cmd.Context().Done()
|
||||
fmt.Fprintln(dockerCli.Out(), "context cancelled")
|
||||
os.Exit(2)
|
||||
}()
|
||||
signalCh := make(chan os.Signal, 10)
|
||||
signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
for range signalCh {
|
||||
fmt.Fprintln(dockerCli.Out(), "received SIGINT")
|
||||
}
|
||||
}()
|
||||
<-time.After(3 * time.Second)
|
||||
fmt.Fprintln(dockerCli.Err(), "exit after 3 seconds")
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
cmd.AddCommand(&cobra.Command{
|
||||
Use: "test-socket-ignore-context",
|
||||
Short: "test command that runs until it receives a SIGINT",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return plugin.PersistentPreRunE(cmd, args)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
signalCh := make(chan os.Signal, 10)
|
||||
signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
for range signalCh {
|
||||
fmt.Fprintln(dockerCli.Out(), "received SIGINT")
|
||||
}
|
||||
}()
|
||||
<-time.After(3 * time.Second)
|
||||
fmt.Fprintln(dockerCli.Err(), "exit after 3 seconds")
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
cmd.AddCommand(&cobra.Command{
|
||||
Use: "tty",
|
||||
Short: "test command that attempts to read from the TTY",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
b := make([]byte, 1)
|
||||
_, _ = dockerCli.In().Read(b)
|
||||
done <- struct{}{}
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(2 * time.Second):
|
||||
fmt.Fprint(dockerCli.Err(), "timeout after 2 seconds")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
return &cmd
|
||||
}
|
||||
235
e2e/cli-plugins/socket_test.go
Normal file
235
e2e/cli-plugins/socket_test.go
Normal file
@ -0,0 +1,235 @@
|
||||
package cliplugins
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/creack/pty"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
// TestPluginSocketBackwardsCompatible executes a plugin binary
|
||||
// that does not connect to the CLI plugin socket, simulating
|
||||
// a plugin compiled against an older version of the CLI, and
|
||||
// ensures that backwards compatibility is maintained.
|
||||
func TestPluginSocketBackwardsCompatible(t *testing.T) {
|
||||
run, _, cleanup := prepare(t)
|
||||
defer cleanup()
|
||||
|
||||
t.Run("attached", func(t *testing.T) {
|
||||
t.Run("the plugin gets signalled if attached to a TTY", func(t *testing.T) {
|
||||
cmd := run("presocket", "test-no-socket")
|
||||
command := exec.Command(cmd.Command[0], cmd.Command[1:]...)
|
||||
|
||||
ptmx, err := pty.Start(command)
|
||||
assert.NilError(t, err, "failed to launch command with fake TTY")
|
||||
|
||||
// send a SIGINT to the process group after 1 second, since
|
||||
// we're simulating an "attached TTY" scenario and a TTY would
|
||||
// send a signal to the process group
|
||||
go func() {
|
||||
<-time.After(time.Second)
|
||||
err := syscall.Kill(-command.Process.Pid, syscall.SIGINT)
|
||||
assert.NilError(t, err, "failed to signal process group")
|
||||
}()
|
||||
bytes, err := io.ReadAll(ptmx)
|
||||
if err != nil && !strings.Contains(err.Error(), "input/output error") {
|
||||
t.Fatal("failed to get command output")
|
||||
}
|
||||
|
||||
// the plugin is attached to the TTY, so the parent process
|
||||
// ignores the received signal, and the plugin receives a SIGINT
|
||||
// as well
|
||||
assert.Equal(t, string(bytes), "received SIGINT\r\nexit after 3 seconds\r\n")
|
||||
})
|
||||
|
||||
// ensure that we don't break plugins that attempt to read from the TTY
|
||||
// (see: https://github.com/moby/moby/issues/47073)
|
||||
// (remove me if/when we decide to break compatibility here)
|
||||
t.Run("the plugin can read from the TTY", func(t *testing.T) {
|
||||
cmd := run("presocket", "tty")
|
||||
command := exec.Command(cmd.Command[0], cmd.Command[1:]...)
|
||||
|
||||
ptmx, err := pty.Start(command)
|
||||
assert.NilError(t, err, "failed to launch command with fake TTY")
|
||||
_, _ = ptmx.Write([]byte("hello!"))
|
||||
|
||||
done := make(chan error)
|
||||
go func() {
|
||||
<-time.After(time.Second)
|
||||
_, err := io.ReadAll(ptmx)
|
||||
done <- err
|
||||
}()
|
||||
|
||||
select {
|
||||
case cmdErr := <-done:
|
||||
if cmdErr != nil && !strings.Contains(cmdErr.Error(), "input/output error") {
|
||||
t.Fatal("failed to get command output")
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("timed out! plugin process probably stuck")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("detached", func(t *testing.T) {
|
||||
t.Run("the plugin does not get signalled", func(t *testing.T) {
|
||||
cmd := run("presocket", "test-no-socket")
|
||||
command := exec.Command(cmd.Command[0], cmd.Command[1:]...)
|
||||
t.Log(strings.Join(command.Args, " "))
|
||||
command.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-time.After(time.Second)
|
||||
// we're signalling the parent process directly and not
|
||||
// the process group, since we're testing the case where
|
||||
// the process is detached and not simulating a CTRL-C
|
||||
// from a TTY
|
||||
err := syscall.Kill(command.Process.Pid, syscall.SIGINT)
|
||||
assert.NilError(t, err, "failed to signal process group")
|
||||
}()
|
||||
bytes, err := command.CombinedOutput()
|
||||
t.Log("command output: " + string(bytes))
|
||||
assert.NilError(t, err, "failed to run command")
|
||||
|
||||
// the plugin process does not receive a SIGINT
|
||||
// so it exits after 3 seconds and prints this message
|
||||
assert.Equal(t, string(bytes), "exit after 3 seconds\n")
|
||||
})
|
||||
|
||||
t.Run("the main CLI exits after 3 signals", func(t *testing.T) {
|
||||
cmd := run("presocket", "test-no-socket")
|
||||
command := exec.Command(cmd.Command[0], cmd.Command[1:]...)
|
||||
t.Log(strings.Join(command.Args, " "))
|
||||
command.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-time.After(time.Second)
|
||||
// we're signalling the parent process directly and not
|
||||
// the process group, since we're testing the case where
|
||||
// the process is detached and not simulating a CTRL-C
|
||||
// from a TTY
|
||||
err := syscall.Kill(command.Process.Pid, syscall.SIGINT)
|
||||
assert.NilError(t, err, "failed to signal process group")
|
||||
// TODO: look into CLI signal handling, it's currently necessary
|
||||
// to add a short delay between each signal in order for the CLI
|
||||
// process to consistently pick them all up.
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
err = syscall.Kill(command.Process.Pid, syscall.SIGINT)
|
||||
assert.NilError(t, err, "failed to signal process group")
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
err = syscall.Kill(command.Process.Pid, syscall.SIGINT)
|
||||
assert.NilError(t, err, "failed to signal process group")
|
||||
}()
|
||||
bytes, err := command.CombinedOutput()
|
||||
assert.ErrorContains(t, err, "exit status 1")
|
||||
|
||||
// the plugin process does not receive a SIGINT and does
|
||||
// the CLI cannot cancel it over the socket, so it kills
|
||||
// the plugin process and forcefully exits
|
||||
assert.Equal(t, string(bytes), "got 3 SIGTERM/SIGINTs, forcefully exiting\n")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestPluginSocketCommunication(t *testing.T) {
|
||||
run, _, cleanup := prepare(t)
|
||||
defer cleanup()
|
||||
|
||||
t.Run("attached", func(t *testing.T) {
|
||||
t.Run("the socket is not closed + the plugin receives a signal due to pgid", func(t *testing.T) {
|
||||
cmd := run("presocket", "test-socket")
|
||||
command := exec.Command(cmd.Command[0], cmd.Command[1:]...)
|
||||
|
||||
ptmx, err := pty.Start(command)
|
||||
assert.NilError(t, err, "failed to launch command with fake TTY")
|
||||
|
||||
// send a SIGINT to the process group after 1 second, since
|
||||
// we're simulating an "attached TTY" scenario and a TTY would
|
||||
// send a signal to the process group
|
||||
go func() {
|
||||
<-time.After(time.Second)
|
||||
err := syscall.Kill(-command.Process.Pid, syscall.SIGINT)
|
||||
assert.NilError(t, err, "failed to signal process group")
|
||||
}()
|
||||
bytes, err := io.ReadAll(ptmx)
|
||||
if err != nil && !strings.Contains(err.Error(), "input/output error") {
|
||||
t.Fatal("failed to get command output")
|
||||
}
|
||||
|
||||
// the plugin is attached to the TTY, so the parent process
|
||||
// ignores the received signal, and the plugin receives a SIGINT
|
||||
// as well
|
||||
assert.Equal(t, string(bytes), "received SIGINT\r\nexit after 3 seconds\r\n")
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("detached", func(t *testing.T) {
|
||||
t.Run("the plugin does not get signalled", func(t *testing.T) {
|
||||
cmd := run("presocket", "test-socket")
|
||||
command := exec.Command(cmd.Command[0], cmd.Command[1:]...)
|
||||
outB := bytes.Buffer{}
|
||||
command.Stdout = &outB
|
||||
command.Stderr = &outB
|
||||
command.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
|
||||
// send a SIGINT to the process group after 1 second
|
||||
go func() {
|
||||
<-time.After(time.Second)
|
||||
err := syscall.Kill(command.Process.Pid, syscall.SIGINT)
|
||||
assert.NilError(t, err, "failed to signal CLI process")
|
||||
}()
|
||||
err := command.Run()
|
||||
t.Log(outB.String())
|
||||
assert.ErrorContains(t, err, "exit status 2")
|
||||
|
||||
// the plugin does not get signalled, but it does get it's
|
||||
// context cancelled by the CLI through the socket
|
||||
assert.Equal(t, outB.String(), "context cancelled\n")
|
||||
})
|
||||
|
||||
t.Run("the main CLI exits after 3 signals", func(t *testing.T) {
|
||||
cmd := run("presocket", "test-socket-ignore-context")
|
||||
command := exec.Command(cmd.Command[0], cmd.Command[1:]...)
|
||||
command.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-time.After(time.Second)
|
||||
// we're signalling the parent process directly and not
|
||||
// the process group, since we're testing the case where
|
||||
// the process is detached and not simulating a CTRL-C
|
||||
// from a TTY
|
||||
err := syscall.Kill(command.Process.Pid, syscall.SIGINT)
|
||||
assert.NilError(t, err, "failed to signal CLI process")
|
||||
// TODO: same as above TODO, CLI signal handling is not consistent
|
||||
// with multiple signals without intervals
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
err = syscall.Kill(command.Process.Pid, syscall.SIGINT)
|
||||
assert.NilError(t, err, "failed to signal CLI process")
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
err = syscall.Kill(command.Process.Pid, syscall.SIGINT)
|
||||
assert.NilError(t, err, "failed to signal CLI process§")
|
||||
}()
|
||||
bytes, err := command.CombinedOutput()
|
||||
assert.ErrorContains(t, err, "exit status 1")
|
||||
|
||||
// the plugin process does not receive a SIGINT and does
|
||||
// not exit after having it's context cancelled, so the CLI
|
||||
// kills the plugin process and forcefully exits
|
||||
assert.Equal(t, string(bytes), "got 3 SIGTERM/SIGINTs, forcefully exiting\n")
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -8,9 +8,6 @@ export GO111MODULE=auto
|
||||
|
||||
function clean {
|
||||
rm -rf "$buildir"
|
||||
if [ -f "$(pwd)/docs/reference/commandline/docker.md" ]; then
|
||||
mv "$(pwd)/docs/reference/commandline/docker.md" "$(pwd)/docs/reference/commandline/cli.md"
|
||||
fi
|
||||
}
|
||||
|
||||
buildir=$(mktemp -d -t docker-cli-docsgen.XXXXXXXXXX)
|
||||
@ -32,12 +29,6 @@ trap clean EXIT
|
||||
go build -mod=vendor -modfile=vendor.mod -tags docsgen -o /tmp/docsgen ./docs/generate/generate.go
|
||||
)
|
||||
|
||||
# yaml generation on docs repo needs the cli.md file: https://github.com/docker/cli/pull/3924#discussion_r1059986605
|
||||
# but markdown generation docker.md atm. While waiting for a fix in cli-docs-tool
|
||||
# we need to first move the cli.md file to docker.md, do the generation and
|
||||
# then move it back in trap handler.
|
||||
mv "$(pwd)/docs/reference/commandline/cli.md" "$(pwd)/docs/reference/commandline/docker.md"
|
||||
|
||||
(
|
||||
set -x
|
||||
/tmp/docsgen --formats md --source "$(pwd)/docs/reference/commandline" --target "$(pwd)/docs/reference/commandline"
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
# Run integration tests against the latest docker-ce dind
|
||||
set -eu -o pipefail
|
||||
|
||||
source ./scripts/build/.variables
|
||||
|
||||
container_ip() {
|
||||
local cid=$1
|
||||
local network=$2
|
||||
@ -69,7 +71,7 @@ runtests() {
|
||||
GOPATH="$GOPATH" \
|
||||
PATH="$PWD/build/:/usr/bin:/usr/local/bin:/usr/local/go/bin" \
|
||||
HOME="$HOME" \
|
||||
DOCKER_CLI_E2E_PLUGINS_EXTRA_DIRS="$PWD/build/plugins-linux-amd64" \
|
||||
DOCKER_CLI_E2E_PLUGINS_EXTRA_DIRS="$PWD/build/plugins-linux-${GOARCH}" \
|
||||
GO111MODULE=auto \
|
||||
"$(command -v gotestsum)" -- ${TESTDIRS:-./e2e/...} ${TESTFLAGS-}
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ require (
|
||||
github.com/creack/pty v1.1.21
|
||||
github.com/distribution/reference v0.5.0
|
||||
github.com/docker/distribution v2.8.3+incompatible
|
||||
github.com/docker/docker v25.0.0-rc.3+incompatible
|
||||
github.com/docker/docker v25.0.2+incompatible
|
||||
github.com/docker/docker-credential-helpers v0.8.1
|
||||
github.com/docker/go-connections v0.5.0
|
||||
github.com/docker/go-units v0.5.0
|
||||
|
||||
@ -54,8 +54,8 @@ github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v25.0.0-rc.3+incompatible h1:f2YaukI/rOEueLwmDGAVcES5E8Y+BT/e7pQWLu/WZSk=
|
||||
github.com/docker/docker v25.0.0-rc.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v25.0.2+incompatible h1:/OaKeauroa10K4Nqavw4zlhcDq/WBcPMc5DbjOGgozY=
|
||||
github.com/docker/docker v25.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo=
|
||||
github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||
|
||||
2
vendor/github.com/docker/docker/builder/remotecontext/urlutil/urlutil.go
generated
vendored
2
vendor/github.com/docker/docker/builder/remotecontext/urlutil/urlutil.go
generated
vendored
@ -12,7 +12,7 @@ import (
|
||||
|
||||
// urlPathWithFragmentSuffix matches fragments to use as Git reference and build
|
||||
// context from the Git repository. See IsGitURL for details.
|
||||
var urlPathWithFragmentSuffix = regexp.MustCompile(".git(?:#.+)?$")
|
||||
var urlPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`)
|
||||
|
||||
// IsURL returns true if the provided str is an HTTP(S) URL by checking if it
|
||||
// has a http:// or https:// scheme. No validation is performed to verify if the
|
||||
|
||||
18
vendor/github.com/docker/docker/pkg/system/xattrs.go
generated
vendored
Normal file
18
vendor/github.com/docker/docker/pkg/system/xattrs.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
package system // import "github.com/docker/docker/pkg/system"
|
||||
|
||||
type XattrError struct {
|
||||
Op string
|
||||
Attr string
|
||||
Path string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *XattrError) Error() string { return e.Op + " " + e.Attr + " " + e.Path + ": " + e.Err.Error() }
|
||||
|
||||
func (e *XattrError) Unwrap() error { return e.Err }
|
||||
|
||||
// Timeout reports whether this error represents a timeout.
|
||||
func (e *XattrError) Timeout() bool {
|
||||
t, ok := e.Err.(interface{ Timeout() bool })
|
||||
return ok && t.Timeout()
|
||||
}
|
||||
12
vendor/github.com/docker/docker/pkg/system/xattrs_linux.go
generated
vendored
12
vendor/github.com/docker/docker/pkg/system/xattrs_linux.go
generated
vendored
@ -1,8 +1,6 @@
|
||||
package system // import "github.com/docker/docker/pkg/system"
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
@ -10,8 +8,8 @@ import (
|
||||
// and associated with the given path in the file system.
|
||||
// It will returns a nil slice and nil error if the xattr is not set.
|
||||
func Lgetxattr(path string, attr string) ([]byte, error) {
|
||||
pathErr := func(err error) ([]byte, error) {
|
||||
return nil, &fs.PathError{Op: "lgetxattr", Path: path, Err: err}
|
||||
sysErr := func(err error) ([]byte, error) {
|
||||
return nil, &XattrError{Op: "lgetxattr", Attr: attr, Path: path, Err: err}
|
||||
}
|
||||
|
||||
// Start with a 128 length byte array
|
||||
@ -22,7 +20,7 @@ func Lgetxattr(path string, attr string) ([]byte, error) {
|
||||
// Buffer too small, use zero-sized buffer to get the actual size
|
||||
sz, errno = unix.Lgetxattr(path, attr, []byte{})
|
||||
if errno != nil {
|
||||
return pathErr(errno)
|
||||
return sysErr(errno)
|
||||
}
|
||||
dest = make([]byte, sz)
|
||||
sz, errno = unix.Lgetxattr(path, attr, dest)
|
||||
@ -32,7 +30,7 @@ func Lgetxattr(path string, attr string) ([]byte, error) {
|
||||
case errno == unix.ENODATA:
|
||||
return nil, nil
|
||||
case errno != nil:
|
||||
return pathErr(errno)
|
||||
return sysErr(errno)
|
||||
}
|
||||
|
||||
return dest[:sz], nil
|
||||
@ -43,7 +41,7 @@ func Lgetxattr(path string, attr string) ([]byte, error) {
|
||||
func Lsetxattr(path string, attr string, data []byte, flags int) error {
|
||||
err := unix.Lsetxattr(path, attr, data, flags)
|
||||
if err != nil {
|
||||
return &fs.PathError{Op: "lsetxattr", Path: path, Err: err}
|
||||
return &XattrError{Op: "lsetxattr", Attr: attr, Path: path, Err: err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@ -53,7 +53,7 @@ github.com/docker/distribution/registry/client/transport
|
||||
github.com/docker/distribution/registry/storage/cache
|
||||
github.com/docker/distribution/registry/storage/cache/memory
|
||||
github.com/docker/distribution/uuid
|
||||
# github.com/docker/docker v25.0.0-rc.3+incompatible
|
||||
# github.com/docker/docker v25.0.2+incompatible
|
||||
## explicit
|
||||
github.com/docker/docker/api
|
||||
github.com/docker/docker/api/types
|
||||
|
||||
Reference in New Issue
Block a user