daemon/linux: Set console size on creation

On Linux the daemon was not respecting the HostConfig.ConsoleSize
property and relied on cli initializing the tty size after the container
was created. This caused a delay between container creation and
the tty actually being resized.

This is also a small change to the api description, because
HostConfig.ConsoleSize is no longer Windows-only.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski 2022-05-12 14:54:44 +02:00
parent 1a0587bd76
commit 85a7f5a09a
9 changed files with 92 additions and 13 deletions

View File

@ -6,6 +6,7 @@ import (
"fmt"
"io"
"net/http"
"runtime"
"strconv"
"github.com/containerd/containerd/platforms"
@ -517,6 +518,11 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo
hostConfig.KernelMemory = 0
}
if hostConfig != nil && runtime.GOOS == "linux" && versions.LessThan(version, "1.42") {
// ConsoleSize is not respected by Linux daemon before API 1.42
hostConfig.ConsoleSize = [2]uint{0, 0}
}
var platform *specs.Platform
if versions.GreaterThanOrEqualTo(version, "1.41") {
if v := r.Form.Get("platform"); v != "" {

View File

@ -955,6 +955,15 @@ definitions:
type: "array"
items:
$ref: "#/definitions/Mount"
ConsoleSize:
type: "array"
description: |
Initial console size, as an `[height, width]` array.
minItems: 2
maxItems: 2
items:
type: "integer"
minimum: 0
# Applicable to UNIX platforms
CapAdd:
@ -1119,15 +1128,6 @@ definitions:
type: "string"
description: "Runtime to use with this container."
# Applicable to Windows
ConsoleSize:
type: "array"
description: |
Initial console size, as an `[height, width]` array. (Windows only)
minItems: 2
maxItems: 2
items:
type: "integer"
minimum: 0
Isolation:
type: "string"
description: |

View File

@ -417,6 +417,7 @@ type HostConfig struct {
AutoRemove bool // Automatically remove container when it exits
VolumeDriver string // Name of the volume driver used to mount volumes
VolumesFrom []string // List of volumes to take from other container
ConsoleSize [2]uint // Initial console size (height,width)
// Applicable to UNIX platforms
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container
@ -445,8 +446,7 @@ type HostConfig struct {
Runtime string `json:",omitempty"` // Runtime to use with this container
// Applicable to Windows
ConsoleSize [2]uint // Initial console size (height,width)
Isolation Isolation // Isolation technology of the container (e.g. default, hyperv)
Isolation Isolation // Isolation technology of the container (e.g. default, hyperv)
// Contains container's resources (cgroups, ulimits)
Resources

View File

@ -27,11 +27,18 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config
return response, err
}
clientVersion := cli.ClientVersion()
// When using API 1.24 and under, the client is responsible for removing the container
if hostConfig != nil && versions.LessThan(cli.ClientVersion(), "1.25") {
if hostConfig != nil && versions.LessThan(clientVersion, "1.25") {
hostConfig.AutoRemove = false
}
// When using API under 1.42, the Linux daemon doesn't respect the ConsoleSize
if hostConfig != nil && platform != nil && platform.OS == "linux" && versions.LessThan(clientVersion, "1.42") {
hostConfig.ConsoleSize = [2]uint{0, 0}
}
if err := cli.NewVersionError("1.41", "specify container image platform"); platform != nil && err != nil {
return response, err
}

View File

@ -1023,7 +1023,9 @@ func (daemon *Daemon) createSpec(c *container.Container) (retSpec *specs.Spec, e
if c.NoNewPrivileges {
opts = append(opts, coci.WithNoNewPrivileges)
}
if c.Config.Tty {
opts = append(opts, WithConsoleSize(c))
}
// Set the masked and readonly paths with regard to the host config options if they are set.
if c.HostConfig.MaskedPaths != nil {
opts = append(opts, coci.WithMaskedPaths(c.HostConfig.MaskedPaths))

23
daemon/oci_opts.go Normal file
View File

@ -0,0 +1,23 @@
package daemon
import (
"context"
"github.com/containerd/containerd/containers"
coci "github.com/containerd/containerd/oci"
"github.com/docker/docker/container"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
// WithConsoleSize sets the initial console size
func WithConsoleSize(c *container.Container) coci.SpecOpts {
return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
if c.HostConfig.ConsoleSize[0] > 0 || c.HostConfig.ConsoleSize[1] > 0 {
s.Process.ConsoleSize = &specs.Box{
Height: c.HostConfig.ConsoleSize[0],
Width: c.HostConfig.ConsoleSize[1],
}
}
return nil
}
}

View File

@ -94,6 +94,9 @@ keywords: "API, Docker, rcli, REST, documentation"
actually supported. API versions before v1.42 continue to ignore these parameters
and default to attaching to all streams. To preserve the pre-v1.42 behavior,
set all three query parameters (`?stdin=1,stdout=1,stderr=1`).
* `POST /containers/create` on Linux now respects the `HostConfig.ConsoleSize` property.
Container is immediately created with the desired terminal size and clients no longer
need to set the desired size on their own.
## v1.41 API changes

View File

@ -1,13 +1,16 @@
package container // import "github.com/docker/docker/integration/container"
import (
"bytes"
"context"
"io"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/docker/docker/api/types"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/integration/internal/container"
@ -183,3 +186,31 @@ func TestPrivilegedHostDevices(t *testing.T) {
assert.Check(t, is.Equal(strings.TrimSpace(res.Stdout()), devRootOnlyTest))
}
}
func TestConsoleSize(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType != "linux")
skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.42"), "skip test from new feature")
defer setupTest(t)()
client := testEnv.APIClient()
ctx := context.Background()
cID := container.Run(ctx, t, client,
container.WithTty(true),
container.WithImage("busybox"),
container.WithCmd("stty", "size"),
container.WithConsoleSize(57, 123),
)
poll.WaitOn(t, container.IsStopped(ctx, client, cID), poll.WithDelay(100*time.Millisecond))
out, err := client.ContainerLogs(ctx, cID, types.ContainerLogsOptions{ShowStdout: true})
assert.NilError(t, err)
defer out.Close()
var b bytes.Buffer
_, err = io.Copy(&b, out)
assert.NilError(t, err)
assert.Equal(t, strings.TrimSpace(b.String()), "123 57")
}

View File

@ -227,3 +227,10 @@ func WithIsolation(isolation containertypes.Isolation) func(*TestContainerConfig
c.HostConfig.Isolation = isolation
}
}
// WithConsoleSize sets the initial console size of the container
func WithConsoleSize(width, height uint) func(*TestContainerConfig) {
return func(c *TestContainerConfig) {
c.HostConfig.ConsoleSize = [2]uint{height, width}
}
}