mirror of
https://github.com/docker/cli.git
synced 2026-08-25 02:24:25 -05:00
Compare commits
@@ -63,7 +63,7 @@ jobs:
|
||||
name: Update Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.25.5"
|
||||
go-version: "1.25.6"
|
||||
-
|
||||
name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
|
||||
@@ -67,11 +67,14 @@ jobs:
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.25.5"
|
||||
go-version: "1.25.6"
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
go test -coverprofile=/tmp/coverage.txt $(go list ./... | grep -vE '/vendor/|/e2e/|/cmd/docker-trust')
|
||||
# run in go modules mode to prevent traversing to nested modules
|
||||
ln -s vendor.mod go.mod
|
||||
ln -s vendor.sum go.sum
|
||||
go test -coverprofile=/tmp/coverage.txt $(go list ./... | grep -vE '^github.com/docker/cli/e2e/')
|
||||
go tool cover -func=/tmp/coverage.txt
|
||||
working-directory: ${{ env.GOPATH }}/src/github.com/docker/cli
|
||||
shell: bash
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ run:
|
||||
# which causes it to fallback to go1.17 semantics.
|
||||
#
|
||||
# TODO(thaJeztah): update "usetesting" settings to enable go1.24 features once our minimum version is go1.24
|
||||
go: "1.25.5"
|
||||
go: "1.25.6"
|
||||
|
||||
timeout: 5m
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ ARG BASE_VARIANT=alpine
|
||||
ARG ALPINE_VERSION=3.22
|
||||
ARG BASE_DEBIAN_DISTRO=bookworm
|
||||
|
||||
ARG GO_VERSION=1.25.5
|
||||
ARG GO_VERSION=1.25.6
|
||||
|
||||
# XX_VERSION specifies the version of the xx utility to use.
|
||||
# It must be a valid tag in the docker.io/tonistiigi/xx image repository.
|
||||
|
||||
@@ -16,6 +16,5 @@ import (
|
||||
//
|
||||
// [ConfigFile.CLIPluginsExtraDirs]: https://pkg.go.dev/github.com/docker/cli@v26.1.4+incompatible/cli/config/configfile#ConfigFile.CLIPluginsExtraDirs
|
||||
var defaultSystemPluginDirs = []string{
|
||||
filepath.Join(os.Getenv("ProgramData"), "Docker", "cli-plugins"),
|
||||
filepath.Join(os.Getenv("ProgramFiles"), "Docker", "cli-plugins"),
|
||||
}
|
||||
|
||||
+2
-3
@@ -60,6 +60,7 @@ type Cli interface {
|
||||
type DockerCli struct {
|
||||
configFile *configfile.ConfigFile
|
||||
options *cliflags.ClientOptions
|
||||
clientOpts []client.Opt
|
||||
in *streams.In
|
||||
out *streams.Out
|
||||
err *streams.Out
|
||||
@@ -72,7 +73,6 @@ type DockerCli struct {
|
||||
dockerEndpoint docker.Endpoint
|
||||
contextStoreConfig *store.Config
|
||||
initTimeout time.Duration
|
||||
userAgent string
|
||||
res telemetryResource
|
||||
|
||||
// baseCtx is the base context used for internal operations. In the future
|
||||
@@ -533,8 +533,7 @@ func (cli *DockerCli) initialize() error {
|
||||
return
|
||||
}
|
||||
if cli.client == nil {
|
||||
ops := []client.Opt{client.WithUserAgent(cli.userAgent)}
|
||||
if cli.client, cli.initErr = newAPIClientFromEndpoint(cli.dockerEndpoint, cli.configFile, ops...); cli.initErr != nil {
|
||||
if cli.client, cli.initErr = newAPIClientFromEndpoint(cli.dockerEndpoint, cli.configFile, cli.clientOpts...); cli.initErr != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,16 @@ func WithInitializeClient(makeClient func(*DockerCli) (client.APIClient, error))
|
||||
}
|
||||
}
|
||||
|
||||
// WithAPIClientOptions configures additional [client.Opt] to use when
|
||||
// initializing the API client. These options have no effect if a custom
|
||||
// client is set (through [WithAPIClient] or [WithInitializeClient]).
|
||||
func WithAPIClientOptions(c ...client.Opt) CLIOption {
|
||||
return func(cli *DockerCli) error {
|
||||
cli.clientOpts = append(cli.clientOpts, c...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// envOverrideHTTPHeaders is the name of the environment-variable that can be
|
||||
// used to set custom HTTP headers to be sent by the client. This environment
|
||||
// variable is the equivalent to the HttpHeaders field in the configuration
|
||||
@@ -221,7 +231,7 @@ func WithUserAgent(userAgent string) CLIOption {
|
||||
if userAgent == "" {
|
||||
return errors.New("user agent cannot be blank")
|
||||
}
|
||||
cli.userAgent = userAgent
|
||||
cli.clientOpts = append(cli.clientOpts, client.WithUserAgent(userAgent))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,6 +358,7 @@ func TestSetGoDebug(t *testing.T) {
|
||||
assert.Equal(t, "val1,val2", os.Getenv("GODEBUG"))
|
||||
})
|
||||
t.Run("GODEBUG in context metadata can set env", func(t *testing.T) {
|
||||
t.Setenv("GODEBUG", "")
|
||||
meta := store.Metadata{
|
||||
Metadata: DockerContext{
|
||||
AdditionalFields: map[string]any{
|
||||
@@ -392,3 +393,26 @@ func TestNewDockerCliWithCustomUserAgent(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, received, "fake-agent/0.0.1")
|
||||
}
|
||||
|
||||
func TestNewDockerCliWithAPIClientOptions(t *testing.T) {
|
||||
var received string
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
received = r.UserAgent()
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer ts.Close()
|
||||
host := strings.Replace(ts.URL, "http://", "tcp://", 1)
|
||||
opts := &flags.ClientOptions{Hosts: []string{host}}
|
||||
|
||||
cli, err := NewDockerCli(
|
||||
WithAPIClientOptions(client.WithUserAgent("fake-agent/0.0.1")),
|
||||
)
|
||||
assert.NilError(t, err)
|
||||
cli.currentContext = DefaultContextName
|
||||
cli.options = opts
|
||||
cli.configFile = &configfile.ConfigFile{}
|
||||
|
||||
_, err = cli.Client().Ping(t.Context(), client.PingOptions{})
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, received, "fake-agent/0.0.1")
|
||||
}
|
||||
|
||||
@@ -70,6 +70,14 @@ func newAttachCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
|
||||
// RunAttach executes an `attach` command
|
||||
func RunAttach(ctx context.Context, dockerCLI command.Cli, containerID string, opts *AttachOptions) error {
|
||||
detachKeys := opts.DetachKeys
|
||||
if detachKeys == "" {
|
||||
detachKeys = dockerCLI.ConfigFile().DetachKeys
|
||||
}
|
||||
if err := validateDetachKeys(detachKeys); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apiClient := dockerCLI.Client()
|
||||
|
||||
// request channel to wait for client
|
||||
@@ -85,11 +93,6 @@ func RunAttach(ctx context.Context, dockerCLI command.Cli, containerID string, o
|
||||
return err
|
||||
}
|
||||
|
||||
detachKeys := dockerCLI.ConfigFile().DetachKeys
|
||||
if opts.DetachKeys != "" {
|
||||
detachKeys = opts.DetachKeys
|
||||
}
|
||||
|
||||
options := client.ContainerAttachOptions{
|
||||
Stream: true,
|
||||
Stdin: !opts.NoStdin && c.Config.OpenStdin,
|
||||
|
||||
@@ -27,6 +27,14 @@ func TestNewAttachCommandErrors(t *testing.T) {
|
||||
return client.ContainerInspectResult{}, errors.New("something went wrong")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid-detach-keys",
|
||||
args: []string{"--detach-keys", "shift-b", "5cb5bb5e4a3b"},
|
||||
expectedError: "invalid detach keys (shift-b):",
|
||||
containerInspectFunc: func(containerID string) (client.ContainerInspectResult, error) {
|
||||
return client.ContainerInspectResult{}, errors.New("something went wrong")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "client-stopped",
|
||||
args: []string{"5cb5bb5e4a3b"},
|
||||
|
||||
@@ -132,7 +132,6 @@ func runCreate(ctx context.Context, dockerCLI command.Cli, flags *pflag.FlagSet,
|
||||
return nil
|
||||
}
|
||||
|
||||
// FIXME(thaJeztah): this is the only code-path that uses APIClient.ImageCreate. Rewrite this to use the regular "pull" code (or vice-versa).
|
||||
func pullImage(ctx context.Context, dockerCLI command.Cli, img string, options *createOptions) error {
|
||||
encodedAuth, err := command.RetrieveAuthTokenFromImage(dockerCLI.ConfigFile(), img)
|
||||
if err != nil {
|
||||
@@ -177,8 +176,8 @@ func (cid *cidFile) Close() error {
|
||||
if cid.written {
|
||||
return nil
|
||||
}
|
||||
if err := os.Remove(cid.path); err != nil {
|
||||
return fmt.Errorf("failed to remove the CID file '%s': %w", cid.path, err)
|
||||
if err := os.Remove(cid.path); err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
return fmt.Errorf("failed to remove the CID file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -188,8 +187,8 @@ func (cid *cidFile) Write(id string) error {
|
||||
if cid.file == nil {
|
||||
return nil
|
||||
}
|
||||
if _, err := cid.file.Write([]byte(id)); err != nil {
|
||||
return fmt.Errorf("failed to write the container ID to the file: %w", err)
|
||||
if _, err := cid.file.WriteString(id); err != nil {
|
||||
return fmt.Errorf("failed to write the container ID (%s) to file: %w", id, err)
|
||||
}
|
||||
cid.written = true
|
||||
return nil
|
||||
@@ -212,7 +211,7 @@ func newCIDFile(cidPath string) (*cidFile, error) {
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *containerConfig, options *createOptions) (containerID string, err error) {
|
||||
func createContainer(ctx context.Context, dockerCLI command.Cli, containerCfg *containerConfig, options *createOptions) (containerID string, _ error) {
|
||||
config := containerCfg.Config
|
||||
hostConfig := containerCfg.HostConfig
|
||||
networkingConfig := containerCfg.NetworkingConfig
|
||||
@@ -221,8 +220,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
||||
|
||||
// TODO(thaJeztah): add a platform option-type / flag-type.
|
||||
if options.platform != "" {
|
||||
_, err = platforms.Parse(options.platform)
|
||||
if err != nil {
|
||||
if _, err := platforms.Parse(options.platform); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
@@ -248,10 +246,11 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
||||
|
||||
if options.useAPISocket {
|
||||
// We'll create two new mounts to handle this flag:
|
||||
//
|
||||
// 1. Mount the actual docker socket.
|
||||
// 2. A synthezised ~/.docker/config.json with resolved tokens.
|
||||
// 2. A synthesized ~/.docker/config.json with resolved tokens.
|
||||
|
||||
if dockerCli.ServerInfo().OSType == "windows" {
|
||||
if dockerCLI.ServerInfo().OSType == "windows" {
|
||||
return "", errors.New("flag --use-api-socket can't be used with a Windows Docker Engine")
|
||||
}
|
||||
|
||||
@@ -286,18 +285,18 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
||||
})
|
||||
*/
|
||||
|
||||
var envvarPresent bool
|
||||
for _, envvar := range containerCfg.Config.Env {
|
||||
if strings.HasPrefix(envvar, "DOCKER_CONFIG=") {
|
||||
envvarPresent = true
|
||||
var envVarPresent bool
|
||||
for _, envVar := range containerCfg.Config.Env {
|
||||
if strings.HasPrefix(envVar, "DOCKER_CONFIG=") {
|
||||
envVarPresent = true
|
||||
}
|
||||
}
|
||||
|
||||
// If the DOCKER_CONFIG env var is already present, we assume the client knows
|
||||
// what they're doing and don't inject the creds.
|
||||
if !envvarPresent {
|
||||
if !envVarPresent {
|
||||
// Resolve this here for later, ensuring we error our before we create the container.
|
||||
creds, err := readCredentials(dockerCli)
|
||||
creds, err := readCredentials(dockerCLI)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("resolving credentials failed: %w", err)
|
||||
}
|
||||
@@ -319,22 +318,15 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
||||
platform = &p
|
||||
}
|
||||
|
||||
pullAndTagImage := func() error {
|
||||
if err := pullImage(ctx, dockerCli, config.Image, options); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if options.pull == PullImageAlways {
|
||||
if err := pullAndTagImage(); err != nil {
|
||||
if err := pullImage(ctx, dockerCLI, config.Image, options); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = dockerCli.Out().GetTtySize()
|
||||
hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = dockerCLI.Out().GetTtySize()
|
||||
|
||||
response, err := dockerCli.Client().ContainerCreate(ctx, client.ContainerCreateOptions{
|
||||
response, err := dockerCLI.Client().ContainerCreate(ctx, client.ContainerCreateOptions{
|
||||
Name: options.name,
|
||||
// Image: config.Image, // TODO(thaJeztah): pass image-ref separate
|
||||
Platform: platform,
|
||||
@@ -347,15 +339,15 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
||||
if errdefs.IsNotFound(err) && namedRef != nil && options.pull == PullImageMissing {
|
||||
if !options.quiet {
|
||||
// we don't want to write to stdout anything apart from container.ID
|
||||
_, _ = fmt.Fprintf(dockerCli.Err(), "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef))
|
||||
_, _ = fmt.Fprintf(dockerCLI.Err(), "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef))
|
||||
}
|
||||
|
||||
if err := pullAndTagImage(); err != nil {
|
||||
if err := pullImage(ctx, dockerCLI, config.Image, options); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var retryErr error
|
||||
response, retryErr = dockerCli.Client().ContainerCreate(ctx, client.ContainerCreateOptions{
|
||||
response, retryErr = dockerCLI.Client().ContainerCreate(ctx, client.ContainerCreateOptions{
|
||||
Name: options.name,
|
||||
// Image: config.Image, // TODO(thaJeztah): pass image-ref separate
|
||||
Platform: platform,
|
||||
@@ -371,24 +363,20 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
|
||||
}
|
||||
}
|
||||
|
||||
containerID = response.ID
|
||||
for _, w := range response.Warnings {
|
||||
_, _ = fmt.Fprintln(dockerCli.Err(), "WARNING:", w)
|
||||
}
|
||||
err = containerIDFile.Write(containerID)
|
||||
|
||||
if options.useAPISocket && len(apiSocketCreds) > 0 {
|
||||
// Create a new config file with just the auth.
|
||||
newConfig := &configfile.ConfigFile{
|
||||
if err := copyDockerConfigIntoContainer(ctx, dockerCLI.Client(), response.ID, dockerConfigPathInContainer, &configfile.ConfigFile{
|
||||
AuthConfigs: apiSocketCreds,
|
||||
}
|
||||
|
||||
if err := copyDockerConfigIntoContainer(ctx, dockerCli.Client(), containerID, dockerConfigPathInContainer, newConfig); err != nil {
|
||||
return "", fmt.Errorf("injecting docker config.json into container failed: %w", err)
|
||||
}); err != nil {
|
||||
response.Warnings = append(response.Warnings, fmt.Sprintf("injecting docker config.json into container failed: %v", err))
|
||||
}
|
||||
}
|
||||
for _, w := range response.Warnings {
|
||||
_, _ = fmt.Fprintln(dockerCLI.Err(), "WARNING:", w)
|
||||
}
|
||||
|
||||
return containerID, err
|
||||
err = containerIDFile.Write(response.ID)
|
||||
return response.ID, err
|
||||
}
|
||||
|
||||
func validatePullOpt(val string) error {
|
||||
@@ -428,6 +416,7 @@ func copyDockerConfigIntoContainer(ctx context.Context, apiClient client.APIClie
|
||||
})
|
||||
|
||||
if _, err := io.Copy(tarWriter, &configBuf); err != nil {
|
||||
_ = tarWriter.Close()
|
||||
return fmt.Errorf("writing config to tar file for config copy: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -42,17 +43,31 @@ func TestNewCIDFileWhenFileAlreadyExists(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCIDFileCloseWithNoWrite(t *testing.T) {
|
||||
tempdir := fs.NewDir(t, "test-cid-file")
|
||||
defer tempdir.Remove()
|
||||
// Closing should remove the file if it was not written to.
|
||||
t.Run("closing should remove file", func(t *testing.T) {
|
||||
filename := filepath.Join(t.TempDir(), "cidfile-1")
|
||||
file, err := newCIDFile(filename)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(file.path, filename))
|
||||
|
||||
path := tempdir.Join("cidfile")
|
||||
file, err := newCIDFile(path)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(file.path, path))
|
||||
assert.NilError(t, file.Close())
|
||||
_, err = os.Stat(filename)
|
||||
assert.Check(t, os.IsNotExist(err))
|
||||
})
|
||||
|
||||
assert.NilError(t, file.Close())
|
||||
_, err = os.Stat(path)
|
||||
assert.Check(t, os.IsNotExist(err))
|
||||
// Closing (and removing) the file should not produce an error if the file no longer exists.
|
||||
t.Run("close should remove file", func(t *testing.T) {
|
||||
filename := filepath.Join(t.TempDir(), "cidfile-2")
|
||||
file, err := newCIDFile(filename)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(file.path, filename))
|
||||
|
||||
assert.NilError(t, os.Remove(filename))
|
||||
_, err = os.Stat(filename)
|
||||
assert.Check(t, os.IsNotExist(err))
|
||||
|
||||
assert.NilError(t, file.Close())
|
||||
})
|
||||
}
|
||||
|
||||
func TestCIDFileCloseWithWrite(t *testing.T) {
|
||||
|
||||
@@ -248,5 +248,8 @@ func parseExec(execOpts ExecOptions, configFile *configfile.ConfigFile) (*client
|
||||
} else {
|
||||
execOptions.DetachKeys = configFile.DetachKeys
|
||||
}
|
||||
if err := validateDetachKeys(execOpts.DetachKeys); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return execOptions, nil
|
||||
}
|
||||
|
||||
@@ -92,21 +92,21 @@ TWO=2
|
||||
},
|
||||
{
|
||||
options: withDefaultOpts(ExecOptions{Detach: true}),
|
||||
configFile: configfile.ConfigFile{DetachKeys: "de"},
|
||||
configFile: configfile.ConfigFile{DetachKeys: "ctrl-d,e"},
|
||||
expected: client.ExecCreateOptions{
|
||||
Cmd: []string{"command"},
|
||||
DetachKeys: "de",
|
||||
DetachKeys: "ctrl-d,e",
|
||||
},
|
||||
},
|
||||
{
|
||||
options: withDefaultOpts(ExecOptions{
|
||||
Detach: true,
|
||||
DetachKeys: "ab",
|
||||
DetachKeys: "ctrl-a,b",
|
||||
}),
|
||||
configFile: configfile.ConfigFile{DetachKeys: "de"},
|
||||
configFile: configfile.ConfigFile{DetachKeys: "ctrl-d,e"},
|
||||
expected: client.ExecCreateOptions{
|
||||
Cmd: []string{"command"},
|
||||
DetachKeys: "ab",
|
||||
DetachKeys: "ctrl-a,b",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -147,13 +147,23 @@ TWO=2
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseExecNoSuchFile(t *testing.T) {
|
||||
execOpts := withDefaultOpts(ExecOptions{})
|
||||
assert.Check(t, execOpts.EnvFile.Set("no-such-env-file"))
|
||||
execConfig, err := parseExec(execOpts, &configfile.ConfigFile{})
|
||||
assert.ErrorContains(t, err, "no-such-env-file")
|
||||
assert.Check(t, os.IsNotExist(err))
|
||||
assert.Check(t, execConfig == nil)
|
||||
func TestParseExecErrors(t *testing.T) {
|
||||
t.Run("missing env-file", func(t *testing.T) {
|
||||
execOpts := withDefaultOpts(ExecOptions{})
|
||||
assert.Check(t, execOpts.EnvFile.Set("no-such-env-file"))
|
||||
execConfig, err := parseExec(execOpts, &configfile.ConfigFile{})
|
||||
assert.ErrorContains(t, err, "no-such-env-file")
|
||||
assert.Check(t, os.IsNotExist(err))
|
||||
assert.Check(t, execConfig == nil)
|
||||
})
|
||||
t.Run("invalid detach keys", func(t *testing.T) {
|
||||
execOpts := withDefaultOpts(ExecOptions{
|
||||
DetachKeys: "shift-a",
|
||||
})
|
||||
execConfig, err := parseExec(execOpts, &configfile.ConfigFile{})
|
||||
assert.Check(t, is.ErrorContains(err, "invalid detach keys (shift-a):"))
|
||||
assert.Check(t, is.Nil(execConfig))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunExec(t *testing.T) {
|
||||
|
||||
@@ -30,6 +30,16 @@ func (r *readCloserWrapper) Close() error {
|
||||
return r.closer()
|
||||
}
|
||||
|
||||
func validateDetachKeys(keys string) error {
|
||||
if keys == "" {
|
||||
return nil
|
||||
}
|
||||
if _, err := term.ToBytes(keys); err != nil {
|
||||
return invalidParameter(fmt.Errorf("invalid detach keys (%s): %w", keys, err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A hijackedIOStreamer handles copying input to and output from streams to the
|
||||
// connection.
|
||||
type hijackedIOStreamer struct {
|
||||
@@ -82,13 +92,15 @@ func (h *hijackedIOStreamer) stream(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (h *hijackedIOStreamer) setupInput() (restore func(), err error) {
|
||||
func (h *hijackedIOStreamer) setupInput() (restore func(), _ error) {
|
||||
if h.inputStream == nil || !h.tty {
|
||||
// No need to setup input TTY.
|
||||
// The restore func is a nop.
|
||||
return func() {}, nil
|
||||
}
|
||||
|
||||
if err := validateDetachKeys(h.detachKeys); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := setRawTerminal(h.streams); err != nil {
|
||||
return nil, fmt.Errorf("unable to set IO streams as raw terminal: %s", err)
|
||||
}
|
||||
@@ -103,11 +115,11 @@ func (h *hijackedIOStreamer) setupInput() (restore func(), err error) {
|
||||
// Use default escape keys if an invalid sequence is given.
|
||||
escapeKeys := defaultEscapeKeys
|
||||
if h.detachKeys != "" {
|
||||
customEscapeKeys, err := term.ToBytes(h.detachKeys)
|
||||
var err error
|
||||
escapeKeys, err = term.ToBytes(h.detachKeys)
|
||||
if err != nil {
|
||||
logrus.Warnf("invalid detach escape keys, using default: %s", err)
|
||||
} else {
|
||||
escapeKeys = customEscapeKeys
|
||||
restore()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -795,7 +795,7 @@ func parseNetworkOpts(copts *containerOptions) (map[string]*network.EndpointSett
|
||||
// and only a single network is specified, omit the endpoint-configuration
|
||||
// on the client (the daemon will still create it when creating the container)
|
||||
if i == 0 && len(copts.netMode.Value()) == 1 {
|
||||
if ep == nil || reflect.DeepEqual(*ep, network.EndpointSettings{}) {
|
||||
if ep == nil || reflect.ValueOf(*ep).IsZero() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -947,11 +947,11 @@ func parseSecurityOpts(securityOpts []string) ([]string, error) {
|
||||
if err != nil {
|
||||
return securityOpts, fmt.Errorf("opening seccomp profile (%s) failed: %w", v, err)
|
||||
}
|
||||
b := bytes.NewBuffer(nil)
|
||||
if err := json.Compact(b, f); err != nil {
|
||||
var b bytes.Buffer
|
||||
if err := json.Compact(&b, f); err != nil {
|
||||
return securityOpts, fmt.Errorf("compacting json for seccomp profile (%s) failed: %w", v, err)
|
||||
}
|
||||
securityOpts[key] = fmt.Sprintf("seccomp=%s", b.Bytes())
|
||||
securityOpts[key] = "seccomp=" + b.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,6 +140,14 @@ func runContainer(ctx context.Context, dockerCli command.Cli, runOpts *runOption
|
||||
config.StdinOnce = false
|
||||
}
|
||||
|
||||
detachKeys := runOpts.detachKeys
|
||||
if detachKeys == "" {
|
||||
detachKeys = dockerCli.ConfigFile().DetachKeys
|
||||
}
|
||||
if err := validateDetachKeys(runOpts.detachKeys); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
containerID, err := createContainer(ctx, dockerCli, containerCfg, &runOpts.createOptions)
|
||||
if err != nil {
|
||||
return toStatusError(err)
|
||||
@@ -172,11 +180,6 @@ func runContainer(ctx context.Context, dockerCli command.Cli, runOpts *runOption
|
||||
}()
|
||||
}
|
||||
if attach {
|
||||
detachKeys := dockerCli.ConfigFile().DetachKeys
|
||||
if runOpts.detachKeys != "" {
|
||||
detachKeys = runOpts.detachKeys
|
||||
}
|
||||
|
||||
// ctx should not be cancellable here, as this would kill the stream to the container
|
||||
// and we want to keep the stream open until the process in the container exits or until
|
||||
// the user forcefully terminates the CLI.
|
||||
|
||||
@@ -34,6 +34,11 @@ func TestRunValidateFlags(t *testing.T) {
|
||||
args: []string{"--attach", "stdin", "--detach", "myimage"},
|
||||
expectedErr: "conflicting options: cannot specify both --attach and --detach",
|
||||
},
|
||||
{
|
||||
name: "with invalid --detach-keys",
|
||||
args: []string{"--detach-keys", "shift-a", "myimage"},
|
||||
expectedErr: "invalid detach keys (shift-a):",
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cmd := newRunCommand(test.NewFakeCli(&fakeClient{}))
|
||||
|
||||
@@ -70,6 +70,14 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er
|
||||
ctx, cancelFun := context.WithCancel(ctx)
|
||||
defer cancelFun()
|
||||
|
||||
detachKeys := opts.DetachKeys
|
||||
if detachKeys == "" {
|
||||
detachKeys = dockerCli.ConfigFile().DetachKeys
|
||||
}
|
||||
if err := validateDetachKeys(detachKeys); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case opts.Attach || opts.OpenStdin:
|
||||
// We're going to attach to a container.
|
||||
@@ -93,11 +101,6 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er
|
||||
defer signal.StopCatch(sigc)
|
||||
}
|
||||
|
||||
detachKeys := dockerCli.ConfigFile().DetachKeys
|
||||
if opts.DetachKeys != "" {
|
||||
detachKeys = opts.DetachKeys
|
||||
}
|
||||
|
||||
options := client.ContainerAttachOptions{
|
||||
Stream: true,
|
||||
Stdin: opts.OpenStdin && c.Container.Config.OpenStdin,
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestStartValidateFlags(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
args []string
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "with invalid --detach-keys",
|
||||
args: []string{"--detach-keys", "shift-a", "myimage"},
|
||||
expectedErr: "invalid detach keys (shift-a):",
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cmd := newStartCommand(test.NewFakeCli(&fakeClient{}))
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
|
||||
err := cmd.Execute()
|
||||
if tc.expectedErr != "" {
|
||||
assert.Check(t, is.ErrorContains(err, tc.expectedErr))
|
||||
} else {
|
||||
assert.Check(t, is.Nil(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,10 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
type dummy struct{}
|
||||
@@ -45,24 +47,18 @@ var dummyExpected = map[string]any{
|
||||
func TestMarshalMap(t *testing.T) {
|
||||
d := dummy{}
|
||||
m, err := marshalMap(&d)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(dummyExpected, m) {
|
||||
t.Fatalf("expected %+v, got %+v",
|
||||
dummyExpected, m)
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(m, dummyExpected))
|
||||
}
|
||||
|
||||
func TestMarshalMapBad(t *testing.T) {
|
||||
if _, err := marshalMap(nil); err == nil {
|
||||
t.Fatal("expected an error (argument is nil)")
|
||||
}
|
||||
if _, err := marshalMap(dummy{}); err == nil {
|
||||
t.Fatal("expected an error (argument is non-pointer)")
|
||||
}
|
||||
_, err := marshalMap(nil)
|
||||
assert.Check(t, is.Error(err, "expected a pointer to a struct, got invalid"), "expected an error (argument is nil)")
|
||||
|
||||
_, err = marshalMap(dummy{})
|
||||
assert.Check(t, is.Error(err, "expected a pointer to a struct, got struct"), "expected an error (argument is non-pointer)")
|
||||
|
||||
x := 42
|
||||
if _, err := marshalMap(&x); err == nil {
|
||||
t.Fatal("expected an error (argument is a pointer to non-struct)")
|
||||
}
|
||||
_, err = marshalMap(&x)
|
||||
assert.Check(t, is.Error(err, "expected a pointer to a struct, got a pointer to int"), "expected an error (argument is a pointer to non-struct)")
|
||||
}
|
||||
|
||||
@@ -480,7 +480,7 @@ func Compress(buildCtx io.ReadCloser) (io.ReadCloser, error) {
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
|
||||
go func() {
|
||||
compressWriter, err := compression.CompressStream(pipeWriter, archive.Gzip)
|
||||
compressWriter, err := compression.CompressStream(pipeWriter, compression.Gzip)
|
||||
if err != nil {
|
||||
_ = pipeWriter.CloseWithError(err)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
@@ -170,15 +170,23 @@ func (c *nodeContext) ManagerStatus() string {
|
||||
}
|
||||
|
||||
func (c *nodeContext) TLSStatus() string {
|
||||
if c.info.Cluster == nil || reflect.DeepEqual(c.info.Cluster.TLSInfo, swarm.TLSInfo{}) || reflect.DeepEqual(c.n.Description.TLSInfo, swarm.TLSInfo{}) {
|
||||
if c.info.Cluster == nil || isEmptyTLSInfo(c.info.Cluster.TLSInfo) || isEmptyTLSInfo(c.n.Description.TLSInfo) {
|
||||
return "Unknown"
|
||||
}
|
||||
if reflect.DeepEqual(c.n.Description.TLSInfo, c.info.Cluster.TLSInfo) {
|
||||
if equalTLSInfo(c.n.Description.TLSInfo, c.info.Cluster.TLSInfo) {
|
||||
return "Ready"
|
||||
}
|
||||
return "Needs Rotation"
|
||||
}
|
||||
|
||||
func isEmptyTLSInfo(t swarm.TLSInfo) bool {
|
||||
return t.TrustRoot == "" && len(t.CertIssuerSubject) == 0 && len(t.CertIssuerPublicKey) == 0
|
||||
}
|
||||
|
||||
func equalTLSInfo(t, o swarm.TLSInfo) bool {
|
||||
return t.TrustRoot == o.TrustRoot && bytes.Equal(t.CertIssuerSubject, o.CertIssuerSubject) && bytes.Equal(t.CertIssuerPublicKey, o.CertIssuerPublicKey)
|
||||
}
|
||||
|
||||
func (c *nodeContext) EngineVersion() string {
|
||||
return c.n.Description.Engine.EngineVersion
|
||||
}
|
||||
@@ -320,8 +328,7 @@ func (ctx *nodeInspectContext) EngineVersion() string {
|
||||
}
|
||||
|
||||
func (ctx *nodeInspectContext) HasTLSInfo() bool {
|
||||
tlsInfo := ctx.Node.Description.TLSInfo
|
||||
return !reflect.DeepEqual(tlsInfo, swarm.TLSInfo{})
|
||||
return !isEmptyTLSInfo(ctx.Node.Description.TLSInfo)
|
||||
}
|
||||
|
||||
func (ctx *nodeInspectContext) TLSInfoTrustRoot() string {
|
||||
|
||||
@@ -200,7 +200,7 @@ func RetrieveAuthTokenFromImage(cfg *configfile.ConfigFile, image string) (strin
|
||||
return "", err
|
||||
}
|
||||
|
||||
encodedAuth, err := authconfig.Encode(registrytypes.AuthConfig{
|
||||
return authconfig.Encode(registrytypes.AuthConfig{
|
||||
Username: authConfig.Username,
|
||||
Password: authConfig.Password,
|
||||
ServerAddress: authConfig.ServerAddress,
|
||||
@@ -210,10 +210,6 @@ func RetrieveAuthTokenFromImage(cfg *configfile.ConfigFile, image string) (strin
|
||||
IdentityToken: authConfig.IdentityToken,
|
||||
RegistryToken: authConfig.RegistryToken,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return encodedAuth, nil
|
||||
}
|
||||
|
||||
// getAuthConfigKey special-cases using the full index address of the official
|
||||
|
||||
@@ -197,7 +197,7 @@ func loginUser(ctx context.Context, dockerCLI command.Cli, opts loginOptions, de
|
||||
// will hit this if you attempt docker login from mintty where stdin
|
||||
// is a pipe, not a character based console.
|
||||
if (opts.user == "" || opts.password == "") && !dockerCLI.In().IsTerminal() {
|
||||
return "", errors.New("error: cannot perform an interactive login from a non TTY device")
|
||||
return "", errors.New("error: cannot perform an interactive login from a non-TTY device")
|
||||
}
|
||||
|
||||
// If we're logging into the index server and the user didn't provide a username or password, use the device flow
|
||||
|
||||
@@ -127,7 +127,7 @@ func TestRunLogin(t *testing.T) {
|
||||
input: loginOptions{
|
||||
serverAddress: "reg1",
|
||||
},
|
||||
expectedErr: "error: cannot perform an interactive login from a non TTY device",
|
||||
expectedErr: "error: cannot perform an interactive login from a non-TTY device",
|
||||
},
|
||||
{
|
||||
doc: "store valid username and password",
|
||||
@@ -334,19 +334,19 @@ func TestLoginNonInteractive(t *testing.T) {
|
||||
doc: "error - w/o user w/o pass ",
|
||||
username: false,
|
||||
password: false,
|
||||
expectedErr: "error: cannot perform an interactive login from a non TTY device",
|
||||
expectedErr: "error: cannot perform an interactive login from a non-TTY device",
|
||||
},
|
||||
{
|
||||
doc: "error - w/ user w/o pass",
|
||||
username: true,
|
||||
password: false,
|
||||
expectedErr: "error: cannot perform an interactive login from a non TTY device",
|
||||
expectedErr: "error: cannot perform an interactive login from a non-TTY device",
|
||||
},
|
||||
{
|
||||
doc: "error - w/o user w/ pass",
|
||||
username: false,
|
||||
password: true,
|
||||
expectedErr: "error: cannot perform an interactive login from a non TTY device",
|
||||
expectedErr: "error: cannot perform an interactive login from a non-TTY device",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -403,13 +403,13 @@ func TestLoginNonInteractive(t *testing.T) {
|
||||
doc: "error - w/ user w/o pass",
|
||||
username: true,
|
||||
password: false,
|
||||
expectedErr: "error: cannot perform an interactive login from a non TTY device",
|
||||
expectedErr: "error: cannot perform an interactive login from a non-TTY device",
|
||||
},
|
||||
{
|
||||
doc: "error - w/o user w/ pass",
|
||||
username: false,
|
||||
password: true,
|
||||
expectedErr: "error: cannot perform an interactive login from a non TTY device",
|
||||
expectedErr: "error: cannot perform an interactive login from a non-TTY device",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -905,9 +905,9 @@ func addServiceFlags(flags *pflag.FlagSet, options *serviceOptions, defaultFlagV
|
||||
flags.Int64Var(&options.resources.limitPids, flagLimitPids, 0, "Limit maximum number of processes (default 0 = unlimited)")
|
||||
flags.SetAnnotation(flagLimitPids, "version", []string{"1.41"})
|
||||
flags.Var(&options.resources.swapBytes, flagSwapBytes, "Swap Bytes (-1 for unlimited)")
|
||||
flags.SetAnnotation(flagLimitPids, "version", []string{"1.52"})
|
||||
flags.SetAnnotation(flagSwapBytes, "version", []string{"1.52"})
|
||||
flags.Int64Var(&options.resources.memSwappiness, flagMemSwappiness, -1, "Tune memory swappiness (0-100), -1 to reset to default")
|
||||
flags.SetAnnotation(flagLimitPids, "version", []string{"1.52"})
|
||||
flags.SetAnnotation(flagMemSwappiness, "version", []string{"1.52"})
|
||||
|
||||
flags.Var(&options.stopGrace, flagStopGracePeriod, flagDesc(flagStopGracePeriod, "Time to wait before force killing a container (ns|us|ms|s|m|h)"))
|
||||
flags.Var(&options.replicas, flagReplicas, "Number of tasks")
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -368,23 +368,23 @@ func TestUpdateHealthcheckTable(t *testing.T) {
|
||||
err: "--no-healthcheck conflicts with --health-* options",
|
||||
},
|
||||
}
|
||||
for i, c := range testCases {
|
||||
flags := newUpdateCommand(nil).Flags()
|
||||
for _, flag := range c.flags {
|
||||
flags.Set(flag[0], flag[1])
|
||||
}
|
||||
cspec := &swarm.ContainerSpec{
|
||||
Healthcheck: c.initial,
|
||||
}
|
||||
err := updateHealthcheck(flags, cspec)
|
||||
if c.err != "" {
|
||||
assert.Error(t, err, c.err)
|
||||
} else {
|
||||
assert.NilError(t, err)
|
||||
if !reflect.DeepEqual(cspec.Healthcheck, c.expected) {
|
||||
t.Errorf("incorrect result for test %d, expected health config:\n\t%#v\ngot:\n\t%#v", i, c.expected, cspec.Healthcheck)
|
||||
for i, tc := range testCases {
|
||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||
flags := newUpdateCommand(nil).Flags()
|
||||
for _, flag := range tc.flags {
|
||||
assert.Check(t, flags.Set(flag[0], flag[1]))
|
||||
}
|
||||
}
|
||||
cspec := &swarm.ContainerSpec{
|
||||
Healthcheck: tc.initial,
|
||||
}
|
||||
err := updateHealthcheck(flags, cspec)
|
||||
if tc.err != "" {
|
||||
assert.Error(t, err, tc.err)
|
||||
} else {
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(cspec.Healthcheck, tc.expected))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -397,6 +397,13 @@ func prettyPrintServerInfo(streams command.Streams, info *dockerInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
if info.NRI != nil {
|
||||
fprintln(output, " NRI:")
|
||||
for _, v := range info.NRI.Info {
|
||||
fprintf(output, " %s: %s\n", v[0], v[1])
|
||||
}
|
||||
}
|
||||
|
||||
fprintln(output)
|
||||
for _, w := range info.Warnings {
|
||||
fprintln(streams.Err(), w)
|
||||
|
||||
@@ -126,6 +126,12 @@ var sampleInfoNoSwarm = system.Info{
|
||||
{"ReloadedAt", "2025-07-16T16:59:14Z"},
|
||||
},
|
||||
},
|
||||
NRI: &system.NRIInfo{
|
||||
Info: [][2]string{
|
||||
{"plugin-path", "/usr/libexec/docker/nri-plugins"},
|
||||
{"plugin-config-path", "/etc/docker/nri/conf.d"},
|
||||
},
|
||||
},
|
||||
CDISpecDirs: []string{"/etc/cdi", "/var/run/cdi"},
|
||||
}
|
||||
|
||||
|
||||
@@ -54,4 +54,7 @@ Server:
|
||||
Base: 10.123.0.0/16, Size: 24
|
||||
Firewall Backend: nftables+firewalld
|
||||
ReloadedAt: 2025-07-16T16:59:14Z
|
||||
NRI:
|
||||
plugin-path: /usr/libexec/docker/nri-plugins
|
||||
plugin-config-path: /etc/docker/nri/conf.d
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"overlay2","DriverStatus":[["Backing Filesystem","extfs"],["Supports d_type","true"],["Using metacopy","false"],["Native Overlay Diff","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa"},"SecurityOptions":["foo="],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"FirewallBackend":{"Driver":"nftables+firewalld","Info":[["ReloadedAt","2025-07-16T16:59:14Z"]]},"CDISpecDirs":["/etc/cdi","/var/run/cdi"],"Warnings":null,"ServerErrors":["a server error occurred"],"ClientInfo":{"Debug":false,"Context":"","Plugins":[],"Warnings":null}}
|
||||
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"overlay2","DriverStatus":[["Backing Filesystem","extfs"],["Supports d_type","true"],["Using metacopy","false"],["Native Overlay Diff","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa"},"SecurityOptions":["foo="],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"FirewallBackend":{"Driver":"nftables+firewalld","Info":[["ReloadedAt","2025-07-16T16:59:14Z"]]},"CDISpecDirs":["/etc/cdi","/var/run/cdi"],"NRI":{"Info":[["plugin-path","/usr/libexec/docker/nri-plugins"],["plugin-config-path","/etc/docker/nri/conf.d"]]},"Warnings":null,"ServerErrors":["a server error occurred"],"ClientInfo":{"Debug":false,"Context":"","Plugins":[],"Warnings":null}}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"overlay2","DriverStatus":[["Backing Filesystem","extfs"],["Supports d_type","true"],["Using metacopy","false"],["Native Overlay Diff","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"FirewallBackend":{"Driver":"nftables+firewalld","Info":[["ReloadedAt","2025-07-16T16:59:14Z"]]},"CDISpecDirs":["/etc/cdi","/var/run/cdi"],"Warnings":["WARNING: No memory limit support","WARNING: No swap limit support","WARNING: No oom kill disable support","WARNING: No cpu cfs quota support","WARNING: No cpu cfs period support","WARNING: No cpu shares support","WARNING: No cpuset support","WARNING: IPv4 forwarding is disabled"],"ClientInfo":{"Debug":true,"Platform":{"Name":"Docker Engine - Community"},"Version":"24.0.0","Context":"default","Plugins":[],"Warnings":null}}
|
||||
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"overlay2","DriverStatus":[["Backing Filesystem","extfs"],["Supports d_type","true"],["Using metacopy","false"],["Native Overlay Diff","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"FirewallBackend":{"Driver":"nftables+firewalld","Info":[["ReloadedAt","2025-07-16T16:59:14Z"]]},"CDISpecDirs":["/etc/cdi","/var/run/cdi"],"NRI":{"Info":[["plugin-path","/usr/libexec/docker/nri-plugins"],["plugin-config-path","/etc/docker/nri/conf.d"]]},"Warnings":["WARNING: No memory limit support","WARNING: No swap limit support","WARNING: No oom kill disable support","WARNING: No cpu cfs quota support","WARNING: No cpu cfs period support","WARNING: No cpu shares support","WARNING: No cpuset support","WARNING: IPv4 forwarding is disabled"],"ClientInfo":{"Debug":true,"Platform":{"Name":"Docker Engine - Community"},"Version":"24.0.0","Context":"default","Plugins":[],"Warnings":null}}
|
||||
|
||||
@@ -59,4 +59,7 @@ Server:
|
||||
Base: 10.123.0.0/16, Size: 24
|
||||
Firewall Backend: nftables+firewalld
|
||||
ReloadedAt: 2025-07-16T16:59:14Z
|
||||
NRI:
|
||||
plugin-path: /usr/libexec/docker/nri-plugins
|
||||
plugin-config-path: /etc/docker/nri/conf.d
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"overlay2","DriverStatus":[["Backing Filesystem","extfs"],["Supports d_type","true"],["Using metacopy","false"],["Native Overlay Diff","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"FirewallBackend":{"Driver":"nftables+firewalld","Info":[["ReloadedAt","2025-07-16T16:59:14Z"]]},"CDISpecDirs":["/etc/cdi","/var/run/cdi"],"Warnings":null,"ClientInfo":{"Debug":true,"Platform":{"Name":"Docker Engine - Community"},"Version":"24.0.0","Context":"default","Plugins":[],"Warnings":null}}
|
||||
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"overlay2","DriverStatus":[["Backing Filesystem","extfs"],["Supports d_type","true"],["Using metacopy","false"],["Native Overlay Diff","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"FirewallBackend":{"Driver":"nftables+firewalld","Info":[["ReloadedAt","2025-07-16T16:59:14Z"]]},"CDISpecDirs":["/etc/cdi","/var/run/cdi"],"NRI":{"Info":[["plugin-path","/usr/libexec/docker/nri-plugins"],["plugin-config-path","/etc/docker/nri/conf.d"]]},"Warnings":null,"ClientInfo":{"Debug":true,"Platform":{"Name":"Docker Engine - Community"},"Version":"24.0.0","Context":"default","Plugins":[],"Warnings":null}}
|
||||
|
||||
@@ -64,4 +64,7 @@ Server:
|
||||
Base: 10.123.0.0/16, Size: 24
|
||||
Firewall Backend: nftables+firewalld
|
||||
ReloadedAt: 2025-07-16T16:59:14Z
|
||||
NRI:
|
||||
plugin-path: /usr/libexec/docker/nri-plugins
|
||||
plugin-config-path: /etc/docker/nri/conf.d
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"overlay2","DriverStatus":[["Backing Filesystem","extfs"],["Supports d_type","true"],["Using metacopy","false"],["Native Overlay Diff","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"FirewallBackend":{"Driver":"nftables+firewalld","Info":[["ReloadedAt","2025-07-16T16:59:14Z"]]},"CDISpecDirs":["/etc/cdi","/var/run/cdi"],"Warnings":null,"ClientInfo":{"Debug":false,"Context":"default","Plugins":[{"SchemaVersion":"0.1.0","Vendor":"ACME Corp","Version":"0.1.0","ShortDescription":"unit test is good","Name":"goodplugin","Path":"/path/to/docker-goodplugin"},{"SchemaVersion":"0.1.0","Vendor":"ACME Corp","ShortDescription":"this plugin has no version","Name":"unversionedplugin","Path":"/path/to/docker-unversionedplugin"},{"Name":"badplugin","Path":"/path/to/docker-badplugin","Err":"something wrong"}],"Warnings":null}}
|
||||
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"overlay2","DriverStatus":[["Backing Filesystem","extfs"],["Supports d_type","true"],["Using metacopy","false"],["Native Overlay Diff","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"FirewallBackend":{"Driver":"nftables+firewalld","Info":[["ReloadedAt","2025-07-16T16:59:14Z"]]},"CDISpecDirs":["/etc/cdi","/var/run/cdi"],"NRI":{"Info":[["plugin-path","/usr/libexec/docker/nri-plugins"],["plugin-config-path","/etc/docker/nri/conf.d"]]},"Warnings":null,"ClientInfo":{"Debug":false,"Context":"default","Plugins":[{"SchemaVersion":"0.1.0","Vendor":"ACME Corp","Version":"0.1.0","ShortDescription":"unit test is good","Name":"goodplugin","Path":"/path/to/docker-goodplugin"},{"SchemaVersion":"0.1.0","Vendor":"ACME Corp","ShortDescription":"this plugin has no version","Name":"unversionedplugin","Path":"/path/to/docker-unversionedplugin"},{"Name":"badplugin","Path":"/path/to/docker-badplugin","Err":"something wrong"}],"Warnings":null}}
|
||||
|
||||
@@ -59,4 +59,7 @@ Server:
|
||||
Base: 10.123.0.0/16, Size: 24
|
||||
Firewall Backend: nftables+firewalld
|
||||
ReloadedAt: 2025-07-16T16:59:14Z
|
||||
NRI:
|
||||
plugin-path: /usr/libexec/docker/nri-plugins
|
||||
plugin-config-path: /etc/docker/nri/conf.d
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"overlay2","DriverStatus":[["Backing Filesystem","extfs"],["Supports d_type","true"],["Using metacopy","false"],["Native Overlay Diff","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"FirewallBackend":{"Driver":"nftables+firewalld","Info":[["ReloadedAt","2025-07-16T16:59:14Z"]]},"CDISpecDirs":["/etc/cdi","/var/run/cdi"],"DiscoveredDevices":[{"Source":"cdi","ID":"com.example.device1"},{"Source":"cdi","ID":"nvidia.com/gpu=gpu0"}],"Warnings":null}
|
||||
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"overlay2","DriverStatus":[["Backing Filesystem","extfs"],["Supports d_type","true"],["Using metacopy","false"],["Native Overlay Diff","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"FirewallBackend":{"Driver":"nftables+firewalld","Info":[["ReloadedAt","2025-07-16T16:59:14Z"]]},"CDISpecDirs":["/etc/cdi","/var/run/cdi"],"DiscoveredDevices":[{"Source":"cdi","ID":"com.example.device1"},{"Source":"cdi","ID":"nvidia.com/gpu=gpu0"}],"NRI":{"Info":[["plugin-path","/usr/libexec/docker/nri-plugins"],["plugin-config-path","/etc/docker/nri/conf.d"]]},"Warnings":null}
|
||||
|
||||
@@ -56,4 +56,7 @@ Server:
|
||||
Base: 10.123.0.0/16, Size: 24
|
||||
Firewall Backend: nftables+firewalld
|
||||
ReloadedAt: 2025-07-16T16:59:14Z
|
||||
NRI:
|
||||
plugin-path: /usr/libexec/docker/nri-plugins
|
||||
plugin-config-path: /etc/docker/nri/conf.d
|
||||
|
||||
|
||||
@@ -56,4 +56,7 @@ Server:
|
||||
Base: 10.123.0.0/16, Size: 24
|
||||
Firewall Backend: nftables+firewalld
|
||||
ReloadedAt: 2025-07-16T16:59:14Z
|
||||
NRI:
|
||||
plugin-path: /usr/libexec/docker/nri-plugins
|
||||
plugin-config-path: /etc/docker/nri/conf.d
|
||||
|
||||
|
||||
@@ -80,4 +80,7 @@ Server:
|
||||
Base: 10.123.0.0/16, Size: 24
|
||||
Firewall Backend: nftables+firewalld
|
||||
ReloadedAt: 2025-07-16T16:59:14Z
|
||||
NRI:
|
||||
plugin-path: /usr/libexec/docker/nri-plugins
|
||||
plugin-config-path: /etc/docker/nri/conf.d
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"overlay2","DriverStatus":[["Backing Filesystem","extfs"],["Supports d_type","true"],["Using metacopy","false"],["Native Overlay Diff","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"qo2dfdig9mmxqkawulggepdih","NodeAddr":"165.227.107.89","LocalNodeState":"active","ControlAvailable":true,"Error":"","RemoteManagers":[{"NodeID":"qo2dfdig9mmxqkawulggepdih","Addr":"165.227.107.89:2377"}],"Nodes":1,"Managers":1,"Cluster":{"ID":"9vs5ygs0gguyyec4iqf2314c0","Version":{"Index":11},"CreatedAt":"2017-08-24T17:34:19.278062352Z","UpdatedAt":"2017-08-24T17:34:42.398815481Z","Spec":{"Name":"default","Labels":null,"Orchestration":{"TaskHistoryRetentionLimit":5},"Raft":{"SnapshotInterval":10000,"KeepOldSnapshots":0,"LogEntriesForSlowFollowers":500,"ElectionTick":3,"HeartbeatTick":1},"Dispatcher":{"HeartbeatPeriod":5000000000},"CAConfig":{"NodeCertExpiry":7776000000000000},"TaskDefaults":{},"EncryptionConfig":{"AutoLockManagers":true}},"TLSInfo":{"TrustRoot":"\n-----BEGIN CERTIFICATE-----\nMIIBajCCARCgAwIBAgIUaFCW5xsq8eyiJ+Pmcv3MCflMLnMwCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwODI0MTcyOTAwWhcNMzcwODE5MTcy\nOTAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABDy7NebyUJyUjWJDBUdnZoV6GBxEGKO4TZPNDwnxDxJcUdLVaB7WGa4/DLrW\nUfsVgh1JGik2VTiLuTMA1tLlNPOjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBQl16XFtaaXiUAwEuJptJlDjfKskDAKBggqhkjO\nPQQDAgNIADBFAiEAo9fTQNM5DP9bHVcTJYfl2Cay1bFu1E+lnpmN+EYJfeACIGKH\n1pCUkZ+D0IB6CiEZGWSHyLuXPM1rlP+I5KuS7sB8\n-----END CERTIFICATE-----\n","CertIssuerSubject":"MBMxETAPBgNVBAMTCHN3YXJtLWNh","CertIssuerPublicKey":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPLs15vJQnJSNYkMFR2dmhXoYHEQYo7hNk80PCfEPElxR0tVoHtYZrj8MutZR+xWCHUkaKTZVOIu5MwDW0uU08w=="},"RootRotationInProgress":false,"DefaultAddrPool":null,"SubnetSize":0,"DataPathPort":0}},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"FirewallBackend":{"Driver":"nftables+firewalld","Info":[["ReloadedAt","2025-07-16T16:59:14Z"]]},"CDISpecDirs":["/etc/cdi","/var/run/cdi"],"Warnings":null,"ClientInfo":{"Debug":false,"Context":"default","Plugins":[],"Warnings":null}}
|
||||
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"overlay2","DriverStatus":[["Backing Filesystem","extfs"],["Supports d_type","true"],["Using metacopy","false"],["Native Overlay Diff","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"qo2dfdig9mmxqkawulggepdih","NodeAddr":"165.227.107.89","LocalNodeState":"active","ControlAvailable":true,"Error":"","RemoteManagers":[{"NodeID":"qo2dfdig9mmxqkawulggepdih","Addr":"165.227.107.89:2377"}],"Nodes":1,"Managers":1,"Cluster":{"ID":"9vs5ygs0gguyyec4iqf2314c0","Version":{"Index":11},"CreatedAt":"2017-08-24T17:34:19.278062352Z","UpdatedAt":"2017-08-24T17:34:42.398815481Z","Spec":{"Name":"default","Labels":null,"Orchestration":{"TaskHistoryRetentionLimit":5},"Raft":{"SnapshotInterval":10000,"KeepOldSnapshots":0,"LogEntriesForSlowFollowers":500,"ElectionTick":3,"HeartbeatTick":1},"Dispatcher":{"HeartbeatPeriod":5000000000},"CAConfig":{"NodeCertExpiry":7776000000000000},"TaskDefaults":{},"EncryptionConfig":{"AutoLockManagers":true}},"TLSInfo":{"TrustRoot":"\n-----BEGIN CERTIFICATE-----\nMIIBajCCARCgAwIBAgIUaFCW5xsq8eyiJ+Pmcv3MCflMLnMwCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwODI0MTcyOTAwWhcNMzcwODE5MTcy\nOTAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABDy7NebyUJyUjWJDBUdnZoV6GBxEGKO4TZPNDwnxDxJcUdLVaB7WGa4/DLrW\nUfsVgh1JGik2VTiLuTMA1tLlNPOjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBQl16XFtaaXiUAwEuJptJlDjfKskDAKBggqhkjO\nPQQDAgNIADBFAiEAo9fTQNM5DP9bHVcTJYfl2Cay1bFu1E+lnpmN+EYJfeACIGKH\n1pCUkZ+D0IB6CiEZGWSHyLuXPM1rlP+I5KuS7sB8\n-----END CERTIFICATE-----\n","CertIssuerSubject":"MBMxETAPBgNVBAMTCHN3YXJtLWNh","CertIssuerPublicKey":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPLs15vJQnJSNYkMFR2dmhXoYHEQYo7hNk80PCfEPElxR0tVoHtYZrj8MutZR+xWCHUkaKTZVOIu5MwDW0uU08w=="},"RootRotationInProgress":false,"DefaultAddrPool":null,"SubnetSize":0,"DataPathPort":0}},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"FirewallBackend":{"Driver":"nftables+firewalld","Info":[["ReloadedAt","2025-07-16T16:59:14Z"]]},"CDISpecDirs":["/etc/cdi","/var/run/cdi"],"NRI":{"Info":[["plugin-path","/usr/libexec/docker/nri-plugins"],["plugin-config-path","/etc/docker/nri/conf.d"]]},"Warnings":null,"ClientInfo":{"Debug":false,"Context":"default","Plugins":[],"Warnings":null}}
|
||||
|
||||
@@ -4,14 +4,15 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/spf13/cobra"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func setupCobraCommands() (*cobra.Command, *cobra.Command, *cobra.Command) {
|
||||
@@ -139,7 +140,7 @@ func TestStdioAttributes(t *testing.T) {
|
||||
cli.Out().SetIsTerminal(tc.stdoutTty)
|
||||
actual := stdioAttributes(cli)
|
||||
|
||||
assert.Check(t, reflect.DeepEqual(actual, tc.expected))
|
||||
assert.Check(t, is.DeepEqual(actual, tc.expected, cmpopts.EquateComparable(attribute.Value{})))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -179,7 +180,7 @@ func TestAttributesFromError(t *testing.T) {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
actual := attributesFromError(tc.err)
|
||||
assert.Check(t, reflect.DeepEqual(actual, tc.expected))
|
||||
assert.Check(t, is.DeepEqual(actual, tc.expected, cmpopts.EquateComparable(attribute.Value{})))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package volume
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"maps"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -124,10 +127,10 @@ func TestVolumeCreateWithFlags(t *testing.T) {
|
||||
if options.Driver != expectedDriver {
|
||||
return client.VolumeCreateResult{}, fmt.Errorf("expected driver %q, got %q", expectedDriver, options.Driver)
|
||||
}
|
||||
if !reflect.DeepEqual(options.DriverOpts, expectedOpts) {
|
||||
if !maps.Equal(options.DriverOpts, expectedOpts) {
|
||||
return client.VolumeCreateResult{}, fmt.Errorf("expected drivers opts %v, got %v", expectedOpts, options.DriverOpts)
|
||||
}
|
||||
if !reflect.DeepEqual(options.Labels, expectedLabels) {
|
||||
if !maps.Equal(options.Labels, expectedLabels) {
|
||||
return client.VolumeCreateResult{}, fmt.Errorf("expected labels %v, got %v", expectedLabels, options.Labels)
|
||||
}
|
||||
return client.VolumeCreateResult{
|
||||
|
||||
@@ -322,26 +322,26 @@ type Transformer struct {
|
||||
|
||||
func createTransformHook(additionalTransformers ...Transformer) mapstructure.DecodeHookFuncType {
|
||||
transforms := map[reflect.Type]func(any) (any, error){
|
||||
reflect.TypeOf(types.External{}): transformExternal,
|
||||
reflect.TypeOf(types.HealthCheckTest{}): transformHealthCheckTest,
|
||||
reflect.TypeOf(types.ShellCommand{}): transformShellCommand,
|
||||
reflect.TypeOf(types.StringList{}): transformStringList,
|
||||
reflect.TypeOf(map[string]string{}): transformMapStringString,
|
||||
reflect.TypeOf(types.UlimitsConfig{}): transformUlimits,
|
||||
reflect.TypeOf(types.UnitBytes(0)): transformSize,
|
||||
reflect.TypeOf([]types.ServicePortConfig{}): transformServicePort,
|
||||
reflect.TypeOf(types.ServiceSecretConfig{}): transformStringSourceMap,
|
||||
reflect.TypeOf(types.ServiceConfigObjConfig{}): transformStringSourceMap,
|
||||
reflect.TypeOf(types.StringOrNumberList{}): transformStringOrNumberList,
|
||||
reflect.TypeOf(map[string]*types.ServiceNetworkConfig{}): transformServiceNetworkMap,
|
||||
reflect.TypeOf(types.Mapping{}): transformMappingOrListFunc("=", false),
|
||||
reflect.TypeOf(types.MappingWithEquals{}): transformMappingOrListFunc("=", true),
|
||||
reflect.TypeOf(types.Labels{}): transformMappingOrListFunc("=", false),
|
||||
reflect.TypeOf(types.MappingWithColon{}): transformMappingOrListFunc(":", false),
|
||||
reflect.TypeOf(types.HostsList{}): transformHostsList,
|
||||
reflect.TypeOf(types.ServiceVolumeConfig{}): transformServiceVolumeConfig,
|
||||
reflect.TypeOf(types.BuildConfig{}): transformBuildConfig,
|
||||
reflect.TypeOf(types.Duration(0)): transformStringToDuration,
|
||||
reflect.TypeFor[types.External](): transformExternal,
|
||||
reflect.TypeFor[types.HealthCheckTest](): transformHealthCheckTest,
|
||||
reflect.TypeFor[types.ShellCommand](): transformShellCommand,
|
||||
reflect.TypeFor[types.StringList](): transformStringList,
|
||||
reflect.TypeFor[map[string]string](): transformMapStringString,
|
||||
reflect.TypeFor[types.UlimitsConfig](): transformUlimits,
|
||||
reflect.TypeFor[types.UnitBytes](): transformSize,
|
||||
reflect.TypeFor[[]types.ServicePortConfig](): transformServicePort,
|
||||
reflect.TypeFor[types.ServiceSecretConfig](): transformStringSourceMap,
|
||||
reflect.TypeFor[types.ServiceConfigObjConfig](): transformStringSourceMap,
|
||||
reflect.TypeFor[types.StringOrNumberList](): transformStringOrNumberList,
|
||||
reflect.TypeFor[map[string]*types.ServiceNetworkConfig](): transformServiceNetworkMap,
|
||||
reflect.TypeFor[types.Mapping](): transformMappingOrListFunc("=", false),
|
||||
reflect.TypeFor[types.MappingWithEquals](): transformMappingOrListFunc("=", true),
|
||||
reflect.TypeFor[types.Labels](): transformMappingOrListFunc("=", false),
|
||||
reflect.TypeFor[types.MappingWithColon](): transformMappingOrListFunc(":", false),
|
||||
reflect.TypeFor[types.HostsList](): transformHostsList,
|
||||
reflect.TypeFor[types.ServiceVolumeConfig](): transformServiceVolumeConfig,
|
||||
reflect.TypeFor[types.BuildConfig](): transformBuildConfig,
|
||||
reflect.TypeFor[types.Duration](): transformStringToDuration,
|
||||
}
|
||||
|
||||
for _, transformer := range additionalTransformers {
|
||||
|
||||
+10
-10
@@ -56,15 +56,15 @@ func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig,
|
||||
overrideServices := mapByName(override)
|
||||
specials := &specials{
|
||||
m: map[reflect.Type]func(dst, src reflect.Value) error{
|
||||
reflect.TypeOf(&types.LoggingConfig{}): safelyMerge(mergeLoggingConfig),
|
||||
reflect.TypeOf([]types.ServicePortConfig{}): mergeSlice(toServicePortConfigsMap, toServicePortConfigsSlice),
|
||||
reflect.TypeOf([]types.ServiceSecretConfig{}): mergeSlice(toServiceSecretConfigsMap, toServiceSecretConfigsSlice),
|
||||
reflect.TypeOf([]types.ServiceConfigObjConfig{}): mergeSlice(toServiceConfigObjConfigsMap, toSServiceConfigObjConfigsSlice),
|
||||
reflect.TypeOf(&types.UlimitsConfig{}): mergeUlimitsConfig,
|
||||
reflect.TypeOf([]types.ServiceVolumeConfig{}): mergeSlice(toServiceVolumeConfigsMap, toServiceVolumeConfigsSlice),
|
||||
reflect.TypeOf(types.ShellCommand{}): mergeShellCommand,
|
||||
reflect.TypeOf(&types.ServiceNetworkConfig{}): mergeServiceNetworkConfig,
|
||||
reflect.PointerTo(reflect.TypeOf(uint64(1))): mergeUint64,
|
||||
reflect.PointerTo(reflect.TypeFor[types.LoggingConfig]()): safelyMerge(mergeLoggingConfig),
|
||||
reflect.TypeFor[[]types.ServicePortConfig](): mergeSlice(toServicePortConfigsMap, toServicePortConfigsSlice),
|
||||
reflect.TypeFor[[]types.ServiceSecretConfig](): mergeSlice(toServiceSecretConfigsMap, toServiceSecretConfigsSlice),
|
||||
reflect.TypeFor[[]types.ServiceConfigObjConfig](): mergeSlice(toServiceConfigObjConfigsMap, toSServiceConfigObjConfigsSlice),
|
||||
reflect.PointerTo(reflect.TypeFor[types.UlimitsConfig]()): mergeUlimitsConfig,
|
||||
reflect.TypeFor[[]types.ServiceVolumeConfig](): mergeSlice(toServiceVolumeConfigsMap, toServiceVolumeConfigsSlice),
|
||||
reflect.TypeFor[types.ShellCommand](): mergeShellCommand,
|
||||
reflect.PointerTo(reflect.TypeFor[types.ServiceNetworkConfig]()): mergeServiceNetworkConfig,
|
||||
reflect.PointerTo(reflect.TypeFor[uint64]()): mergeUint64,
|
||||
},
|
||||
}
|
||||
for name, overrideService := range overrideServices {
|
||||
@@ -77,7 +77,7 @@ func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig,
|
||||
}
|
||||
baseServices[name] = overrideService
|
||||
}
|
||||
services := []types.ServiceConfig{}
|
||||
services := make([]types.ServiceConfig, 0, len(baseServices))
|
||||
for _, baseService := range baseServices {
|
||||
services = append(services, baseService)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package connhelper
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
@@ -27,7 +26,8 @@ func TestSSHFlags(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
assert.DeepEqual(t, addSSHTimeout(tc.in), tc.out)
|
||||
result := addSSHTimeout(tc.in)
|
||||
assert.DeepEqual(t, result, tc.out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,9 +57,7 @@ func TestDisablePseudoTerminalAllocation(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := disablePseudoTerminalAllocation(tc.sshFlags)
|
||||
if !reflect.DeepEqual(result, tc.expected) {
|
||||
t.Errorf("expected %v, got %v", tc.expected, result)
|
||||
}
|
||||
assert.DeepEqual(t, result, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
variable "GO_VERSION" {
|
||||
default = "1.25.5"
|
||||
default = "1.25.6"
|
||||
}
|
||||
variable "VERSION" {
|
||||
default = ""
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ mddocs: build_docker_image ## generate markdown files from go source
|
||||
|
||||
.PHONY: yamldocs
|
||||
yamldocs: build_docker_image ## generate documentation YAML files consumed by docs repo
|
||||
$(DOCKER_RUN) -it $(DEV_DOCKER_IMAGE_NAME) make yamldocs
|
||||
$(DOCKER_RUN) $(DEV_DOCKER_IMAGE_NAME) make yamldocs
|
||||
|
||||
.PHONY: test ## run unit and e2e tests
|
||||
test: test-unit test-e2e
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.25.5
|
||||
ARG GO_VERSION=1.25.6
|
||||
|
||||
# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image.
|
||||
# It must be a supported tag in the docker.io/library/alpine image repository
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.25.5
|
||||
ARG GO_VERSION=1.25.6
|
||||
|
||||
# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image.
|
||||
# It must be a supported tag in the docker.io/library/alpine image repository
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.25.5
|
||||
ARG GO_VERSION=1.25.6
|
||||
|
||||
# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image.
|
||||
# It must be a supported tag in the docker.io/library/alpine image repository
|
||||
|
||||
@@ -1282,7 +1282,9 @@ connect to services running on the host machine.
|
||||
|
||||
It's conventional to use `host.docker.internal` as the hostname referring to
|
||||
`host-gateway`. Docker Desktop automatically resolves this hostname, see
|
||||
[Explore networking features](https://docs.docker.com/desktop/features/networking/networking-how-tos/#i-want-to-connect-from-a-container-to-a-service-on-the-host).
|
||||
[Explore networking how-tos on Docker Desktop](https://docs.docker.com/desktop/features/networking/networking-how-tos/#connect-a-container-to-a-service-on-the-host)
|
||||
and
|
||||
[Configure host gateway IP](https://docs.docker.com/reference/cli/dockerd/#configure-host-gateway-ip).
|
||||
|
||||
The following example shows how the special `host-gateway` value works. The
|
||||
example runs an HTTP server that serves a file from host to container over the
|
||||
|
||||
@@ -114,8 +114,7 @@ Options:
|
||||
|
||||
### Environment variables
|
||||
|
||||
The following list of environment variables are supported by the `docker` command
|
||||
line:
|
||||
The following environment variables control the behavior of the `docker` command-line client:
|
||||
|
||||
| Variable | Description |
|
||||
| :---------------------------- |:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|
||||
@@ -876,7 +876,7 @@ You can view the configured CDI specification directories using the `docker info
|
||||
|
||||
#### Disable CDI devices
|
||||
|
||||
The feature in enabled by default. To disable it, use the `cdi` options in the `deamon.json` file:
|
||||
The feature in enabled by default. To disable it, use the `cdi` options in the `daemon.json` file:
|
||||
|
||||
```json
|
||||
"features": {
|
||||
|
||||
@@ -2,14 +2,14 @@ package jsonstream
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/moby/moby/api/types/jsonstream"
|
||||
"github.com/docker/cli/internal/test"
|
||||
"github.com/moby/moby/client/pkg/progress"
|
||||
"github.com/moby/moby/client/pkg/streamformatter"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
@@ -23,21 +23,19 @@ func TestDisplay(t *testing.T) {
|
||||
})
|
||||
|
||||
go func() {
|
||||
enc := json.NewEncoder(server)
|
||||
id := test.RandomID()[:12] // short-ID
|
||||
progressOutput := streamformatter.NewJSONProgressOutput(server, true)
|
||||
for i := 0; i < 100; i++ {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
assert.NilError(t, server.Close(), "failed to close jsonmessage server")
|
||||
return
|
||||
default:
|
||||
err := enc.Encode(JSONMessage{
|
||||
Status: "Downloading",
|
||||
ID: fmt.Sprintf("id-%d", i),
|
||||
Progress: &jsonstream.Progress{
|
||||
Current: int64(i),
|
||||
Total: 100,
|
||||
Start: 0,
|
||||
},
|
||||
err := progressOutput.WriteProgress(progress.Progress{
|
||||
ID: id,
|
||||
Message: "Downloading",
|
||||
Current: int64(i),
|
||||
Total: 100,
|
||||
})
|
||||
if err != nil {
|
||||
break
|
||||
|
||||
@@ -216,7 +216,7 @@ func continueOnError(err error) bool {
|
||||
}
|
||||
|
||||
func (c *client) iterateEndpoints(ctx context.Context, namedRef reference.Named, each func(context.Context, distribution.Repository, reference.Named) (bool, error)) error {
|
||||
endpoints, err := allEndpoints(namedRef, c.insecureRegistry)
|
||||
endpoints, err := allEndpoints(ctx, namedRef, c.insecureRegistry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -271,11 +271,11 @@ func (c *client) iterateEndpoints(ctx context.Context, namedRef reference.Named,
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return newNotFoundError(namedRef.String())
|
||||
return notFoundError{errors.New("no such manifest: " + namedRef.String())}
|
||||
}
|
||||
|
||||
// allEndpoints returns a list of endpoints ordered by priority (v2, http).
|
||||
func allEndpoints(namedRef reference.Named, insecure bool) ([]registry.APIEndpoint, error) {
|
||||
func allEndpoints(ctx context.Context, namedRef reference.Named, insecure bool) ([]registry.APIEndpoint, error) {
|
||||
var serviceOpts registry.ServiceOptions
|
||||
if insecure {
|
||||
logrus.Debugf("allowing insecure registry for: %s", reference.Domain(namedRef))
|
||||
@@ -285,22 +285,11 @@ func allEndpoints(namedRef reference.Named, insecure bool) ([]registry.APIEndpoi
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
endpoints, err := registryService.Endpoints(context.TODO(), reference.Domain(namedRef))
|
||||
endpoints, err := registryService.Endpoints(ctx, reference.Domain(namedRef))
|
||||
logrus.Debugf("endpoints for %s: %v", namedRef, endpoints)
|
||||
return endpoints, err
|
||||
}
|
||||
|
||||
func newNotFoundError(ref string) *notFoundError {
|
||||
return ¬FoundError{err: errors.New("no such manifest: " + ref)}
|
||||
}
|
||||
type notFoundError struct{ error }
|
||||
|
||||
type notFoundError struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (n *notFoundError) Error() string {
|
||||
return n.err.Error()
|
||||
}
|
||||
|
||||
// NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
|
||||
func (notFoundError) NotFound() {}
|
||||
|
||||
+189
-144
@@ -113,12 +113,11 @@ pull** IMAGE, before it starts the container from that image.
|
||||
**-a**, **--attach**=[]
|
||||
Attach to STDIN, STDOUT or STDERR.
|
||||
|
||||
In foreground mode (the default when **-d**
|
||||
is not specified), **docker run** can start the process in the container
|
||||
and attach the console to the process's standard input, output, and standard
|
||||
error. It can even pretend to be a TTY (this is what most commandline
|
||||
executables expect) and pass along signals. The **-a** option can be set for
|
||||
each of stdin, stdout, and stderr.
|
||||
In foreground mode (the default when **-d** is not specified), **docker run**
|
||||
can start the process in the container and attach the console to the process's
|
||||
standard input, output, and standard error. It can even pretend to be a TTY
|
||||
(this is what most commandline executables expect) and pass along signals.
|
||||
The **-a** option can be set for each of stdin, stdout, and stderr.
|
||||
|
||||
**--add-host**=[]
|
||||
Add a custom host-to-IP mapping (host=ip, or host:ip)
|
||||
@@ -141,32 +140,32 @@ each of stdin, stdout, and stderr.
|
||||
CPU shares (relative weight)
|
||||
|
||||
By default, all containers get the same proportion of CPU cycles. This proportion
|
||||
can be modified by changing the container's CPU share weighting relative
|
||||
to the weighting of all other running containers.
|
||||
can be modified by changing the container's CPU share weighting relative
|
||||
to the weighting of all other running containers.
|
||||
|
||||
To modify the proportion from the default of 1024, use the **-c** or **--cpu-shares**
|
||||
flag to set the weighting to 2 or higher.
|
||||
To modify the proportion from the default of 1024, use the **-c** or **--cpu-shares**
|
||||
flag to set the weighting to 2 or higher.
|
||||
|
||||
The proportion will only apply when CPU-intensive processes are running.
|
||||
When tasks in one container are idle, other containers can use the
|
||||
left-over CPU time. The actual amount of CPU time will vary depending on
|
||||
the number of containers running on the system.
|
||||
The proportion will only apply when CPU-intensive processes are running.
|
||||
When tasks in one container are idle, other containers can use the
|
||||
left-over CPU time. The actual amount of CPU time will vary depending on
|
||||
the number of containers running on the system.
|
||||
|
||||
For example, consider three containers, one has a cpu-share of 1024 and
|
||||
two others have a cpu-share setting of 512. When processes in all three
|
||||
containers attempt to use 100% of CPU, the first container would receive
|
||||
50% of the total CPU time. If you add a fourth container with a cpu-share
|
||||
of 1024, the first container only gets 33% of the CPU. The remaining containers
|
||||
receive 16.5%, 16.5% and 33% of the CPU.
|
||||
For example, consider three containers, one has a cpu-share of 1024 and
|
||||
two others have a cpu-share setting of 512. When processes in all three
|
||||
containers attempt to use 100% of CPU, the first container would receive
|
||||
50% of the total CPU time. If you add a fourth container with a cpu-share
|
||||
of 1024, the first container only gets 33% of the CPU. The remaining containers
|
||||
receive 16.5%, 16.5% and 33% of the CPU.
|
||||
|
||||
On a multi-core system, the shares of CPU time are distributed over all CPU
|
||||
cores. Even if a container is limited to less than 100% of CPU time, it can
|
||||
use 100% of each individual CPU core.
|
||||
On a multi-core system, the shares of CPU time are distributed over all CPU
|
||||
cores. Even if a container is limited to less than 100% of CPU time, it can
|
||||
use 100% of each individual CPU core.
|
||||
|
||||
For example, consider a system with more than three cores. If you start one
|
||||
container **{C0}** with **-c=512** running one process, and another container
|
||||
**{C1}** with **-c=1024** running two processes, this can result in the following
|
||||
division of CPU shares:
|
||||
For example, consider a system with more than three cores. If you start one
|
||||
container **{C0}** with **-c=512** running one process, and another container
|
||||
**{C1}** with **-c=1024** running two processes, this can result in the following
|
||||
division of CPU shares:
|
||||
|
||||
PID container CPU CPU share
|
||||
100 {C0} 0 100% of CPU0
|
||||
@@ -186,33 +185,42 @@ division of CPU shares:
|
||||
**""**: (unset) use the daemon's default configuration (**host** on cgroup v1, **private** on cgroup v2)
|
||||
|
||||
**--cgroup-parent**=""
|
||||
Path to cgroups under which the cgroup for the container will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist.
|
||||
Path to cgroups under which the cgroup for the container will be created.
|
||||
If the path is not absolute, the path is considered to be relative to the
|
||||
cgroups path of the init process. Cgroups will be created if they do not
|
||||
already exist.
|
||||
|
||||
**--cidfile**=""
|
||||
Write the container ID to the file
|
||||
|
||||
**--cpu-count**=*0*
|
||||
Limit the number of CPUs available for execution by the container.
|
||||
|
||||
On Windows Server containers, this is approximated as a percentage of total CPU usage.
|
||||
Limit the number of CPUs available for execution by the container.
|
||||
|
||||
On Windows Server containers, the processor resource controls are mutually exclusive, the order of precedence is CPUCount first, then CPUShares, and CPUPercent last.
|
||||
On Windows Server containers, this is approximated as a percentage of total
|
||||
CPU usage. On Windows Server containers, the processor resource controls
|
||||
are mutually exclusive, the order of precedence is CPUCount first, then
|
||||
CPUShares, and CPUPercent last.
|
||||
|
||||
**--cpu-percent**=*0*
|
||||
Limit the percentage of CPU available for execution by a container running on a Windows daemon.
|
||||
Limit the percentage of CPU available for execution by a container running
|
||||
on a Windows daemon.
|
||||
|
||||
On Windows Server containers, the processor resource controls are mutually exclusive, the order of precedence is CPUCount first, then CPUShares, and CPUPercent last.
|
||||
On Windows Server containers, the processor resource controls are mutually
|
||||
exclusive, the order of precedence is CPUCount first, then CPUShares, and
|
||||
CPUPercent last.
|
||||
|
||||
**--cpu-period**=*0*
|
||||
Limit the CPU CFS (Completely Fair Scheduler) period
|
||||
|
||||
Limit the container's CPU usage. This flag tell the kernel to restrict the container's CPU usage to the period you specify.
|
||||
Limit the container's CPU usage. This flag tell the kernel to restrict the
|
||||
container's CPU usage to the period you specify.
|
||||
|
||||
**--cpuset-cpus**=""
|
||||
CPUs in which to allow execution (0-3, 0,1)
|
||||
|
||||
**--cpuset-mems**=""
|
||||
Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.
|
||||
Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective
|
||||
on NUMA systems.
|
||||
|
||||
If you have four memory nodes on your system (0-3), use `--cpuset-mems=0,1`
|
||||
then processes in your Docker container will only use memory from the first
|
||||
@@ -222,55 +230,70 @@ two memory nodes.
|
||||
Limit the CPU CFS (Completely Fair Scheduler) quota
|
||||
|
||||
Limit the container's CPU usage. By default, containers run with the full
|
||||
CPU resource. This flag tell the kernel to restrict the container's CPU usage
|
||||
to the quota you specify.
|
||||
CPU resource. This flag tell the kernel to restrict the container's CPU
|
||||
usage to the quota you specify.
|
||||
|
||||
**--cpu-rt-period**=0
|
||||
Limit the CPU real-time period in microseconds
|
||||
|
||||
Limit the container's Real Time CPU usage. This flag tell the kernel to restrict the container's Real Time CPU usage to the period you specify.
|
||||
Limit the container's Real Time CPU usage. This flag tell the kernel to
|
||||
restrict the container's Real Time CPU usage to the period you specify.
|
||||
|
||||
**--cpu-rt-runtime**=0
|
||||
Limit the CPU real-time runtime in microseconds
|
||||
|
||||
Limit the containers Real Time CPU usage. This flag tells the kernel to limit the amount of time in a given CPU period Real Time tasks may consume. Ex:
|
||||
Period of 1,000,000us and Runtime of 950,000us means that this container could consume 95% of available CPU and leave the remaining 5% to normal priority tasks.
|
||||
Limit the containers Real Time CPU usage. This flag tells the kernel to
|
||||
limit the amount of time in a given CPU period Real Time tasks may consume.
|
||||
For example, a period of 1,000,000us and Runtime of 950,000us means that
|
||||
this container could consume 95% of available CPU and leave the remaining
|
||||
5% to normal priority tasks.
|
||||
|
||||
The sum of all runtimes across containers cannot exceed the amount allotted to the parent cgroup.
|
||||
The sum of all runtimes across containers cannot exceed the amount allotted
|
||||
to the parent cgroup.
|
||||
|
||||
**--cpus**=0.0
|
||||
Number of CPUs. The default is *0.0* which means no limit.
|
||||
|
||||
**-d**, **--detach**=*true*|*false*
|
||||
Detached mode: run the container in the background and print the new container ID. The default is *false*.
|
||||
Detached mode: run the container in the background and print the new
|
||||
container ID. The default is *false*.
|
||||
|
||||
At any time you can run **docker ps** in
|
||||
the other shell to view a list of the running containers. You can reattach to a
|
||||
detached container with **docker attach**.
|
||||
At any time you can run **docker ps** in the other shell to view a list
|
||||
of the running containers. You can reattach to a detached container with
|
||||
**docker attach**.
|
||||
|
||||
When attached in the tty mode, you can detach from the container (and leave it
|
||||
running) using a configurable key sequence. The default sequence is `CTRL-p CTRL-q`.
|
||||
You configure the key sequence using the **--detach-keys** option or a configuration file.
|
||||
See **config-json(5)** for documentation on using a configuration file.
|
||||
When attached in the tty mode, you can detach from the container (and leave
|
||||
it running) using a configurable key sequence. The default sequence is
|
||||
`CTRL-p CTRL-q`.
|
||||
You configure the key sequence using the **--detach-keys** option or a
|
||||
configuration file. See **config-json(5)** for documentation on using a
|
||||
configuration file.
|
||||
|
||||
**--detach-keys**=*key*
|
||||
Override the key sequence for detaching a container; *key* is a single character from the [a-Z] range, or **ctrl**-*value*, where *value* is one of: **a-z**, **@**, **^**, **[**, **,**, or **_**.
|
||||
Override the key sequence for detaching a container; *key* is a single
|
||||
character from the [a-Z] range, or **ctrl**-*value*, where *value* is
|
||||
one of: **a-z**, **@**, **^**, **[**, **,**, or **_**.
|
||||
|
||||
**--device**=*onhost*:*incontainer*[:*mode*]
|
||||
Add a host device *onhost* to the container under the *incontainer* name.
|
||||
Optional *mode* parameter can be used to specify device permissions, it is
|
||||
a combination of **r** (for read), **w** (for write), and **m** (for **mknod**(2)).
|
||||
Optional *mode* parameter can be used to specify device permissions, it is
|
||||
a combination of **r** (for read), **w** (for write), and **m** (for
|
||||
**mknod**(2)).
|
||||
|
||||
For example, **--device=/dev/sdc:/dev/xvdc:rwm** will give a container all
|
||||
permissions for the host device **/dev/sdc**, seen as **/dev/xvdc** inside the container.
|
||||
For example, **--device=/dev/sdc:/dev/xvdc:rwm** will give a container all
|
||||
permissions for the host device **/dev/sdc**, seen as **/dev/xvdc** inside
|
||||
the container.
|
||||
|
||||
**--device-cgroup-rule**="*type* *major*:*minor* *mode*"
|
||||
Add a rule to the cgroup allowed devices list. The rule is expected to be in the format specified in the Linux kernel documentation (Documentation/cgroup-v1/devices.txt):
|
||||
Add a rule to the cgroup allowed devices list. The rule is expected to be
|
||||
in the format specified in the Linux kernel documentation
|
||||
(Documentation/cgroup-v1/devices.txt):
|
||||
- *type*: **a** (all), **c** (char), or **b** (block);
|
||||
- *major* and *minor*: either a number, or __*__ for all;
|
||||
- *mode*: a composition of **r** (read), **w** (write), and **m** (**mknod**(2)).
|
||||
|
||||
Example: **--device-cgroup-rule "c 1:3 mr"**: allow for a character device idendified by **1:3** to be created and read.
|
||||
Example: **--device-cgroup-rule "c 1:3 mr"**: allow for a character device
|
||||
identified by **1:3** to be created and read.
|
||||
|
||||
**--device-read-bps**=[]
|
||||
Limit read rate from a device (e.g. --device-read-bps=/dev/sda:1mb)
|
||||
@@ -285,7 +308,8 @@ permissions for the host device **/dev/sdc**, seen as **/dev/xvdc** inside the c
|
||||
Limit write rate to a device (e.g. --device-write-iops=/dev/sda:1000)
|
||||
|
||||
**--dns-search**=[]
|
||||
Set custom DNS search domains (Use --dns-search=. if you don't wish to set the search domain)
|
||||
Set custom DNS search domains (Use --dns-search=. if you don't wish to set
|
||||
the search domain)
|
||||
|
||||
**--dns-option**=[]
|
||||
Set custom DNS options
|
||||
@@ -293,10 +317,10 @@ permissions for the host device **/dev/sdc**, seen as **/dev/xvdc** inside the c
|
||||
**--dns**=[]
|
||||
Set custom DNS servers
|
||||
|
||||
This option can be used to override the DNS
|
||||
configuration passed to the container. Typically this is necessary when the
|
||||
host DNS configuration is invalid for the container (e.g., 127.0.0.1). When this
|
||||
is the case the **--dns** flags is necessary for every run.
|
||||
This option can be used to override the DNS configuration passed to the
|
||||
container. Typically this is necessary when the host DNS configuration
|
||||
is invalid for the container (e.g., 127.0.0.1). When this is the case
|
||||
the **--dns** flags is necessary for every run.
|
||||
|
||||
**--domainname**=""
|
||||
Container NIS domain name
|
||||
@@ -315,24 +339,24 @@ inside of the container.
|
||||
Overwrite the default ENTRYPOINT of the image
|
||||
|
||||
This option allows you to overwrite the default entrypoint of the image that
|
||||
is set in the Dockerfile. The ENTRYPOINT of an image is similar to a COMMAND
|
||||
because it specifies what executable to run when the container starts, but it is
|
||||
(purposely) more difficult to override. The ENTRYPOINT gives a container its
|
||||
default nature or behavior, so that when you set an ENTRYPOINT you can run the
|
||||
container as if it were that binary, complete with default options, and you can
|
||||
pass in more options via the COMMAND. But, sometimes an operator may want to run
|
||||
something else inside the container, so you can override the default ENTRYPOINT
|
||||
at runtime by using a **--entrypoint** and a string to specify the new
|
||||
ENTRYPOINT.
|
||||
is set in the Dockerfile. The ENTRYPOINT of an image is similar to a COMMAND
|
||||
because it specifies what executable to run when the container starts, but it is
|
||||
(purposely) more difficult to override. The ENTRYPOINT gives a container its
|
||||
default nature or behavior, so that when you set an ENTRYPOINT you can run the
|
||||
container as if it were that binary, complete with default options, and you can
|
||||
pass in more options via the COMMAND. But, sometimes an operator may want to run
|
||||
something else inside the container, so you can override the default ENTRYPOINT
|
||||
at runtime by using a **--entrypoint** and a string to specify the new
|
||||
ENTRYPOINT.
|
||||
|
||||
**--env-file**=[]
|
||||
Read in a line delimited file of environment variables
|
||||
|
||||
**--expose**=[]
|
||||
Expose a port, or a range of ports (e.g. --expose=3300-3310) informs Docker
|
||||
that the container listens on the specified network ports at runtime. Docker
|
||||
uses this information to interconnect containers using links and to set up port
|
||||
redirection on the host system.
|
||||
Expose a port, or a range of ports (e.g. --expose=3300-3310) informs the
|
||||
docker daemon that the container listens on the specified network ports at
|
||||
runtime. The docker daemon uses this information to interconnect containers
|
||||
using links and to set up port redirection on the host system.
|
||||
|
||||
**--group-add**=[]
|
||||
Add additional groups to run as
|
||||
@@ -390,10 +414,10 @@ is `hyperv`. Linux only supports `default`.
|
||||
Kernel memory limit; *S* is an optional suffix which can be one of **b**, **k**, **m**, or **g**.
|
||||
|
||||
Constrains the kernel memory available to a container. If a limit of 0
|
||||
is specified (not using **--kernel-memory**), the container's kernel memory
|
||||
is not limited. If you specify a limit, it may be rounded up to a multiple
|
||||
of the operating system's page size and the value can be very large,
|
||||
millions of trillions.
|
||||
is specified (not using **--kernel-memory**), the container's kernel memory
|
||||
is not limited. If you specify a limit, it may be rounded up to a multiple
|
||||
of the operating system's page size and the value can be very large,
|
||||
millions of trillions.
|
||||
|
||||
**--label-file**=[]
|
||||
Read in a line delimited file of labels
|
||||
@@ -422,24 +446,27 @@ which interface and port to use.
|
||||
Memory limit; *S* is an optional suffix which can be one of **b**, **k**, **m**, or **g**.
|
||||
|
||||
Allows you to constrain the memory available to a container. If the host
|
||||
supports swap memory, then the **-m** memory setting can be larger than physical
|
||||
RAM. If a limit of 0 is specified (not using **-m**), the container's memory is
|
||||
not limited. The actual limit may be rounded up to a multiple of the operating
|
||||
system's page size (the value would be very large, that's millions of trillions).
|
||||
supports swap memory, then the **-m** memory setting can be larger than physical
|
||||
RAM. If a limit of 0 is specified (not using **-m**), the container's memory is
|
||||
not limited. The actual limit may be rounded up to a multiple of the operating
|
||||
system's page size (the value would be very large, that's millions of trillions).
|
||||
|
||||
**--memory-reservation**=*number*[*S]
|
||||
Memory soft limit; *S* is an optional suffix which can be one of **b**, **k**, **m**, or **g**.
|
||||
|
||||
After setting memory reservation, when the system detects memory contention
|
||||
or low memory, containers are forced to restrict their consumption to their
|
||||
reservation. So you should always set the value below **--memory**, otherwise the
|
||||
hard limit will take precedence. By default, memory reservation will be the same
|
||||
as memory limit.
|
||||
or low memory, containers are forced to restrict their consumption to their
|
||||
reservation. So you should always set the value below **--memory**, otherwise the
|
||||
hard limit will take precedence. By default, memory reservation will be the same
|
||||
as memory limit.
|
||||
|
||||
**--memory-swap**=*number*[*S*]
|
||||
Combined memory plus swap limit; *S* is an optional suffix which can be one of **b**, **k**, **m**, or **g**.
|
||||
Combined memory plus swap limit; *S* is an optional suffix which can be
|
||||
one of **b**, **k**, **m**, or **g**.
|
||||
|
||||
This option can only be used together with **--memory**. The argument should always be larger than that of **--memory**. Default is double the value of **--memory**. Set to **-1** to enable unlimited swap.
|
||||
This option can only be used together with **--memory**. The argument
|
||||
should always be larger than that of **--memory**. Default is double the
|
||||
value of **--memory**. Set to **-1** to enable unlimited swap.
|
||||
|
||||
**--mac-address**=""
|
||||
Container MAC address (e.g., **92:d0:c6:0a:29:33**)
|
||||
@@ -453,11 +480,11 @@ according to RFC4862.
|
||||
|
||||
Current supported mount `TYPES` are `bind`, `volume`, and `tmpfs`.
|
||||
|
||||
e.g.
|
||||
For example;
|
||||
|
||||
`type=bind,source=/path/on/host,destination=/path/in/container`
|
||||
|
||||
`type=volume,source=my-volume,destination=/path/in/container,volume-label="color=red",volume-label="shape=round"`
|
||||
`type=volume,source=my-volume,destination=/path/in/container,volume-label="color=red"`
|
||||
|
||||
`type=tmpfs,tmpfs-size=512M,destination=/path/in/container`
|
||||
|
||||
@@ -484,7 +511,9 @@ according to RFC4862.
|
||||
|
||||
* `volume-driver`: Name of the volume-driver plugin.
|
||||
* `volume-label`: Custom metadata.
|
||||
* `volume-nocopy`: `true`(default) or `false`. If set to `false`, the Engine copies existing files and directories under the mount-path into the volume, allowing the host to access them.
|
||||
* `volume-nocopy`: `true`(default) or `false`. If set to `false`, the Engine
|
||||
copies existing files and directories under the mount-path into the volume,
|
||||
allowing the host to access them.
|
||||
* `volume-opt`: specific to a given volume driver.
|
||||
|
||||
Options specific to `tmpfs`:
|
||||
@@ -497,17 +526,15 @@ according to RFC4862.
|
||||
|
||||
The operator can identify a container in three ways:
|
||||
|
||||
| Identifier type | Example value |
|
||||
|:----------------------|:-------------------------------------------------------------------|
|
||||
| UUID long identifier | "f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778" |
|
||||
| UUID short identifier | "f78375b1c487" |
|
||||
| Name | "evil_ptolemy" |
|
||||
- **long ID**: `f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778`
|
||||
- **short ID**: `f78375b1c487`
|
||||
- **Name**: `evil_ptolemy`
|
||||
|
||||
The UUID identifiers come from the Docker daemon, and if a name is not assigned
|
||||
to the container with **--name** then the daemon will also generate a random
|
||||
string name. The name is useful when defining links (see **--link**) (or any
|
||||
other place you need to identify a container). This works for both background
|
||||
and foreground Docker containers.
|
||||
The ID identifiers come from the Docker daemon, and if a name is not assigned
|
||||
to the container with **--name** then the daemon will also generate a random
|
||||
string name. The name is useful when defining links (see **--link**) (or any
|
||||
other place you need to identify a container). This works for both background
|
||||
and foreground Docker containers.
|
||||
|
||||
**--network**=*type*
|
||||
Set the Network mode for the container. Supported values are:
|
||||
@@ -555,8 +582,9 @@ Use `docker port`(1) to see the actual mapping, e.g. `docker port CONTAINER $CON
|
||||
**--pid**=""
|
||||
Set the PID mode for the container
|
||||
Default is to create a private PID namespace for the container
|
||||
'container:<name|id>': join another container's PID namespace
|
||||
'host': use the host's PID namespace for the container. Note: the host mode gives the container full access to local PID and is therefore considered insecure.
|
||||
'container:<name|id>': join another container's PID namespace
|
||||
'host': use the host's PID namespace for the container. Note: the host mode
|
||||
gives the container full access to local PID and is therefore considered insecure.
|
||||
|
||||
**--userns**=""
|
||||
Set the usernamespace mode for the container when `userns-remap` option is enabled.
|
||||
@@ -566,9 +594,11 @@ Use `docker port`(1) to see the actual mapping, e.g. `docker port CONTAINER $CON
|
||||
Tune the container's pids (process IDs) limit. Set to `-1` to have unlimited pids for the container.
|
||||
|
||||
**--uts**=*type*
|
||||
Set the UTS mode for the container. The only possible *type* is **host**, meaning to
|
||||
use the host's UTS namespace inside the container.
|
||||
Note: the host mode gives the container access to changing the host's hostname and is therefore considered insecure.
|
||||
Set the UTS mode for the container. The only possible *type* is **host**,
|
||||
meaning to use the host's UTS namespace inside the container.
|
||||
|
||||
Note: the host mode gives the container access to changing the host's hostname
|
||||
and is therefore considered insecure.
|
||||
|
||||
**--privileged** [**true**|**false**]
|
||||
Give extended privileges to this container. A "privileged" container is given access to all devices.
|
||||
@@ -588,46 +618,50 @@ its root filesystem mounted as read only prohibiting any writes.
|
||||
**--restart** *policy*
|
||||
Restart policy to apply when a container exits. Supported values are:
|
||||
|
||||
| Policy | Result |
|
||||
|:-------------------------------|:----------------------|
|
||||
| **no** | Do not automatically restart the container when it exits. |
|
||||
| **on-failure**[:_max-retries_] | Restart only if the container exits with a non-zero exit status. Optionally, limit the number of restart retries the Docker daemon attempts. |
|
||||
| **always** | Always restart the container regardless of the exit status. When you specify always, the Docker daemon will try to restart the container indefinitely. The container will also always start on daemon startup, regardless of the current state of the container. |
|
||||
| **unless-stopped** | Always restart the container regardless of the exit status, but do not start it on daemon startup if the container has been put to a stopped state before. |
|
||||
| Policy | Result |
|
||||
|:-------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **no** | Do not automatically restart the container when it exits. |
|
||||
| **on-failure**[:_max-retries_] | Restart only if the container exits with a non-zero exit status. Optionally, limit the number of restart retries the Docker daemon attempts. |
|
||||
| **always** | Always restart the container regardless of the exit status. When you specify always, the Docker daemon tries to restart the container indefinitely. The container always start on daemon startup, regardless of the current state of the container. |
|
||||
| **unless-stopped** | Always restart the container regardless of the exit status, but do not start it on daemon startup if the container has been put to a stopped state before. |
|
||||
|
||||
Default is **no**.
|
||||
|
||||
**--rm** **true**|**false**
|
||||
Automatically remove the container when it exits. The default is **false**.
|
||||
`--rm` flag can work together with `-d`, and auto-removal will be done on daemon side. Note that it's
|
||||
incompatible with any restart policy other than `none`.
|
||||
`--rm` flag can work together with `-d`, and auto-removal will be done on
|
||||
daemon side. Note that it's incompatible with any restart policy other than
|
||||
`none`.
|
||||
|
||||
**--security-opt** *value*[,...]
|
||||
Security Options for the container. The following options can be given:
|
||||
|
||||
"label=user:USER" : Set the label user for the container
|
||||
"label=role:ROLE" : Set the label role for the container
|
||||
"label=type:TYPE" : Set the label type for the container
|
||||
"label=level:LEVEL" : Set the label level for the container
|
||||
"label=disable" : Turn off label confinement for the container
|
||||
"no-new-privileges" : Disable container processes from gaining additional privileges
|
||||
|
||||
"seccomp=unconfined" : Turn off seccomp confinement for the container
|
||||
"seccomp=profile.json : White listed syscalls seccomp Json file to be used as a seccomp filter
|
||||
|
||||
"apparmor=unconfined" : Turn off apparmor confinement for the container
|
||||
"apparmor=your-profile" : Set the apparmor confinement profile for the container
|
||||
| Option | Description |
|
||||
|:-------------------------|:-----------------------------------------------------------------------|
|
||||
| "label=user:USER" | Set the label user for the container |
|
||||
| "label=role:ROLE" | Set the label role for the container |
|
||||
| "label=type:TYPE" | Set the label type for the container |
|
||||
| "label=level:LEVEL" | Set the label level for the container |
|
||||
| "label=disable" | Turn off label confinement for the container |
|
||||
| "no-new-privileges" | Disable container processes from gaining additional privileges |
|
||||
| "seccomp=unconfined" | Turn off seccomp confinement for the container |
|
||||
| "seccomp=profile.json" | White listed syscalls seccomp Json file to be used as a seccomp filter |
|
||||
| "apparmor=unconfined" | Turn off apparmor confinement for the container |
|
||||
| "apparmor=your-profile" | Set the apparmor confinement profile for the container |
|
||||
|
||||
**--storage-opt**
|
||||
Storage driver options per container
|
||||
|
||||
$ docker run -it --storage-opt size=120G fedora /bin/bash
|
||||
|
||||
This (size) will allow to set the container rootfs size to 120G at creation time.
|
||||
This option is only available for the `btrfs`, `overlay2` and `zfs` graph drivers.
|
||||
For the `btrfs` and `zfs` storage drivers, user cannot pass a size less than the Default BaseFS Size.
|
||||
For the `overlay2` storage driver, the size option is only available if the backing fs is `xfs` and mounted with the `pquota` mount option.
|
||||
Under these conditions, user can pass any size less than the backing fs size.
|
||||
This (size) will allow to set the container rootfs size to 120G at creation
|
||||
time. This option is only available for the `btrfs`, `overlay2` and `zfs`
|
||||
graph drivers.
|
||||
For the `btrfs` and `zfs` storage drivers, user cannot pass a size less than
|
||||
the Default BaseFS Size. For the `overlay2` storage driver, the size option
|
||||
is only available if the backing fs is `xfs` and mounted with the `pquota`
|
||||
mount option. Under these conditions, user can pass any size less than the
|
||||
backing fs size.
|
||||
|
||||
**--stop-signal**=""
|
||||
Signal to stop the container.
|
||||
@@ -656,16 +690,18 @@ incompatible with any restart policy other than `none`.
|
||||
|
||||
**--shm-size**=""
|
||||
Size of `/dev/shm`. The format is `<number><unit>`.
|
||||
`number` must be greater than `0`. Unit is optional and can be `b` (bytes), `k` (kilobytes), `m`(megabytes), or `g` (gigabytes).
|
||||
If you omit the unit, the system uses bytes. If you omit the size entirely, the system uses `64m`.
|
||||
`number` must be greater than `0`. Unit is optional and can be `b` (bytes),
|
||||
`k` (kilobytes), `m`(megabytes), or `g` (gigabytes). If you omit the unit,
|
||||
the system uses bytes. If you omit the size entirely, the system uses `64m`.
|
||||
|
||||
**--sysctl**=SYSCTL
|
||||
Configure namespaced kernel parameters at runtime
|
||||
|
||||
IPC Namespace - current sysctls allowed:
|
||||
|
||||
kernel.msgmax, kernel.msgmnb, kernel.msgmni, kernel.sem, kernel.shmall, kernel.shmmax, kernel.shmmni, kernel.shm_rmid_forced
|
||||
Sysctls beginning with fs.mqueue.*
|
||||
kernel.msgmax, kernel.msgmnb, kernel.msgmni, kernel.sem, kernel.shmall,
|
||||
kernel.shmmax, kernel.shmmni, kernel.shm_rmid_forced, and sysctls beginning
|
||||
with fs.mqueue.*
|
||||
|
||||
If you use the `--ipc=host` option these sysctls will not be allowed.
|
||||
|
||||
@@ -675,7 +711,8 @@ incompatible with any restart policy other than `none`.
|
||||
If you use the `--network=host` option these sysctls will not be allowed.
|
||||
|
||||
**--sig-proxy**=*true*|*false*
|
||||
Proxy received signals to the process (non-TTY mode only). SIGCHLD, SIGSTOP, and SIGKILL are not proxied. The default is *true*.
|
||||
Proxy received signals to the process (non-TTY mode only). SIGCHLD, SIGSTOP,
|
||||
and SIGKILL are not proxied. The default is *true*.
|
||||
|
||||
**--memory-swappiness**=""
|
||||
Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100.
|
||||
@@ -928,7 +965,8 @@ Host shows a shared memory segment with 7 pids attached, happens to be from http
|
||||
0x01128e25 0 root 600 1000 7
|
||||
```
|
||||
|
||||
Now run a regular container, and it correctly does NOT see the shared memory segment from the host:
|
||||
Now run a regular container, and it correctly does NOT see the shared memory
|
||||
segment from the host:
|
||||
|
||||
```
|
||||
$ docker run -it shm ipcs -m
|
||||
@@ -937,7 +975,8 @@ Now run a regular container, and it correctly does NOT see the shared memory seg
|
||||
key shmid owner perms bytes nattch status
|
||||
```
|
||||
|
||||
Run a container with the new `--ipc=host` option, and it now sees the shared memory segment from the host httpd:
|
||||
Run a container with the new `--ipc=host` option, and it now sees the shared
|
||||
memory segment from the host httpd:
|
||||
|
||||
```
|
||||
$ docker run -it --ipc=host shm ipcs -m
|
||||
@@ -958,7 +997,8 @@ Start a container with a program to create a shared memory segment:
|
||||
key shmid owner perms bytes nattch status
|
||||
0x0000162e 0 root 666 27 1
|
||||
```
|
||||
Create a 2nd container correctly shows no shared memory segment from 1st container:
|
||||
Create a second container correctly shows no shared memory segment from first
|
||||
container:
|
||||
```
|
||||
$ docker run shm ipcs -m
|
||||
|
||||
@@ -966,7 +1006,8 @@ Create a 2nd container correctly shows no shared memory segment from 1st contain
|
||||
key shmid owner perms bytes nattch status
|
||||
```
|
||||
|
||||
Create a 3rd container using the new --ipc=container:CONTAINERID option, now it shows the shared memory segment from the first:
|
||||
Create a 3rd container using the new --ipc=container:CONTAINERID option,
|
||||
now it shows the shared memory segment from the first:
|
||||
|
||||
```
|
||||
$ docker run -it --ipc=container:ed735b2264ac shm ipcs -m
|
||||
@@ -1129,18 +1170,22 @@ $ docker run -d --isolation default busybox top
|
||||
|
||||
On Microsoft Windows, can take any of these values:
|
||||
|
||||
* `default`: Use the value specified by the Docker daemon's `--exec-opt` . If the `daemon` does not specify an isolation technology, Microsoft Windows uses `process` as its default value.
|
||||
* `default`: Use the value specified by the Docker daemon's `--exec-opt` . If the
|
||||
`daemon` does not specify an isolation technology, Microsoft Windows uses
|
||||
`process` as its default value.
|
||||
* `process`: Namespace isolation only.
|
||||
* `hyperv`: Hyper-V hypervisor partition-based isolation.
|
||||
|
||||
In practice, when running on Microsoft Windows without a `daemon` option set, these two commands are equivalent:
|
||||
In practice, when running on Microsoft Windows without a `daemon` option set,
|
||||
these two commands are equivalent:
|
||||
|
||||
```
|
||||
$ docker run -d --isolation default busybox top
|
||||
$ docker run -d --isolation process busybox top
|
||||
```
|
||||
|
||||
If you have set the `--exec-opt isolation=hyperv` option on the Docker `daemon`, any of these commands also result in `hyperv` isolation:
|
||||
If you have set the `--exec-opt isolation=hyperv` option on the Docker `daemon`,
|
||||
any of these commands also result in `hyperv` isolation:
|
||||
|
||||
```
|
||||
$ docker run -d --isolation default busybox top
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.24
|
||||
|
||||
package swarmopts
|
||||
|
||||
import (
|
||||
|
||||
+41
-1
@@ -6,6 +6,9 @@ package templates
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
@@ -15,7 +18,7 @@ import (
|
||||
var basicFunctions = template.FuncMap{
|
||||
"json": formatJSON,
|
||||
"split": strings.Split,
|
||||
"join": strings.Join,
|
||||
"join": joinElements,
|
||||
"title": strings.Title, //nolint:nolintlint,staticcheck // strings.Title is deprecated, but we only use it for ASCII, so replacing with golang.org/x/text is out of scope
|
||||
"lower": strings.ToLower,
|
||||
"upper": strings.ToUpper,
|
||||
@@ -97,3 +100,40 @@ func formatJSON(v any) string {
|
||||
// Remove the trailing new line added by the encoder
|
||||
return strings.TrimSpace(buf.String())
|
||||
}
|
||||
|
||||
// joinElements joins a slice of items with the given separator. It uses
|
||||
// [strings.Join] if it's a slice of strings, otherwise uses [fmt.Sprint]
|
||||
// to join each item to the output.
|
||||
func joinElements(elems any, sep string) (string, error) {
|
||||
if elems == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if ss, ok := elems.([]string); ok {
|
||||
return strings.Join(ss, sep), nil
|
||||
}
|
||||
|
||||
switch rv := reflect.ValueOf(elems); rv.Kind() { //nolint:exhaustive // ignore: too many options to make exhaustive
|
||||
case reflect.Array, reflect.Slice:
|
||||
var b strings.Builder
|
||||
for i := range rv.Len() {
|
||||
if i > 0 {
|
||||
b.WriteString(sep)
|
||||
}
|
||||
_, _ = fmt.Fprint(&b, rv.Index(i).Interface())
|
||||
}
|
||||
return b.String(), nil
|
||||
|
||||
case reflect.Map:
|
||||
var out []string
|
||||
for _, k := range rv.MapKeys() {
|
||||
out = append(out, fmt.Sprint(rv.MapIndex(k).Interface()))
|
||||
}
|
||||
// Not ideal, but trying to keep a consistent order
|
||||
sort.Strings(out)
|
||||
return strings.Join(out, sep), nil
|
||||
|
||||
default:
|
||||
return "", fmt.Errorf("expected slice, got %T", elems)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package templates
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"text/template"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
@@ -139,3 +140,92 @@ func TestHeaderFunctions(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type stringerString string
|
||||
|
||||
func (s stringerString) String() string {
|
||||
return "stringer" + string(s)
|
||||
}
|
||||
|
||||
type stringerAndError string
|
||||
|
||||
func (s stringerAndError) String() string {
|
||||
return "stringer" + string(s)
|
||||
}
|
||||
|
||||
func (s stringerAndError) Error() string {
|
||||
return "error" + string(s)
|
||||
}
|
||||
|
||||
func TestJoinElements(t *testing.T) {
|
||||
tests := []struct {
|
||||
doc string
|
||||
data any
|
||||
expOut string
|
||||
expErr string
|
||||
}{
|
||||
{
|
||||
doc: "nil",
|
||||
data: nil,
|
||||
expOut: `output: ""`,
|
||||
},
|
||||
{
|
||||
doc: "non-slice",
|
||||
data: "hello",
|
||||
expOut: `output: "`,
|
||||
expErr: `error calling join: expected slice, got string`,
|
||||
},
|
||||
{
|
||||
doc: "structs",
|
||||
data: []struct{ A, B string }{{"1", "2"}, {"3", "4"}},
|
||||
expOut: `output: "{1 2}, {3 4}"`,
|
||||
},
|
||||
{
|
||||
doc: "map with strings",
|
||||
data: map[string]string{"A": "1", "B": "2", "C": "3"},
|
||||
expOut: `output: "1, 2, 3"`,
|
||||
},
|
||||
{
|
||||
doc: "map with stringers",
|
||||
data: map[string]stringerString{"A": "1", "B": "2", "C": "3"},
|
||||
expOut: `output: "stringer1, stringer2, stringer3"`,
|
||||
},
|
||||
{
|
||||
doc: "map with errors",
|
||||
data: []stringerAndError{"1", "2", "3"},
|
||||
expOut: `output: "error1, error2, error3"`,
|
||||
},
|
||||
{
|
||||
doc: "stringers",
|
||||
data: []stringerString{"1", "2", "3"},
|
||||
expOut: `output: "stringer1, stringer2, stringer3"`,
|
||||
},
|
||||
{
|
||||
doc: "stringer with errors",
|
||||
data: []stringerAndError{"1", "2", "3"},
|
||||
expOut: `output: "error1, error2, error3"`,
|
||||
},
|
||||
{
|
||||
doc: "slice of bools",
|
||||
data: []bool{true, false, true},
|
||||
expOut: `output: "true, false, true"`,
|
||||
},
|
||||
}
|
||||
|
||||
const formatStr = `output: "{{- join . ", " -}}"`
|
||||
tmpl, err := New("my-template").Funcs(template.FuncMap{"join": joinElements}).Parse(formatStr)
|
||||
assert.NilError(t, err)
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
var b bytes.Buffer
|
||||
err := tmpl.Execute(&b, tc.data)
|
||||
if tc.expErr != "" {
|
||||
assert.ErrorContains(t, err, tc.expErr)
|
||||
} else {
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
assert.Equal(t, b.String(), tc.expOut)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+13
-13
@@ -16,20 +16,20 @@ require (
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/docker/cli-docs-tool v0.11.0
|
||||
github.com/docker/distribution v2.8.3+incompatible
|
||||
github.com/docker/docker-credential-helpers v0.9.4
|
||||
github.com/docker/docker-credential-helpers v0.9.5
|
||||
github.com/docker/go-connections v0.6.0
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/fvbommel/sortorder v1.1.0
|
||||
github.com/go-jose/go-jose/v4 v4.1.3
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/mattn/go-runewidth v0.0.19
|
||||
github.com/moby/go-archive v0.1.0
|
||||
github.com/moby/moby/api v1.52.0
|
||||
github.com/moby/moby/client v0.2.1
|
||||
github.com/moby/go-archive v0.2.0
|
||||
github.com/moby/moby/api v1.53.0
|
||||
github.com/moby/moby/client v0.2.2
|
||||
github.com/moby/patternmatcher v0.6.0
|
||||
github.com/moby/swarmkit/v2 v2.1.1
|
||||
github.com/moby/sys/atomicwriter v0.1.0
|
||||
@@ -42,7 +42,7 @@ require (
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/sirupsen/logrus v1.9.4
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346
|
||||
@@ -56,12 +56,12 @@ require (
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0
|
||||
go.opentelemetry.io/otel/trace v1.38.0
|
||||
go.yaml.in/yaml/v3 v3.0.4
|
||||
golang.org/x/sync v0.18.0
|
||||
golang.org/x/sys v0.38.0
|
||||
golang.org/x/term v0.37.0
|
||||
golang.org/x/text v0.31.0
|
||||
golang.org/x/sync v0.19.0
|
||||
golang.org/x/sys v0.40.0
|
||||
golang.org/x/term v0.39.0
|
||||
golang.org/x/text v0.33.0
|
||||
gotest.tools/v3 v3.5.2
|
||||
tags.cncf.io/container-device-interface v1.0.1
|
||||
tags.cncf.io/container-device-interface v1.1.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -82,7 +82,7 @@ require (
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/klauspost/compress v1.18.3 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
@@ -98,7 +98,7 @@ require (
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/net v0.49.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
|
||||
+26
-29
@@ -40,8 +40,8 @@ github.com/docker/cli-docs-tool v0.11.0 h1:7d8QARFb7QEobizqxmEM7fOteZEHwH/zWgHQt
|
||||
github.com/docker/cli-docs-tool v0.11.0/go.mod h1:ma8BKiisUo8D6W05XEYIh3oa1UbgrZhi1nowyKFJa8Q=
|
||||
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-credential-helpers v0.9.4 h1:76ItO69/AP/V4yT9V4uuuItG0B1N8hvt0T0c0NN/DzI=
|
||||
github.com/docker/docker-credential-helpers v0.9.4/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
|
||||
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/go-events v0.0.0-20250808211157-605354379745 h1:yOn6Ze6IbYI/KAw2lw/83ELYvZh6hvsygTVkD0dzMC4=
|
||||
@@ -67,8 +67,8 @@ github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
@@ -96,8 +96,8 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
|
||||
github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
@@ -111,12 +111,12 @@ github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhg
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
|
||||
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
|
||||
github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg=
|
||||
github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
|
||||
github.com/moby/moby/client v0.2.1 h1:1Grh1552mvv6i+sYOdY+xKKVTvzJegcVMhuXocyDz/k=
|
||||
github.com/moby/moby/client v0.2.1/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE=
|
||||
github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
|
||||
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
|
||||
github.com/moby/moby/api v1.53.0 h1:PihqG1ncw4W+8mZs69jlwGXdaYBeb5brF6BL7mPIS/w=
|
||||
github.com/moby/moby/api v1.53.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
|
||||
github.com/moby/moby/client v0.2.2 h1:Pt4hRMCAIlyjL3cr8M5TrXCwKzguebPAc2do2ur7dEM=
|
||||
github.com/moby/moby/client v0.2.2/go.mod h1:2EkIPVNCqR05CMIzL1mfA07t0HvVUUOl85pasRz/GmQ=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/swarmkit/v2 v2.1.1 h1:yvTJ8MMCc3f0qTA44J6R59EZ5yZawdYopkpuLk4+ICU=
|
||||
@@ -178,8 +178,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
@@ -189,7 +189,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346 h1:TvtdmeYsYEij78hS4oxnwikoiLdIrgav3BA+CbhaDAI=
|
||||
@@ -243,15 +242,15 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -259,16 +258,15 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -294,12 +292,11 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
|
||||
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||
tags.cncf.io/container-device-interface v1.0.1 h1:KqQDr4vIlxwfYh0Ed/uJGVgX+CHAkahrgabg6Q8GYxc=
|
||||
tags.cncf.io/container-device-interface v1.0.1/go.mod h1:JojJIOeW3hNbcnOH2q0NrWNha/JuHoDZcmYxAZwb2i0=
|
||||
tags.cncf.io/container-device-interface v1.1.0 h1:RnxNhxF1JOu6CJUVpetTYvrXHdxw9j9jFYgZpI+anSY=
|
||||
tags.cncf.io/container-device-interface v1.1.0/go.mod h1:76Oj0Yqp9FwTx/pySDc8Bxjpg+VqXfDb50cKAXVJ34Q=
|
||||
|
||||
+7
-4
@@ -1,4 +1,7 @@
|
||||
if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then
|
||||
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4="
|
||||
fi
|
||||
use flake . --impure
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export DIRENV_WARN_TIMEOUT=20s
|
||||
|
||||
eval "$(devenv direnvrc)"
|
||||
|
||||
use devenv
|
||||
|
||||
+7
-3
@@ -1,6 +1,10 @@
|
||||
/.devenv/
|
||||
/.direnv/
|
||||
/.pre-commit-config.yaml
|
||||
/bin/
|
||||
/build/
|
||||
/var/
|
||||
|
||||
# Devenv
|
||||
.devenv*
|
||||
devenv.local.nix
|
||||
devenv.local.yaml
|
||||
.direnv
|
||||
.pre-commit-config.yaml
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"nodes": {
|
||||
"devenv": {
|
||||
"locked": {
|
||||
"dir": "src/modules",
|
||||
"lastModified": 1765288076,
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "93c055af1e8fcac49251f1b2e1c57f78620ad351",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"dir": "src/modules",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1765121682,
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "65f23138d8d09a92e30f1e5c87611b23ef451bf3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"git-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1765016596,
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "548fc44fca28a5e81c5d6b846e555e6b9c2a5a3c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"git-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1762808025,
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "cb5e3fdca1de58ccbc3ef53de65bd372b48f567c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1764580874,
|
||||
"owner": "cachix",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"rev": "dcf61356c3ab25f1362b4a4428a6d871e84f1d1d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "rolling",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"git-hooks": "git-hooks",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": [
|
||||
"git-hooks"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
languages = {
|
||||
go.enable = true;
|
||||
};
|
||||
|
||||
packages = with pkgs; [
|
||||
golangci-lint
|
||||
];
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json
|
||||
inputs:
|
||||
nixpkgs:
|
||||
url: github:cachix/devenv-nixpkgs/rolling
|
||||
-294
@@ -1,294 +0,0 @@
|
||||
{
|
||||
"nodes": {
|
||||
"cachix": {
|
||||
"inputs": {
|
||||
"devenv": [
|
||||
"devenv"
|
||||
],
|
||||
"flake-compat": [
|
||||
"devenv"
|
||||
],
|
||||
"git-hooks": [
|
||||
"devenv"
|
||||
],
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1742042642,
|
||||
"narHash": "sha256-D0gP8srrX0qj+wNYNPdtVJsQuFzIng3q43thnHXQ/es=",
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"rev": "a624d3eaf4b1d225f918de8543ed739f2f574203",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "latest",
|
||||
"repo": "cachix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"devenv": {
|
||||
"inputs": {
|
||||
"cachix": "cachix",
|
||||
"flake-compat": "flake-compat",
|
||||
"git-hooks": "git-hooks",
|
||||
"nix": "nix",
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1744876578,
|
||||
"narHash": "sha256-8MTBj2REB8t29sIBLpxbR0+AEGJ7f+RkzZPAGsFd40c=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "7ff7c351bba20d0615be25ecdcbcf79b57b85fe1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1733328505,
|
||||
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"devenv",
|
||||
"nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1712014858,
|
||||
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts_2": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1743550720,
|
||||
"narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "c621e8422220273271f52058f618c94e405bb0f5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"git-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv"
|
||||
],
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1742649964,
|
||||
"narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"git-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"libgit2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1697646580,
|
||||
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
|
||||
"owner": "libgit2",
|
||||
"repo": "libgit2",
|
||||
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "libgit2",
|
||||
"repo": "libgit2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv"
|
||||
],
|
||||
"flake-parts": "flake-parts",
|
||||
"libgit2": "libgit2",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nixpkgs-23-11": [
|
||||
"devenv"
|
||||
],
|
||||
"nixpkgs-regression": [
|
||||
"devenv"
|
||||
],
|
||||
"pre-commit-hooks": [
|
||||
"devenv"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1741798497,
|
||||
"narHash": "sha256-E3j+3MoY8Y96mG1dUIiLFm2tZmNbRvSiyN7CrSKuAVg=",
|
||||
"owner": "domenkozar",
|
||||
"repo": "nix",
|
||||
"rev": "f3f44b2baaf6c4c6e179de8cbb1cc6db031083cd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "domenkozar",
|
||||
"ref": "devenv-2.24",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1733212471,
|
||||
"narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "55d15ad12a74eb7d4646254e13638ad0c4128776",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1743296961,
|
||||
"narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1717432640,
|
||||
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "release-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1733477122,
|
||||
"narHash": "sha256-qamMCz5mNpQmgBwc8SB5tVMlD5sbwVIToVZtSxMph9s=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "rolling",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1744536153,
|
||||
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"flake-parts": "flake-parts_2",
|
||||
"nixpkgs": "nixpkgs_4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
devenv.url = "github:cachix/devenv";
|
||||
};
|
||||
|
||||
outputs =
|
||||
inputs@{ flake-parts, ... }:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
imports = [
|
||||
inputs.devenv.flakeModule
|
||||
];
|
||||
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
|
||||
perSystem =
|
||||
{ pkgs, ... }:
|
||||
rec {
|
||||
devenv.shells = {
|
||||
default = {
|
||||
languages = {
|
||||
go.enable = true;
|
||||
};
|
||||
|
||||
pre-commit.hooks = {
|
||||
nixpkgs-fmt.enable = true;
|
||||
};
|
||||
|
||||
packages = with pkgs; [
|
||||
golangci-lint
|
||||
];
|
||||
|
||||
# https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
|
||||
containers = pkgs.lib.mkForce { };
|
||||
};
|
||||
|
||||
ci = devenv.shells.default;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
+284
-48
@@ -173,6 +173,25 @@
|
||||
// Public: "I made it through!"
|
||||
// }
|
||||
//
|
||||
// # Custom Decoding with Unmarshaler
|
||||
//
|
||||
// Types can implement the Unmarshaler interface to control their own decoding. The interface
|
||||
// behaves similarly to how UnmarshalJSON does in the standard library. It can be used as an
|
||||
// alternative or companion to a DecodeHook.
|
||||
//
|
||||
// type TrimmedString string
|
||||
//
|
||||
// func (t *TrimmedString) UnmarshalMapstructure(input any) error {
|
||||
// str, ok := input.(string)
|
||||
// if !ok {
|
||||
// return fmt.Errorf("expected string, got %T", input)
|
||||
// }
|
||||
// *t = TrimmedString(strings.TrimSpace(str))
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// See the Unmarshaler interface documentation for more details.
|
||||
//
|
||||
// # Other Configuration
|
||||
//
|
||||
// mapstructure is highly configurable. See the DecoderConfig struct
|
||||
@@ -218,6 +237,17 @@ type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, any) (any, error)
|
||||
// values.
|
||||
type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (any, error)
|
||||
|
||||
// Unmarshaler is the interface implemented by types that can unmarshal
|
||||
// themselves. UnmarshalMapstructure receives the input data (potentially
|
||||
// transformed by DecodeHook) and should populate the receiver with the
|
||||
// decoded values.
|
||||
//
|
||||
// The Unmarshaler interface takes precedence over the default decoding
|
||||
// logic for any type (structs, slices, maps, primitives, etc.).
|
||||
type Unmarshaler interface {
|
||||
UnmarshalMapstructure(any) error
|
||||
}
|
||||
|
||||
// DecoderConfig is the configuration that is used to create a new decoder
|
||||
// and allows customization of various aspects of decoding.
|
||||
type DecoderConfig struct {
|
||||
@@ -281,6 +311,13 @@ type DecoderConfig struct {
|
||||
// }
|
||||
Squash bool
|
||||
|
||||
// Deep will map structures in slices instead of copying them
|
||||
//
|
||||
// type Parent struct {
|
||||
// Children []Child `mapstructure:",deep"`
|
||||
// }
|
||||
Deep bool
|
||||
|
||||
// Metadata is the struct that will contain extra metadata about
|
||||
// the decoding. If this is nil, then no metadata will be tracked.
|
||||
Metadata *Metadata
|
||||
@@ -290,9 +327,15 @@ type DecoderConfig struct {
|
||||
Result any
|
||||
|
||||
// The tag name that mapstructure reads for field names. This
|
||||
// defaults to "mapstructure"
|
||||
// defaults to "mapstructure". Multiple tag names can be specified
|
||||
// as a comma-separated list (e.g., "yaml,json"), and the first
|
||||
// matching non-empty tag will be used.
|
||||
TagName string
|
||||
|
||||
// RootName specifies the name to use for the root element in error messages. For example:
|
||||
// '<rootName>' has unset fields: <fieldName>
|
||||
RootName string
|
||||
|
||||
// The option of the value in the tag that indicates a field should
|
||||
// be squashed. This defaults to "squash".
|
||||
SquashTagOption string
|
||||
@@ -304,11 +347,34 @@ type DecoderConfig struct {
|
||||
// MatchName is the function used to match the map key to the struct
|
||||
// field name or tag. Defaults to `strings.EqualFold`. This can be used
|
||||
// to implement case-sensitive tag values, support snake casing, etc.
|
||||
//
|
||||
// MatchName is used as a fallback comparison when the direct key lookup fails.
|
||||
// See also MapFieldName for transforming field names before lookup.
|
||||
MatchName func(mapKey, fieldName string) bool
|
||||
|
||||
// DecodeNil, if set to true, will cause the DecodeHook (if present) to run
|
||||
// even if the input is nil. This can be used to provide default values.
|
||||
DecodeNil bool
|
||||
|
||||
// MapFieldName is the function used to convert the struct field name to the map's key name.
|
||||
//
|
||||
// This is useful for automatically converting between naming conventions without
|
||||
// explicitly tagging each field. For example, to convert Go's PascalCase field names
|
||||
// to snake_case map keys:
|
||||
//
|
||||
// MapFieldName: func(s string) string {
|
||||
// return strcase.ToSnake(s)
|
||||
// }
|
||||
//
|
||||
// When decoding from a map to a struct, the transformed field name is used for
|
||||
// the initial lookup. If not found, MatchName is used as a fallback comparison.
|
||||
// Explicit struct tags always take precedence over MapFieldName.
|
||||
MapFieldName func(string) string
|
||||
|
||||
// DisableUnmarshaler, if set to true, disables the use of the Unmarshaler
|
||||
// interface. Types implementing Unmarshaler will be decoded using the
|
||||
// standard struct decoding logic instead.
|
||||
DisableUnmarshaler bool
|
||||
}
|
||||
|
||||
// A Decoder takes a raw interface value and turns it into structured
|
||||
@@ -445,6 +511,12 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
|
||||
config.MatchName = strings.EqualFold
|
||||
}
|
||||
|
||||
if config.MapFieldName == nil {
|
||||
config.MapFieldName = func(s string) string {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
result := &Decoder{
|
||||
config: config,
|
||||
}
|
||||
@@ -458,7 +530,7 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
|
||||
// Decode decodes the given raw interface to the target pointer specified
|
||||
// by the configuration.
|
||||
func (d *Decoder) Decode(input any) error {
|
||||
err := d.decode("", input, reflect.ValueOf(d.config.Result).Elem())
|
||||
err := d.decode(d.config.RootName, input, reflect.ValueOf(d.config.Result).Elem())
|
||||
|
||||
// Retain some of the original behavior when multiple errors ocurr
|
||||
var joinedErr interface{ Unwrap() []error }
|
||||
@@ -540,36 +612,50 @@ func (d *Decoder) decode(name string, input any, outVal reflect.Value) error {
|
||||
|
||||
var err error
|
||||
addMetaKey := true
|
||||
switch outputKind {
|
||||
case reflect.Bool:
|
||||
err = d.decodeBool(name, input, outVal)
|
||||
case reflect.Interface:
|
||||
err = d.decodeBasic(name, input, outVal)
|
||||
case reflect.String:
|
||||
err = d.decodeString(name, input, outVal)
|
||||
case reflect.Int:
|
||||
err = d.decodeInt(name, input, outVal)
|
||||
case reflect.Uint:
|
||||
err = d.decodeUint(name, input, outVal)
|
||||
case reflect.Float32:
|
||||
err = d.decodeFloat(name, input, outVal)
|
||||
case reflect.Complex64:
|
||||
err = d.decodeComplex(name, input, outVal)
|
||||
case reflect.Struct:
|
||||
err = d.decodeStruct(name, input, outVal)
|
||||
case reflect.Map:
|
||||
err = d.decodeMap(name, input, outVal)
|
||||
case reflect.Ptr:
|
||||
addMetaKey, err = d.decodePtr(name, input, outVal)
|
||||
case reflect.Slice:
|
||||
err = d.decodeSlice(name, input, outVal)
|
||||
case reflect.Array:
|
||||
err = d.decodeArray(name, input, outVal)
|
||||
case reflect.Func:
|
||||
err = d.decodeFunc(name, input, outVal)
|
||||
default:
|
||||
// If we reached this point then we weren't able to decode it
|
||||
return newDecodeError(name, fmt.Errorf("unsupported type: %s", outputKind))
|
||||
|
||||
// Check if the target implements Unmarshaler and use it if not disabled
|
||||
unmarshaled := false
|
||||
if !d.config.DisableUnmarshaler {
|
||||
if unmarshaler, ok := getUnmarshaler(outVal); ok {
|
||||
if err = unmarshaler.UnmarshalMapstructure(input); err != nil {
|
||||
err = newDecodeError(name, err)
|
||||
}
|
||||
unmarshaled = true
|
||||
}
|
||||
}
|
||||
|
||||
if !unmarshaled {
|
||||
switch outputKind {
|
||||
case reflect.Bool:
|
||||
err = d.decodeBool(name, input, outVal)
|
||||
case reflect.Interface:
|
||||
err = d.decodeBasic(name, input, outVal)
|
||||
case reflect.String:
|
||||
err = d.decodeString(name, input, outVal)
|
||||
case reflect.Int:
|
||||
err = d.decodeInt(name, input, outVal)
|
||||
case reflect.Uint:
|
||||
err = d.decodeUint(name, input, outVal)
|
||||
case reflect.Float32:
|
||||
err = d.decodeFloat(name, input, outVal)
|
||||
case reflect.Complex64:
|
||||
err = d.decodeComplex(name, input, outVal)
|
||||
case reflect.Struct:
|
||||
err = d.decodeStruct(name, input, outVal)
|
||||
case reflect.Map:
|
||||
err = d.decodeMap(name, input, outVal)
|
||||
case reflect.Ptr:
|
||||
addMetaKey, err = d.decodePtr(name, input, outVal)
|
||||
case reflect.Slice:
|
||||
err = d.decodeSlice(name, input, outVal)
|
||||
case reflect.Array:
|
||||
err = d.decodeArray(name, input, outVal)
|
||||
case reflect.Func:
|
||||
err = d.decodeFunc(name, input, outVal)
|
||||
default:
|
||||
// If we reached this point then we weren't able to decode it
|
||||
return newDecodeError(name, fmt.Errorf("unsupported type: %s", outputKind))
|
||||
}
|
||||
}
|
||||
|
||||
// If we reached here, then we successfully decoded SOMETHING, so
|
||||
@@ -668,7 +754,7 @@ func (d *Decoder) decodeString(name string, data any, val reflect.Value) error {
|
||||
case reflect.Uint8:
|
||||
var uints []uint8
|
||||
if dataKind == reflect.Array {
|
||||
uints = make([]uint8, dataVal.Len(), dataVal.Len())
|
||||
uints = make([]uint8, dataVal.Len())
|
||||
for i := range uints {
|
||||
uints[i] = dataVal.Index(i).Interface().(uint8)
|
||||
}
|
||||
@@ -1060,8 +1146,8 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
||||
)
|
||||
}
|
||||
|
||||
tagValue := f.Tag.Get(d.config.TagName)
|
||||
keyName := f.Name
|
||||
tagValue, _ := getTagValue(f, d.config.TagName)
|
||||
keyName := d.config.MapFieldName(f.Name)
|
||||
|
||||
if tagValue == "" && d.config.IgnoreUntaggedFields {
|
||||
continue
|
||||
@@ -1070,6 +1156,9 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
||||
// If Squash is set in the config, we squash the field down.
|
||||
squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous
|
||||
|
||||
// If Deep is set in the config, set as default value.
|
||||
deep := d.config.Deep
|
||||
|
||||
v = dereferencePtrToStructIfNeeded(v, d.config.TagName)
|
||||
|
||||
// Determine the name of the key in the map
|
||||
@@ -1078,12 +1167,12 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
||||
continue
|
||||
}
|
||||
// If "omitempty" is specified in the tag, it ignores empty values.
|
||||
if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) {
|
||||
if strings.Contains(tagValue[index+1:], "omitempty") && isEmptyValue(v) {
|
||||
continue
|
||||
}
|
||||
|
||||
// If "omitzero" is specified in the tag, it ignores zero values.
|
||||
if strings.Index(tagValue[index+1:], "omitzero") != -1 && v.IsZero() {
|
||||
if strings.Contains(tagValue[index+1:], "omitzero") && v.IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1103,7 +1192,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if strings.Index(tagValue[index+1:], "remain") != -1 {
|
||||
if strings.Contains(tagValue[index+1:], "remain") {
|
||||
if v.Kind() != reflect.Map {
|
||||
return newDecodeError(
|
||||
name+"."+f.Name,
|
||||
@@ -1118,6 +1207,9 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
deep = deep || strings.Contains(tagValue[index+1:], "deep")
|
||||
|
||||
if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" {
|
||||
keyName = keyNameTagValue
|
||||
}
|
||||
@@ -1164,6 +1256,41 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
||||
valMap.SetMapIndex(reflect.ValueOf(keyName), vMap)
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
if deep {
|
||||
var childType reflect.Type
|
||||
switch v.Type().Elem().Kind() {
|
||||
case reflect.Struct:
|
||||
childType = reflect.TypeOf(map[string]any{})
|
||||
default:
|
||||
childType = v.Type().Elem()
|
||||
}
|
||||
|
||||
sType := reflect.SliceOf(childType)
|
||||
|
||||
addrVal := reflect.New(sType)
|
||||
|
||||
vSlice := reflect.MakeSlice(sType, v.Len(), v.Cap())
|
||||
|
||||
if v.Len() > 0 {
|
||||
reflect.Indirect(addrVal).Set(vSlice)
|
||||
|
||||
err := d.decode(keyName, v.Interface(), reflect.Indirect(addrVal))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
vSlice = reflect.Indirect(addrVal)
|
||||
|
||||
valMap.SetMapIndex(reflect.ValueOf(keyName), vSlice)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// When deep mapping is not needed, fallthrough to normal copy
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
valMap.SetMapIndex(reflect.ValueOf(keyName), v)
|
||||
}
|
||||
@@ -1471,7 +1598,10 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
||||
remain := false
|
||||
|
||||
// We always parse the tags cause we're looking for other tags too
|
||||
tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",")
|
||||
tagParts := getTagParts(fieldType, d.config.TagName)
|
||||
if len(tagParts) == 0 {
|
||||
tagParts = []string{""}
|
||||
}
|
||||
for _, tag := range tagParts[1:] {
|
||||
if tag == d.config.SquashTagOption {
|
||||
squash = true
|
||||
@@ -1492,6 +1622,18 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
||||
if !fieldVal.IsNil() {
|
||||
structs = append(structs, fieldVal.Elem().Elem())
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if fieldVal.Type().Elem().Kind() == reflect.Struct {
|
||||
if fieldVal.IsNil() {
|
||||
fieldVal.Set(reflect.New(fieldVal.Type().Elem()))
|
||||
}
|
||||
structs = append(structs, fieldVal.Elem())
|
||||
} else {
|
||||
errs = append(errs, newDecodeError(
|
||||
name+"."+fieldType.Name,
|
||||
fmt.Errorf("unsupported type for squashed pointer: %s", fieldVal.Type().Elem().Kind()),
|
||||
))
|
||||
}
|
||||
default:
|
||||
errs = append(errs, newDecodeError(
|
||||
name+"."+fieldType.Name,
|
||||
@@ -1516,13 +1658,15 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
||||
field, fieldValue := f.field, f.val
|
||||
fieldName := field.Name
|
||||
|
||||
tagValue := field.Tag.Get(d.config.TagName)
|
||||
tagValue, _ := getTagValue(field, d.config.TagName)
|
||||
if tagValue == "" && d.config.IgnoreUntaggedFields {
|
||||
continue
|
||||
}
|
||||
tagValue = strings.SplitN(tagValue, ",", 2)[0]
|
||||
if tagValue != "" {
|
||||
fieldName = tagValue
|
||||
} else {
|
||||
fieldName = d.config.MapFieldName(fieldName)
|
||||
}
|
||||
|
||||
rawMapKey := reflect.ValueOf(fieldName)
|
||||
@@ -1605,8 +1749,14 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
// Improve error message when name is empty by showing the target struct type
|
||||
// in the case where it is empty for embedded structs.
|
||||
errorName := name
|
||||
if errorName == "" {
|
||||
errorName = val.Type().String()
|
||||
}
|
||||
errs = append(errs, newDecodeError(
|
||||
name,
|
||||
errorName,
|
||||
fmt.Errorf("has invalid keys: %s", strings.Join(keys, ", ")),
|
||||
))
|
||||
}
|
||||
@@ -1692,7 +1842,7 @@ func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool,
|
||||
if f.PkgPath == "" && !checkMapstructureTags { // check for unexported fields
|
||||
return true
|
||||
}
|
||||
if checkMapstructureTags && f.Tag.Get(tagName) != "" { // check for mapstructure tags inside
|
||||
if checkMapstructureTags && hasAnyTag(f, tagName) { // check for mapstructure tags inside
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1700,13 +1850,99 @@ func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool,
|
||||
}
|
||||
|
||||
func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Value {
|
||||
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
|
||||
if v.Kind() != reflect.Ptr {
|
||||
return v
|
||||
}
|
||||
deref := v.Elem()
|
||||
derefT := deref.Type()
|
||||
if isStructTypeConvertibleToMap(derefT, true, tagName) {
|
||||
return deref
|
||||
|
||||
switch v.Elem().Kind() {
|
||||
case reflect.Slice:
|
||||
return v.Elem()
|
||||
|
||||
case reflect.Struct:
|
||||
deref := v.Elem()
|
||||
derefT := deref.Type()
|
||||
if isStructTypeConvertibleToMap(derefT, true, tagName) {
|
||||
return deref
|
||||
}
|
||||
return v
|
||||
|
||||
default:
|
||||
return v
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func hasAnyTag(field reflect.StructField, tagName string) bool {
|
||||
_, ok := getTagValue(field, tagName)
|
||||
return ok
|
||||
}
|
||||
|
||||
func getTagParts(field reflect.StructField, tagName string) []string {
|
||||
tagValue, ok := getTagValue(field, tagName)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return strings.Split(tagValue, ",")
|
||||
}
|
||||
|
||||
func getTagValue(field reflect.StructField, tagName string) (string, bool) {
|
||||
for _, name := range splitTagNames(tagName) {
|
||||
if tag := field.Tag.Get(name); tag != "" {
|
||||
return tag, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func splitTagNames(tagName string) []string {
|
||||
if tagName == "" {
|
||||
return []string{"mapstructure"}
|
||||
}
|
||||
parts := strings.Split(tagName, ",")
|
||||
result := make([]string, 0, len(parts))
|
||||
|
||||
for _, name := range parts {
|
||||
name = strings.TrimSpace(name)
|
||||
if name != "" {
|
||||
result = append(result, name)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// unmarshalerType is cached for performance
|
||||
var unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
||||
|
||||
// getUnmarshaler checks if the value implements Unmarshaler and returns
|
||||
// the Unmarshaler and a boolean indicating if it was found. It handles both
|
||||
// pointer and value receivers.
|
||||
func getUnmarshaler(val reflect.Value) (Unmarshaler, bool) {
|
||||
// Skip invalid or nil values
|
||||
if !val.IsValid() {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
switch val.Kind() {
|
||||
case reflect.Pointer, reflect.Interface:
|
||||
if val.IsNil() {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// Check pointer receiver first (most common case)
|
||||
if val.CanAddr() {
|
||||
ptrVal := val.Addr()
|
||||
// Quick check: if no methods, can't implement any interface
|
||||
if ptrVal.Type().NumMethod() > 0 && ptrVal.Type().Implements(unmarshalerType) {
|
||||
return ptrVal.Interface().(Unmarshaler), true
|
||||
}
|
||||
}
|
||||
|
||||
// Check value receiver
|
||||
// Quick check: if no methods, can't implement any interface
|
||||
if val.Type().NumMethod() > 0 && val.CanInterface() && val.Type().Implements(unmarshalerType) {
|
||||
return val.Interface().(Unmarshaler), true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
-40
@@ -36,10 +36,6 @@ import (
|
||||
const ImpliedDirectoryMode = 0o755
|
||||
|
||||
type (
|
||||
// Compression is the state represents if compressed or not.
|
||||
//
|
||||
// Deprecated: use [compression.Compression].
|
||||
Compression = compression.Compression
|
||||
// WhiteoutFormat is the format of whiteouts unpacked
|
||||
WhiteoutFormat int
|
||||
|
||||
@@ -95,14 +91,6 @@ func NewDefaultArchiver() *Archiver {
|
||||
// in order for the test to pass.
|
||||
type breakoutError error
|
||||
|
||||
const (
|
||||
Uncompressed = compression.None // Deprecated: use [compression.None].
|
||||
Bzip2 = compression.Bzip2 // Deprecated: use [compression.Bzip2].
|
||||
Gzip = compression.Gzip // Deprecated: use [compression.Gzip].
|
||||
Xz = compression.Xz // Deprecated: use [compression.Xz].
|
||||
Zstd = compression.Zstd // Deprecated: use [compression.Zstd].
|
||||
)
|
||||
|
||||
const (
|
||||
AUFSWhiteoutFormat WhiteoutFormat = 0 // AUFSWhiteoutFormat is the default format for whiteouts
|
||||
OverlayWhiteoutFormat WhiteoutFormat = 1 // OverlayWhiteoutFormat formats whiteout according to the overlay standard.
|
||||
@@ -126,27 +114,6 @@ func IsArchivePath(path string) bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// DetectCompression detects the compression algorithm of the source.
|
||||
//
|
||||
// Deprecated: use [compression.Detect].
|
||||
func DetectCompression(source []byte) compression.Compression {
|
||||
return compression.Detect(source)
|
||||
}
|
||||
|
||||
// DecompressStream decompresses the archive and returns a ReaderCloser with the decompressed archive.
|
||||
//
|
||||
// Deprecated: use [compression.DecompressStream].
|
||||
func DecompressStream(archive io.Reader) (io.ReadCloser, error) {
|
||||
return compression.DecompressStream(archive)
|
||||
}
|
||||
|
||||
// CompressStream compresses the dest with specified compression algorithm.
|
||||
//
|
||||
// Deprecated: use [compression.CompressStream].
|
||||
func CompressStream(dest io.Writer, comp compression.Compression) (io.WriteCloser, error) {
|
||||
return compression.CompressStream(dest, comp)
|
||||
}
|
||||
|
||||
// TarModifierFunc is a function that can be passed to ReplaceFileTarWrapper to
|
||||
// modify the contents or header of an entry in the archive. If the file already
|
||||
// exists in the archive the TarModifierFunc will be called with the Header and
|
||||
@@ -235,13 +202,6 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
|
||||
return pipeReader
|
||||
}
|
||||
|
||||
// FileInfoHeaderNoLookups creates a partially-populated tar.Header from fi.
|
||||
//
|
||||
// Deprecated: use [tarheader.FileInfoHeaderNoLookups].
|
||||
func FileInfoHeaderNoLookups(fi os.FileInfo, link string) (*tar.Header, error) {
|
||||
return tarheader.FileInfoHeaderNoLookups(fi, link)
|
||||
}
|
||||
|
||||
// FileInfoHeader creates a populated Header from fi.
|
||||
//
|
||||
// Compared to the archive/tar package, this function fills in less information
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
//go:build !linux && !windows
|
||||
//go:build darwin || freebsd || netbsd
|
||||
|
||||
package archive
|
||||
|
||||
|
||||
+14
-3
@@ -1,7 +1,7 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
@@ -176,13 +176,24 @@
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2013-2018 Docker, Inc.
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+5
-1
@@ -71,11 +71,15 @@ func DecodeRequestBody(r io.ReadCloser) (*registry.AuthConfig, error) {
|
||||
|
||||
func decode(r io.Reader) (*registry.AuthConfig, error) {
|
||||
authConfig := ®istry.AuthConfig{}
|
||||
if err := json.NewDecoder(r).Decode(authConfig); err != nil {
|
||||
dec := json.NewDecoder(r)
|
||||
if err := dec.Decode(authConfig); err != nil {
|
||||
// always return an (empty) AuthConfig to increase compatibility with
|
||||
// the existing API.
|
||||
return ®istry.AuthConfig{}, invalid(fmt.Errorf("invalid JSON: %w", err))
|
||||
}
|
||||
if dec.More() {
|
||||
return ®istry.AuthConfig{}, invalid(errors.New("multiple JSON documents not allowed"))
|
||||
}
|
||||
return authConfig, nil
|
||||
}
|
||||
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// BuildIdentity contains build reference information if image was created via build.
|
||||
type BuildIdentity struct {
|
||||
// Ref is the identifier for the build request. This reference can be used to
|
||||
// look up the build details in BuildKit history API.
|
||||
Ref string `json:"Ref,omitempty"`
|
||||
|
||||
// CreatedAt is the time when the build ran.
|
||||
CreatedAt time.Time `json:"CreatedAt,omitempty"`
|
||||
}
|
||||
Generated
Vendored
+5
-6
@@ -2,13 +2,12 @@
|
||||
|
||||
package image
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Code generated by `swagger generate operation`.
|
||||
//
|
||||
// See hack/generate-swagger-api.sh
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
// HistoryResponseItem individual image layer information in response to ImageHistory operation
|
||||
// HistoryResponseItem HistoryResponseItem
|
||||
//
|
||||
// individual image layer information in response to ImageHistory operation
|
||||
//
|
||||
// swagger:model HistoryResponseItem
|
||||
type HistoryResponseItem struct {
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package image
|
||||
|
||||
// Identity holds information about the identity and origin of the image.
|
||||
// This is trusted information verified by the daemon and cannot be modified
|
||||
// by tagging an image to a different name.
|
||||
type Identity struct {
|
||||
// Signature contains the properties of verified signatures for the image.
|
||||
Signature []SignatureIdentity `json:"Signature,omitzero"`
|
||||
// Pull contains remote location information if image was created via pull.
|
||||
// If image was pulled via mirror, this contains the original repository location.
|
||||
// After successful push this images also contains the pushed repository location.
|
||||
Pull []PullIdentity `json:"Pull,omitzero"`
|
||||
// Build contains build reference information if image was created via build.
|
||||
Build []BuildIdentity `json:"Build,omitzero"`
|
||||
}
|
||||
+29
@@ -105,4 +105,33 @@ type InspectResponse struct {
|
||||
// WARNING: This is experimental and may change at any time without any backward
|
||||
// compatibility.
|
||||
Manifests []ManifestSummary `json:"Manifests,omitempty"`
|
||||
|
||||
// Identity holds information about the identity and origin of the image.
|
||||
// This is trusted information verified by the daemon and cannot be modified
|
||||
// by tagging an image to a different name.
|
||||
Identity *Identity `json:"Identity,omitempty"`
|
||||
}
|
||||
|
||||
// SignatureTimestampType is the type of timestamp used in the signature.
|
||||
type SignatureTimestampType string
|
||||
|
||||
const (
|
||||
SignatureTimestampTlog SignatureTimestampType = "Tlog"
|
||||
SignatureTimestampAuthority SignatureTimestampType = "TimestampAuthority"
|
||||
)
|
||||
|
||||
// SignatureType is the type of signature format.
|
||||
type SignatureType string
|
||||
|
||||
const (
|
||||
SignatureTypeBundleV03 SignatureType = "bundle-v0.3"
|
||||
SignatureTypeSimpleSigningV1 SignatureType = "simplesigning-v1"
|
||||
)
|
||||
|
||||
// KnownSignerIdentity is an identifier for a special signer identity that is known to the implementation.
|
||||
type KnownSignerIdentity string
|
||||
|
||||
const (
|
||||
// KnownSignerDHI is the known identity for Docker Hardened Images.
|
||||
KnownSignerDHI KnownSignerIdentity = "DHI"
|
||||
)
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package image
|
||||
|
||||
// PullIdentity contains remote location information if image was created via pull.
|
||||
// If image was pulled via mirror, this contains the original repository location.
|
||||
type PullIdentity struct {
|
||||
// Repository is the remote repository location the image was pulled from.
|
||||
Repository string `json:"Repository,omitempty"`
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package image
|
||||
|
||||
// SignatureIdentity contains the properties of verified signatures for the image.
|
||||
type SignatureIdentity struct {
|
||||
// Name is a textual description summarizing the type of signature.
|
||||
Name string `json:"Name,omitempty"`
|
||||
// Timestamps contains a list of verified signed timestamps for the signature.
|
||||
Timestamps []SignatureTimestamp `json:"Timestamps,omitzero"`
|
||||
// KnownSigner is an identifier for a special signer identity that is known to the implementation.
|
||||
KnownSigner KnownSignerIdentity `json:"KnownSigner,omitempty"`
|
||||
// DockerReference is the Docker image reference associated with the signature.
|
||||
// This is an optional field only present in older hashedrecord signatures.
|
||||
DockerReference string `json:"DockerReference,omitempty"`
|
||||
// Signer contains information about the signer certificate used to sign the image.
|
||||
Signer *SignerIdentity `json:"Signer,omitempty"`
|
||||
// SignatureType is the type of signature format. E.g. "bundle-v0.3" or "hashedrecord".
|
||||
SignatureType SignatureType `json:"SignatureType,omitempty"`
|
||||
|
||||
// Error contains error information if signature verification failed.
|
||||
// Other fields will be empty in this case.
|
||||
Error string `json:"Error,omitempty"`
|
||||
// Warnings contains any warnings that occurred during signature verification.
|
||||
// For example, if there was no internet connectivity and cached trust roots were used.
|
||||
// Warning does not indicate a failed verification but may point to configuration issues.
|
||||
Warnings []string `json:"Warnings,omitzero"`
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// SignatureTimestamp contains information about a verified signed timestamp for an image signature.
|
||||
type SignatureTimestamp struct {
|
||||
Type SignatureTimestampType `json:"Type"`
|
||||
URI string `json:"URI"`
|
||||
Timestamp time.Time `json:"Timestamp"`
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package image
|
||||
|
||||
// SignerIdentity contains information about the signer certificate used to sign the image.
|
||||
// This is [certificate.Summary] with deprecated fields removed and keys in Moby uppercase style.
|
||||
//
|
||||
// [certificate.Summary]: https://pkg.go.dev/github.com/sigstore/sigstore-go/pkg/fulcio/certificate#Summary
|
||||
type SignerIdentity struct {
|
||||
CertificateIssuer string `json:"CertificateIssuer"`
|
||||
SubjectAlternativeName string `json:"SubjectAlternativeName"`
|
||||
// The OIDC issuer. Should match `iss` claim of ID token or, in the case of
|
||||
// a federated login like Dex it should match the issuer URL of the
|
||||
// upstream issuer. The issuer is not set the extensions are invalid and
|
||||
// will fail to render.
|
||||
Issuer string `json:"Issuer,omitempty"` // OID 1.3.6.1.4.1.57264.1.8 and 1.3.6.1.4.1.57264.1.1 (Deprecated)
|
||||
|
||||
// Reference to specific build instructions that are responsible for signing.
|
||||
BuildSignerURI string `json:"BuildSignerURI,omitempty"` // 1.3.6.1.4.1.57264.1.9
|
||||
|
||||
// Immutable reference to the specific version of the build instructions that is responsible for signing.
|
||||
BuildSignerDigest string `json:"BuildSignerDigest,omitempty"` // 1.3.6.1.4.1.57264.1.10
|
||||
|
||||
// Specifies whether the build took place in platform-hosted cloud infrastructure or customer/self-hosted infrastructure.
|
||||
RunnerEnvironment string `json:"RunnerEnvironment,omitempty"` // 1.3.6.1.4.1.57264.1.11
|
||||
|
||||
// Source repository URL that the build was based on.
|
||||
SourceRepositoryURI string `json:"SourceRepositoryURI,omitempty"` // 1.3.6.1.4.1.57264.1.12
|
||||
|
||||
// Immutable reference to a specific version of the source code that the build was based upon.
|
||||
SourceRepositoryDigest string `json:"SourceRepositoryDigest,omitempty"` // 1.3.6.1.4.1.57264.1.13
|
||||
|
||||
// Source Repository Ref that the build run was based upon.
|
||||
SourceRepositoryRef string `json:"SourceRepositoryRef,omitempty"` // 1.3.6.1.4.1.57264.1.14
|
||||
|
||||
// Immutable identifier for the source repository the workflow was based upon.
|
||||
SourceRepositoryIdentifier string `json:"SourceRepositoryIdentifier,omitempty"` // 1.3.6.1.4.1.57264.1.15
|
||||
|
||||
// Source repository owner URL of the owner of the source repository that the build was based on.
|
||||
SourceRepositoryOwnerURI string `json:"SourceRepositoryOwnerURI,omitempty"` // 1.3.6.1.4.1.57264.1.16
|
||||
|
||||
// Immutable identifier for the owner of the source repository that the workflow was based upon.
|
||||
SourceRepositoryOwnerIdentifier string `json:"SourceRepositoryOwnerIdentifier,omitempty"` // 1.3.6.1.4.1.57264.1.17
|
||||
|
||||
// Build Config URL to the top-level/initiating build instructions.
|
||||
BuildConfigURI string `json:"BuildConfigURI,omitempty"` // 1.3.6.1.4.1.57264.1.18
|
||||
|
||||
// Immutable reference to the specific version of the top-level/initiating build instructions.
|
||||
BuildConfigDigest string `json:"BuildConfigDigest,omitempty"` // 1.3.6.1.4.1.57264.1.19
|
||||
|
||||
// Event or action that initiated the build.
|
||||
BuildTrigger string `json:"BuildTrigger,omitempty"` // 1.3.6.1.4.1.57264.1.20
|
||||
|
||||
// Run Invocation URL to uniquely identify the build execution.
|
||||
RunInvocationURI string `json:"RunInvocationURI,omitempty"` // 1.3.6.1.4.1.57264.1.21
|
||||
|
||||
// Source repository visibility at the time of signing the certificate.
|
||||
SourceRepositoryVisibilityAtSigning string `json:"SourceRepositoryVisibilityAtSigning,omitempty"` // 1.3.6.1.4.1.57264.1.22
|
||||
}
|
||||
+2
-2
@@ -2,9 +2,9 @@ package jsonstream
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// JSONMessage defines a message struct. It describes
|
||||
// Message defines a message struct. It describes
|
||||
// the created time, where it from, status, ID of the
|
||||
// message. It's used for docker events.
|
||||
// message.
|
||||
type Message struct {
|
||||
Stream string `json:"stream,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
|
||||
+3
-3
@@ -58,9 +58,9 @@ func (es *EndpointSettings) Copy() *EndpointSettings {
|
||||
|
||||
// EndpointIPAMConfig represents IPAM configurations for the endpoint
|
||||
type EndpointIPAMConfig struct {
|
||||
IPv4Address netip.Addr `json:",omitempty"`
|
||||
IPv6Address netip.Addr `json:",omitempty"`
|
||||
LinkLocalIPs []netip.Addr `json:",omitempty"`
|
||||
IPv4Address netip.Addr `json:"IPv4Address,omitzero"`
|
||||
IPv6Address netip.Addr `json:"IPv6Address,omitzero"`
|
||||
LinkLocalIPs []netip.Addr `json:"LinkLocalIPs,omitempty"`
|
||||
}
|
||||
|
||||
// Copy makes a copy of the endpoint ipam config
|
||||
|
||||
+5
-3
@@ -11,9 +11,11 @@ import (
|
||||
// in the absence of go.dev/issue/29678.
|
||||
type HardwareAddr net.HardwareAddr
|
||||
|
||||
var _ encoding.TextMarshaler = (HardwareAddr)(nil)
|
||||
var _ encoding.TextUnmarshaler = (*HardwareAddr)(nil)
|
||||
var _ fmt.Stringer = (HardwareAddr)(nil)
|
||||
var (
|
||||
_ encoding.TextMarshaler = (HardwareAddr)(nil)
|
||||
_ encoding.TextUnmarshaler = (*HardwareAddr)(nil)
|
||||
_ fmt.Stringer = (HardwareAddr)(nil)
|
||||
)
|
||||
|
||||
func (m *HardwareAddr) UnmarshalText(text []byte) error {
|
||||
if len(text) == 0 {
|
||||
|
||||
+3
-3
@@ -13,9 +13,9 @@ type IPAM struct {
|
||||
|
||||
// IPAMConfig represents IPAM configurations
|
||||
type IPAMConfig struct {
|
||||
Subnet netip.Prefix `json:",omitempty"`
|
||||
IPRange netip.Prefix `json:",omitempty"`
|
||||
Gateway netip.Addr `json:",omitempty"`
|
||||
Subnet netip.Prefix `json:"Subnet,omitzero"`
|
||||
IPRange netip.Prefix `json:"IPRange,omitzero"`
|
||||
Gateway netip.Addr `json:"Gateway,omitzero"`
|
||||
AuxAddress map[string]netip.Addr `json:"AuxiliaryAddresses,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -61,7 +61,7 @@ type EndpointVirtualIP struct {
|
||||
// Addr is the virtual ip address.
|
||||
// This field accepts CIDR notation, for example `10.0.0.1/24`, to maintain backwards
|
||||
// compatibility, but only the IP address is used.
|
||||
Addr netip.Prefix `json:",omitempty"`
|
||||
Addr netip.Prefix `json:"Addr,omitzero"`
|
||||
}
|
||||
|
||||
// Network represents a network.
|
||||
@@ -111,7 +111,7 @@ type IPAMOptions struct {
|
||||
|
||||
// IPAMConfig represents ipam configuration.
|
||||
type IPAMConfig struct {
|
||||
Subnet netip.Prefix `json:",omitempty"`
|
||||
Range netip.Prefix `json:",omitempty"`
|
||||
Gateway netip.Addr `json:",omitempty"`
|
||||
Subnet netip.Prefix `json:"Subnet,omitzero"`
|
||||
Range netip.Prefix `json:"Range,omitzero"`
|
||||
Gateway netip.Addr `json:"Gateway,omitzero"`
|
||||
}
|
||||
|
||||
+4
-4
@@ -109,14 +109,14 @@ type Limit struct {
|
||||
Pids int64 `json:",omitempty"`
|
||||
}
|
||||
|
||||
// GenericResource represents a "user defined" resource which can
|
||||
// GenericResource represents a "user-defined" resource which can
|
||||
// be either an integer (e.g: SSD=3) or a string (e.g: SSD=sda1)
|
||||
type GenericResource struct {
|
||||
NamedResourceSpec *NamedGenericResource `json:",omitempty"`
|
||||
DiscreteResourceSpec *DiscreteGenericResource `json:",omitempty"`
|
||||
}
|
||||
|
||||
// NamedGenericResource represents a "user defined" resource which is defined
|
||||
// NamedGenericResource represents a "user-defined" resource which is defined
|
||||
// as a string.
|
||||
// "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...)
|
||||
// Value is used to identify the resource (GPU="UUID-1", FPGA="/dev/sdb5", ...)
|
||||
@@ -125,7 +125,7 @@ type NamedGenericResource struct {
|
||||
Value string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// DiscreteGenericResource represents a "user defined" resource which is defined
|
||||
// DiscreteGenericResource represents a "user-defined" resource which is defined
|
||||
// as an integer
|
||||
// "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...)
|
||||
// Value is used to count the resource (SSD=5, HDD=3, ...)
|
||||
@@ -148,7 +148,7 @@ type ResourceRequirements struct {
|
||||
// Tune container memory swappiness (0 to 100) - if not specified, defaults
|
||||
// to the container OS's default - generally 60, or the value predefined in
|
||||
// the image; set to -1 to unset a previously set value
|
||||
MemorySwappiness *int64 `json:MemorySwappiness,omitzero"`
|
||||
MemorySwappiness *int64 `json:"MemorySwappiness,omitzero"`
|
||||
}
|
||||
|
||||
// Placement represents orchestration parameters.
|
||||
|
||||
+6
@@ -74,6 +74,7 @@ type Info struct {
|
||||
FirewallBackend *FirewallInfo `json:"FirewallBackend,omitempty"`
|
||||
CDISpecDirs []string
|
||||
DiscoveredDevices []DeviceInfo `json:",omitempty"`
|
||||
NRI *NRIInfo `json:",omitempty"`
|
||||
|
||||
Containerd *ContainerdInfo `json:",omitempty"`
|
||||
|
||||
@@ -163,3 +164,8 @@ type DeviceInfo struct {
|
||||
// Example: CDI FQDN like "vendor.com/gpu=0", or other driver-specific device ID
|
||||
ID string `json:"ID"`
|
||||
}
|
||||
|
||||
// NRIInfo describes the NRI configuration.
|
||||
type NRIInfo struct {
|
||||
Info [][2]string `json:"Info,omitempty"`
|
||||
}
|
||||
|
||||
+4
-1
@@ -10,9 +10,12 @@ const (
|
||||
// MediaTypeJSON is the MIME-Type for JSON objects.
|
||||
MediaTypeJSON = "application/json"
|
||||
|
||||
// MediaTypeNDJSON is the MIME-Type for Newline Delimited JSON objects streams.
|
||||
// MediaTypeNDJSON is the MIME-Type for Newline Delimited JSON objects streams (https://github.com/ndjson/ndjson-spec).
|
||||
MediaTypeNDJSON = "application/x-ndjson"
|
||||
|
||||
// MediaTypeJSONLines is the MIME-Type for JSONLines objects streams (https://jsonlines.org/).
|
||||
MediaTypeJSONLines = "application/jsonl"
|
||||
|
||||
// MediaTypeJSONSequence is the MIME-Type for JSON Text Sequences (RFC7464).
|
||||
MediaTypeJSONSequence = "application/json-seq"
|
||||
)
|
||||
|
||||
+14
-3
@@ -1,7 +1,7 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
@@ -176,13 +176,24 @@
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2013-2018 Docker, Inc.
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
||||
+9
-1
@@ -59,6 +59,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -106,7 +107,7 @@ const DummyHost = "api.moby.localhost"
|
||||
// overriding the version and disable API-version negotiation.
|
||||
//
|
||||
// This version may be lower than the version of the api library module used.
|
||||
const MaxAPIVersion = "1.52"
|
||||
const MaxAPIVersion = "1.53"
|
||||
|
||||
// MinAPIVersion is the minimum API version supported by the client. API versions
|
||||
// below this version are not considered when performing API-version negotiation.
|
||||
@@ -241,6 +242,13 @@ func New(ops ...Opt) (*Client, error) {
|
||||
|
||||
c.client.Transport = otelhttp.NewTransport(c.client.Transport, c.traceOpts...)
|
||||
|
||||
if len(cfg.responseHooks) > 0 {
|
||||
c.client.Transport = &responseHookTransport{
|
||||
base: c.client.Transport,
|
||||
hooks: slices.Clone(cfg.responseHooks),
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
|
||||
+35
-3
@@ -2,6 +2,7 @@ package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -55,10 +56,19 @@ type clientConfig struct {
|
||||
// takes precedence. Either field disables API-version negotiation.
|
||||
envAPIVersion string
|
||||
|
||||
// responseHooks is a list of custom response hooks to call on responses.
|
||||
responseHooks []ResponseHook
|
||||
|
||||
// traceOpts is a list of options to configure the tracing span.
|
||||
traceOpts []otelhttp.Option
|
||||
}
|
||||
|
||||
// ResponseHook is called for each HTTP response returned by the daemon.
|
||||
// Hooks are invoked in the order they were added.
|
||||
//
|
||||
// Hooks must not read or close resp.Body.
|
||||
type ResponseHook func(*http.Response)
|
||||
|
||||
// Opt is a configuration option to initialize a [Client].
|
||||
type Opt func(*clientConfig) error
|
||||
|
||||
@@ -133,8 +143,6 @@ func (tf testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
|
||||
return tf(req)
|
||||
}
|
||||
|
||||
func (testRoundTripper) skipConfigureTransport() bool { return true }
|
||||
|
||||
// WithHostFromEnv overrides the client host with the host specified in the
|
||||
// DOCKER_HOST ([EnvOverrideHost]) environment variable. If DOCKER_HOST is not set,
|
||||
// or set to an empty value, the host is not modified.
|
||||
@@ -151,7 +159,16 @@ func WithHostFromEnv() Opt {
|
||||
func WithHTTPClient(client *http.Client) Opt {
|
||||
return func(c *clientConfig) error {
|
||||
if client != nil {
|
||||
c.client = client
|
||||
// Make a clone of client so modifications do not affect
|
||||
// the caller's client. Clone here instead of in New()
|
||||
// as other options (WithHost) also mutate c.client.
|
||||
// Cloned clients share the same CookieJar as the
|
||||
// original.
|
||||
hc := *client
|
||||
if ht, ok := hc.Transport.(*http.Transport); ok {
|
||||
hc.Transport = ht.Clone()
|
||||
}
|
||||
c.client = &hc
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -341,3 +358,18 @@ func WithTraceOptions(opts ...otelhttp.Option) Opt {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithResponseHook adds a ResponseHook to the client. ResponseHooks are called
|
||||
// for each HTTP response returned by the daemon. Hooks are invoked in the order
|
||||
// they were added.
|
||||
//
|
||||
// Hooks must not read or close resp.Body.
|
||||
func WithResponseHook(h ResponseHook) Opt {
|
||||
return func(c *clientConfig) error {
|
||||
if h == nil {
|
||||
return errors.New("invalid response hook: hook is nil")
|
||||
}
|
||||
c.responseHooks = append(c.responseHooks, h)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type responseHookTransport struct {
|
||||
base http.RoundTripper
|
||||
hooks []ResponseHook
|
||||
}
|
||||
|
||||
func (t *responseHookTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
resp, err := t.base.RoundTrip(req)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
for _, h := range t.hooks {
|
||||
h(resp)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
+1
-1
@@ -23,7 +23,7 @@ type ContainerAttachResult struct {
|
||||
|
||||
// ContainerAttach attaches a connection to a container in the server.
|
||||
// It returns a [HijackedResponse] with the hijacked connection
|
||||
// and a reader to get output. It's up to the called to close
|
||||
// and a reader to get output. It's up to the caller to close
|
||||
// the hijacked connection by calling [HijackedResponse.Close].
|
||||
//
|
||||
// The stream format on the response uses one of two formats:
|
||||
|
||||
+5
-7
@@ -5,7 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
cerrdefs "github.com/containerd/errdefs"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
)
|
||||
|
||||
@@ -82,8 +82,7 @@ type ExecStartOptions struct {
|
||||
}
|
||||
|
||||
// ExecStartResult holds the result of starting a container exec.
|
||||
type ExecStartResult struct {
|
||||
}
|
||||
type ExecStartResult struct{}
|
||||
|
||||
// ExecStart starts an exec process already created in the docker host.
|
||||
func (cli *Client) ExecStart(ctx context.Context, execID string, options ExecStartOptions) (ExecStartResult, error) {
|
||||
@@ -118,7 +117,7 @@ type ExecAttachResult struct {
|
||||
// ExecAttach attaches a connection to an exec process in the server.
|
||||
//
|
||||
// It returns a [HijackedResponse] with the hijacked connection
|
||||
// and a reader to get output. It's up to the called to close
|
||||
// and a reader to get output. It's up to the caller to close
|
||||
// the hijacked connection by calling [HijackedResponse.Close].
|
||||
//
|
||||
// The stream format on the response uses one of two formats:
|
||||
@@ -152,7 +151,7 @@ func (cli *Client) ExecAttach(ctx context.Context, execID string, options ExecAt
|
||||
func getConsoleSize(hasTTY bool, consoleSize ConsoleSize) (*[2]uint, error) {
|
||||
if consoleSize.Height != 0 || consoleSize.Width != 0 {
|
||||
if !hasTTY {
|
||||
return nil, errdefs.ErrInvalidArgument.WithMessage("console size is only supported when TTY is enabled")
|
||||
return nil, cerrdefs.ErrInvalidArgument.WithMessage("console size is only supported when TTY is enabled")
|
||||
}
|
||||
return &[2]uint{consoleSize.Height, consoleSize.Width}, nil
|
||||
}
|
||||
@@ -160,8 +159,7 @@ func getConsoleSize(hasTTY bool, consoleSize ConsoleSize) (*[2]uint, error) {
|
||||
}
|
||||
|
||||
// ExecInspectOptions holds options for inspecting a container exec.
|
||||
type ExecInspectOptions struct {
|
||||
}
|
||||
type ExecInspectOptions struct{}
|
||||
|
||||
// ExecInspectResult holds the result of inspecting a container exec.
|
||||
//
|
||||
|
||||
+15
-11
@@ -13,11 +13,23 @@ import (
|
||||
type ContainerListOptions struct {
|
||||
Size bool
|
||||
All bool
|
||||
Latest bool
|
||||
Since string
|
||||
Before string
|
||||
Limit int
|
||||
Filters Filters
|
||||
|
||||
// Latest is non-functional and should not be used. Use Limit: 1 instead.
|
||||
//
|
||||
// Deprecated: the Latest option is non-functional and should not be used. Use Limit: 1 instead.
|
||||
Latest bool
|
||||
|
||||
// Since is no longer supported. Use the "since" filter instead.
|
||||
//
|
||||
// Deprecated: the Since option is no longer supported since docker 1.12 (API 1.24). Use the "since" filter instead.
|
||||
Since string
|
||||
|
||||
// Before is no longer supported. Use the "since" filter instead.
|
||||
//
|
||||
// Deprecated: the Before option is no longer supported since docker 1.12 (API 1.24). Use the "before" filter instead.
|
||||
Before string
|
||||
}
|
||||
|
||||
type ContainerListResult struct {
|
||||
@@ -36,14 +48,6 @@ func (cli *Client) ContainerList(ctx context.Context, options ContainerListOptio
|
||||
query.Set("limit", strconv.Itoa(options.Limit))
|
||||
}
|
||||
|
||||
if options.Since != "" {
|
||||
query.Set("since", options.Since)
|
||||
}
|
||||
|
||||
if options.Before != "" {
|
||||
query.Set("before", options.Before)
|
||||
}
|
||||
|
||||
if options.Size {
|
||||
query.Set("size", "1")
|
||||
}
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
cerrdefs "github.com/containerd/errdefs"
|
||||
)
|
||||
|
||||
// ContainerRenameOptions represents the options for renaming a container.
|
||||
@@ -28,7 +28,7 @@ func (cli *Client) ContainerRename(ctx context.Context, containerID string, opti
|
||||
if options.NewName == "" || strings.TrimPrefix(options.NewName, "/") == "" {
|
||||
// daemons before v29.0 did not handle the canonical name ("/") well
|
||||
// let's be nice and validate it here before sending
|
||||
return ContainerRenameResult{}, errdefs.ErrInvalidArgument.WithMessage("new name cannot be blank")
|
||||
return ContainerRenameResult{}, cerrdefs.ErrInvalidArgument.WithMessage("new name cannot be blank")
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user