Compare commits

...

24 Commits

Author SHA1 Message Date
Tõnis Tiigi ae4e7ee6a4
Merge pull request #3370 from thaJeztah/bump_engine
vendor: github.com/docker/docker, docker/cli v28.4.0
2025-09-05 14:23:53 -07:00
CrazyMax 70487beecb
Merge pull request #3405 from thaJeztah/dockerfile_bump_docker
Dockerfile: update to docker v28.4.0
2025-09-05 10:03:38 +02:00
CrazyMax 86ddc5de4e
Merge pull request #3406 from docker/dependabot/github_actions/actions/labeler-6
build(deps): bump actions/labeler from 5 to 6
2025-09-05 10:03:17 +02:00
CrazyMax 7bcaf399b9
Merge pull request #3407 from docker/dependabot/github_actions/actions/setup-go-6
build(deps): bump actions/setup-go from 5 to 6
2025-09-05 10:02:58 +02:00
dependabot[bot] dc10c680f3
build(deps): bump actions/setup-go from 5 to 6
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 6.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-04 18:03:46 +00:00
dependabot[bot] 9c9fb2a12a
build(deps): bump actions/labeler from 5 to 6
Bumps [actions/labeler](https://github.com/actions/labeler) from 5 to 6.
- [Release notes](https://github.com/actions/labeler/releases)
- [Commits](https://github.com/actions/labeler/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/labeler
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-04 18:03:42 +00:00
Sebastiaan van Stijn b4d5ec9bc2
Dockerfile: update to docker v28.4.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-09-04 00:24:06 +02:00
Sebastiaan van Stijn a923dbc1d9
vendor: github.com/docker/docker, docker/cli v28.4.0
full diffs:

- https://github.com/docker/docker/compare/v28.3.3...v28.4.0
- https://github.com/docker/cli/compare/v28.3.3...v28.4.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-09-04 00:11:44 +02:00
Tõnis Tiigi bafc4e207e
Merge pull request #3402 from jsternberg/dap-test-close-client
dap: ensure test client is closed on cleanup
2025-09-03 11:23:51 -07:00
Tõnis Tiigi 2109c9d80d
Merge pull request #3399 from crazy-max/test-gitquerystring
test: git query string
2025-09-03 09:35:04 -07:00
Jonathan A. Sternberg 8841b2dfc8
dap: ensure test client is closed on cleanup
The dap test wasn't waiting for the client's goroutines to complete
before exiting which caused a race condition that could cause it to log
to the dead test logger. This became apparent when `--count` of greater
than one was used since it caused the test to run long enough to trigger
the behavior. It would have also triggered if we had added more tests.

Add the client close to the cleanup so it waits for the goroutine to
finish before the test exits as it was properly supposed to do.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-09-03 10:51:01 -05:00
CrazyMax 643322cbc3
test: git query string
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-09-03 17:49:38 +02:00
Akihiro Suda 056780314b
Merge pull request #3401 from crazy-max/buildkit-0.24.0
vendor: update buildkit to v0.24.0
2025-09-03 23:33:10 +09:00
CrazyMax d136d2ba53
vendor: update buildkit to v0.24.0
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-09-03 16:14:35 +02:00
Akihiro Suda e4f23adf3f
Merge pull request #3398 from tonistiigi/gitquerystring-cap-detect
git querystring frontend capability detection
2025-09-03 16:50:10 +09:00
Tonis Tiigi 5e6951c571
git querystring frontend capability detection
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-09-02 10:46:37 -07:00
Tõnis Tiigi d873cae872
Merge pull request #3392 from crazy-max/docs-du-filter
docs: list available filters for du and prune commands
2025-08-29 15:05:35 -07:00
Tõnis Tiigi 4df89d89fc
Merge pull request #3397 from tonistiigi/update-buildkit-v0.24.0-rc2
vendor: update buildkit to v0.24.0-rc2
2025-08-29 15:04:27 -07:00
Tonis Tiigi 1f39ad2001
vendor: update buildkit to v0.24.0-rc2
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2025-08-29 14:49:16 -07:00
Tõnis Tiigi ce3592e4ab
Merge pull request #3390 from crazy-max/docs-du-fixes
docs: fixes for du command
2025-08-29 10:37:22 -07:00
Tõnis Tiigi 67218bef58
Merge pull request #3394 from thaJeztah/check_DisableFlagsInUseLine
commands: verify that DisableFlagsInUseLine is set for all commands
2025-08-28 12:53:21 -07:00
Sebastiaan van Stijn 07b99ae7bf
commands: verify that DisableFlagsInUseLine is set for all commands
This replaces the DisableFlagsInUseLine call from the CLI with a test
that verifies the option is set for all commands and subcommands, so
that it doesn't have to be modified at runtime.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-08-28 17:55:50 +02:00
CrazyMax ebe66a8e2e
docs: list available filters for du and prune commands
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-08-28 15:32:10 +02:00
CrazyMax ce07ae04cd
docs: fixes for du command
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2025-08-28 11:25:53 +02:00
98 changed files with 1141 additions and 1373 deletions

View File

@ -194,7 +194,7 @@ jobs:
uses: actions/checkout@v5
-
name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: "${{ env.GO_VERSION }}"
-

View File

@ -32,7 +32,7 @@ jobs:
uses: actions/checkout@v5
-
name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
-

View File

@ -27,6 +27,6 @@ jobs:
steps:
-
name: Run
uses: actions/labeler@v5
uses: actions/labeler@v6
with:
sync-labels: true

View File

@ -5,7 +5,7 @@ ARG ALPINE_VERSION=3.22
ARG XX_VERSION=1.6.1
# for testing
ARG DOCKER_VERSION=28.3
ARG DOCKER_VERSION=28.4
ARG DOCKER_VERSION_ALT_27=27.5.1
ARG DOCKER_VERSION_ALT_26=26.1.3
ARG DOCKER_CLI_VERSION=${DOCKER_VERSION}

View File

@ -32,8 +32,12 @@ func ReadRemoteFiles(ctx context.Context, nodes []builder.Node, url string, name
var sessions []session.Attachable
var filename string
st, ok := dockerui.DetectGitContext(url, false)
keepGitDir := false
st, ok, err := dockerui.DetectGitContext(url, &keepGitDir)
if ok {
if err != nil {
return nil, nil, err
}
if ssh, err := build.CreateSSH([]*buildflags.SSH{{
ID: "default",
Paths: strings.Split(os.Getenv("BUILDX_BAKE_GIT_SSH"), ","),

View File

@ -38,6 +38,7 @@ import (
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/apicaps"
"github.com/moby/buildkit/util/entitlements"
"github.com/moby/buildkit/util/gitutil"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/tonistiigi/fsutil"
@ -404,6 +405,7 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro
dockerfileName = inp.DockerfilePath
dockerfileSrcName = inp.DockerfilePath
toRemove []string
caps = map[string]struct{}{}
)
switch {
@ -469,6 +471,12 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro
target.FrontendAttrs["dockerfilekey"] = "dockerfile"
}
target.FrontendAttrs["context"] = inp.ContextPath
gitRef, err := gitutil.ParseURL(inp.ContextPath)
if err == nil && len(gitRef.Query) > 0 {
caps["moby.buildkit.frontend.gitquerystring"] = struct{}{}
}
default:
return nil, errors.Errorf("unable to prepare context: path %q not found", inp.ContextPath)
}
@ -516,7 +524,7 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro
target.FrontendAttrs["filename"] = dockerfileName
for k, v := range inp.NamedContexts {
target.FrontendAttrs["frontend.caps"] = "moby.buildkit.frontend.contexts+forward"
caps["moby.buildkit.frontend.contexts+forward"] = struct{}{}
if v.State != nil {
target.FrontendAttrs["context:"+k] = "input:" + k
if target.FrontendInputs == nil {
@ -528,6 +536,12 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro
if IsRemoteURL(v.Path) || strings.HasPrefix(v.Path, "docker-image://") || strings.HasPrefix(v.Path, "target:") {
target.FrontendAttrs["context:"+k] = v.Path
gitRef, err := gitutil.ParseURL(v.Path)
if err == nil && len(gitRef.Query) > 0 {
if _, ok := caps["moby.buildkit.frontend.gitquerystring"]; !ok {
caps["moby.buildkit.frontend.gitquerystring+forward"] = struct{}{}
}
}
continue
}
@ -557,6 +571,7 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro
target.FrontendAttrs["context:"+k] = "oci-layout://" + storeName + ":" + tag + "@" + dig
continue
}
st, err := os.Stat(v.Path)
if err != nil {
return nil, errors.Wrapf(err, "failed to get build context %v", k)
@ -580,6 +595,12 @@ func loadInputs(ctx context.Context, d *driver.DriverHandle, inp *Inputs, pw pro
}
}
if len(caps) > 0 {
keys := slices.Collect(maps.Keys(caps))
slices.Sort(keys)
target.FrontendAttrs["frontend.caps"] = strings.Join(keys, ",")
}
inp.DockerfileMappingSrc = dockerfileSrcName
inp.DockerfileMappingDst = dockerfileName
return release, nil

View File

@ -36,7 +36,7 @@ func IsRemoteURL(c string) bool {
if isHTTPURL(c) {
return true
}
if _, err := dfgitutil.ParseGitRef(c); err == nil {
if _, ok, _ := dfgitutil.ParseGitRef(c); ok {
return true
}
return false

View File

@ -492,7 +492,8 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
// Other common flags (noCache, pull and progress) are processed in runBake function.
return runBake(cmd.Context(), dockerCli, args, options, cFlags, filesFromEnv)
},
ValidArgsFunction: completion.BakeTargets(options.files),
ValidArgsFunction: completion.BakeTargets(options.files),
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -490,6 +490,7 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugger debuggerOpt
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return nil, cobra.ShellCompDirectiveFilterDirs
},
DisableFlagsInUseLine: true,
}
var platformsDefault []string
@ -691,6 +692,11 @@ func wrapBuildError(err error, bake bool) error {
msg += " Named contexts are supported since Dockerfile v1.4. Use #syntax directive in Dockerfile or update to latest BuildKit."
return &wrapped{err, msg}
}
if st.Code() == codes.Unimplemented && strings.Contains(st.Message(), "unsupported frontend capability moby.buildkit.frontend.gitquerystring") {
msg := "current frontend does not support Git URLs with query string components."
msg += " Git URLs with query string are supported since Dockerfile v1.18 and BuildKit v0.24. Use BUILDKIT_SYNTAX build-arg, #syntax directive in Dockerfile or update to latest BuildKit."
return &wrapped{err, msg}
}
}
return err
}

View File

@ -98,7 +98,8 @@ func createCmd(dockerCli command.Cli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
return runCreate(cmd.Context(), dockerCli, options, args)
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -23,6 +23,8 @@ func dapCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
cmd := &cobra.Command{
Use: "dap",
Short: "Start debug adapter protocol compatible debugger",
DisableFlagsInUseLine: true,
}
cobrautil.MarkCommandExperimental(cmd)
@ -116,6 +118,7 @@ func dapAttachCmd() *cobra.Command {
}
return nil
},
DisableFlagsInUseLine: true,
}
return cmd
}

View File

@ -44,6 +44,8 @@ func debugCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
cmd := &cobra.Command{
Use: "debug",
Short: "Start debugger",
DisableFlagsInUseLine: true,
}
cobrautil.MarkCommandExperimental(cmd)

View File

@ -122,6 +122,7 @@ func dialStdioCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
opts.builder = rootOpts.builder
return runDialStdio(dockerCli, opts)
},
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -189,7 +189,8 @@ func duCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
options.builder = rootOpts.builder
return runDiskUsage(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -160,7 +160,8 @@ func exportCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
options.builder = *rootOpts.Builder
return runExport(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -125,7 +125,8 @@ func importCmd(dockerCli command.Cli, _ RootOptions) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
return runImport(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -656,7 +656,8 @@ func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
options.builder = *rootOpts.Builder
return runInspect(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
cmd.AddCommand(

View File

@ -129,7 +129,8 @@ func attachmentCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
options.builder = *rootOpts.Builder
return runAttachment(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -96,7 +96,8 @@ func logsCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
options.builder = *rootOpts.Builder
return runLogs(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -103,7 +103,8 @@ func lsCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
options.builder = *rootOpts.Builder
return runLs(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -55,7 +55,8 @@ func openCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
options.builder = *rootOpts.Builder
return runOpen(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
return cmd

View File

@ -129,7 +129,8 @@ func rmCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
options.builder = *rootOpts.Builder
return runRm(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -16,6 +16,8 @@ func RootCmd(rootcmd *cobra.Command, dockerCli command.Cli, opts RootOptions) *c
Short: "Commands to work on build records",
ValidArgsFunction: completion.Disable,
RunE: rootcmd.RunE,
DisableFlagsInUseLine: true,
}
cmd.AddCommand(

View File

@ -199,7 +199,8 @@ func traceCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
options.builder = *rootOpts.Builder
return runTrace(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -332,7 +332,7 @@ func valueFiler(key, value, sep string) matchFunc {
recValue = v
} else {
if context, ok := rec.FrontendAttrs["context"]; ok {
if ref, err := dfgitutil.ParseGitRef(context); err == nil {
if ref, _, err := dfgitutil.ParseGitRef(context); err == nil {
recValue = ref.Remote
}
}

View File

@ -279,7 +279,8 @@ func createCmd(dockerCli command.Cli, opts RootOptions) *cobra.Command {
options.builder = *opts.Builder
return runCreate(cmd.Context(), dockerCli, options, args)
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -52,7 +52,8 @@ func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
options.builder = *rootOpts.Builder
return runInspect(cmd.Context(), dockerCli, options, args[0])
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -12,10 +12,11 @@ type RootOptions struct {
func RootCmd(rootcmd *cobra.Command, dockerCli command.Cli, opts RootOptions) *cobra.Command {
cmd := &cobra.Command{
Use: "imagetools",
Short: "Commands to work on images in registry",
ValidArgsFunction: completion.Disable,
RunE: rootcmd.RunE,
Use: "imagetools",
Short: "Commands to work on images in registry",
ValidArgsFunction: completion.Disable,
RunE: rootcmd.RunE,
DisableFlagsInUseLine: true,
}
cmd.AddCommand(

View File

@ -182,7 +182,8 @@ func inspectCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
}
return runInspect(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.BuilderNames(dockerCli),
ValidArgsFunction: completion.BuilderNames(dockerCli),
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -47,8 +47,9 @@ func installCmd(dockerCli command.Cli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
return runInstall(dockerCli, options)
},
Hidden: true,
ValidArgsFunction: completion.Disable,
Hidden: true,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
// hide builder persistent flag for this command

View File

@ -107,7 +107,8 @@ func lsCmd(dockerCli command.Cli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
return runLs(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -170,12 +170,13 @@ func pruneCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
options.builder = rootOpts.builder
return runPrune(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()
flags.BoolVarP(&options.all, "all", "a", false, "Include internal/frontend images")
flags.Var(&options.filter, "filter", `Provide filter values (e.g., "until=24h")`)
flags.Var(&options.filter, "filter", `Provide filter values`)
flags.Var(&options.reservedSpace, "reserved-space", "Amount of disk space always allowed to keep for cache")
flags.Var(&options.minFreeSpace, "min-free-space", "Target amount of free disk space after pruning")
flags.Var(&options.maxUsedSpace, "max-used-space", "Maximum amount of disk space allowed to keep for cache")

View File

@ -111,7 +111,8 @@ func rmCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
}
return runRm(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.BuilderNames(dockerCli),
ValidArgsFunction: completion.BuilderNames(dockerCli),
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -71,6 +71,7 @@ func NewRootCmd(name string, isPlugin bool, dockerCli *command.DockerCli) *cobra
Status: fmt.Sprintf("ERROR: unknown command: %q", args[0]),
}
},
DisableFlagsInUseLine: true,
}
if !isPlugin {
// match plugin behavior for standalone mode
@ -78,8 +79,6 @@ func NewRootCmd(name string, isPlugin bool, dockerCli *command.DockerCli) *cobra
cmd.SilenceUsage = true
cmd.SilenceErrors = true
cmd.TraverseChildren = true
cmd.DisableFlagsInUseLine = true
cli.DisableFlagsInUseLine(cmd)
if !confutil.IsExperimental() {
cmd.SetHelpTemplate(cmd.HelpTemplate() + "\n" + experimentalCommandHint + "\n")
}

33
commands/root_test.go Normal file
View File

@ -0,0 +1,33 @@
package commands
import (
stderrs "errors"
"testing"
"github.com/docker/cli/cli/command"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/stretchr/testify/require"
)
func TestDisableFlagsInUseLineIsSet(t *testing.T) {
cmd, err := command.NewDockerCli()
require.NoError(t, err)
rootCmd := NewRootCmd("buildx", true, cmd)
var errs []error
visitAll(rootCmd, func(c *cobra.Command) {
if !c.DisableFlagsInUseLine {
errs = append(errs, errors.New("DisableFlagsInUseLine is not set for "+c.CommandPath()))
}
})
err = stderrs.Join(errs...)
require.NoError(t, err)
}
func visitAll(root *cobra.Command, fn func(*cobra.Command)) {
for _, cmd := range root.Commands() {
visitAll(cmd, fn)
}
fn(root)
}

View File

@ -44,7 +44,8 @@ func stopCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
}
return runStop(cmd.Context(), dockerCli, options)
},
ValidArgsFunction: completion.BuilderNames(dockerCli),
ValidArgsFunction: completion.BuilderNames(dockerCli),
DisableFlagsInUseLine: true,
}
return cmd

View File

@ -53,8 +53,9 @@ func uninstallCmd(dockerCli command.Cli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
return runUninstall(dockerCli, options)
},
Hidden: true,
ValidArgsFunction: completion.Disable,
Hidden: true,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
// hide builder persistent flag for this command

View File

@ -71,7 +71,8 @@ func useCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
}
return runUse(dockerCli, options)
},
ValidArgsFunction: completion.BuilderNames(dockerCli),
ValidArgsFunction: completion.BuilderNames(dockerCli),
DisableFlagsInUseLine: true,
}
flags := cmd.Flags()

View File

@ -24,7 +24,8 @@ func versionCmd(dockerCli command.Cli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
return runVersion(dockerCli)
},
ValidArgsFunction: completion.Disable,
ValidArgsFunction: completion.Disable,
DisableFlagsInUseLine: true,
}
// hide builder persistent flag for this command

View File

@ -14,8 +14,6 @@ import (
)
func TestLaunch(t *testing.T) {
t.Skip("test fails with errgroup v0.16.0, that doesn't swallow panic in goroutine")
adapter, conn, client := NewTestAdapter[common.Config](t)
ctx, cancel := context.WithTimeoutCause(context.Background(), 10*time.Second, context.DeadlineExceeded)
@ -83,14 +81,14 @@ func NewTestAdapter[C LaunchConfig](t *testing.T) (*Adapter[C], Conn, *Client) {
})
clientConn := logConn(t, "client", NewConn(rd2, wr1))
t.Cleanup(func() {
clientConn.Close()
})
t.Cleanup(func() { clientConn.Close() })
adapter := New[C]()
t.Cleanup(func() { adapter.Stop() })
client := NewClient(clientConn)
t.Cleanup(func() { client.Close() })
return adapter, srvConn, client
}

View File

@ -13,7 +13,7 @@ Disk usage
|:------------------------|:---------|:--------|:-----------------------------------------|
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `--filter` | `filter` | | Provide filter values |
| [`--filter`](#filter) | `filter` | | Provide filter values |
| [`--format`](#format) | `string` | | Format the output |
| [`--verbose`](#verbose) | `bool` | | Shorthand for `--format=pretty` |
@ -62,6 +62,10 @@ The asterisks (\*) in the default output format indicate the following:
If you prune such a record then you will lose build cache but only metadata
will be deleted as the image still needs to actual storage layers.
### <a name="filter"></a> Provide filter values (--filter)
Same as [`buildx prune --filter`](buildx_prune.md#filter).
### <a name="format"></a> Format the output (--format)
The formatting options (`--format`) pretty-prints usage information output
@ -82,7 +86,8 @@ Valid placeholders for the Go template are:
* `.Type`
When using the `--format` option, the `du` command will either output the data
exactly as the template declares.
exactly as the template declares or, when using the `table` directive, includes
column headers as well.
The `pretty` format is useful for inspecting the disk usage records in more
detail. It shows the mutable and shared states more clearly, as well as
@ -128,10 +133,15 @@ The following example uses a `table` template and outputs the `ID` and
`Description`:
```console
$ docker buildx du --format "table {{.ID}} {{.Descirption}}"
lu76wm07lk5u7fe9nul93o95o [integration-tests 1/1] COPY . .
v6zmkcmgujv34vnys9eszttnv [dev 1/1] COPY --link . .
nj4fwb6qxznswmij3fg30sns2 mount / from exec /bin/sh -c rpm-init $DISTRO_NAME
$ docker buildx du --format "table {{.ID}} {{.Description}}"
ID DESCRIPTION
03bbhchaib8cygqs68um6hfnl [binaries-linux 2/5] LINK COPY --link --from=binfmt-filter /out/ /
2h8un0tyg57oj64xvbas6mzea [cni-plugins-export 2/4] LINK COPY --link --from=cni-plugins /opt/cni/bin/loopback /buildkit-cni-loopback
evckox33t07ob9dmollhn4h4j [cni-plugins-export 3/4] LINK COPY --link --from=cni-plugins /opt/cni/bin/host-local /buildkit-cni-host-local
jlxzwcw6xaomxj8irerow9bhb [binaries-linux 4/5] LINK COPY --link --from=buildctl /usr/bin/buildctl /
ov2oetgebkhpsw39rv1sbh5w1 [buildkit-linux 1/1] LINK COPY --link --from=binaries / /usr/bin/
ruoczhyq25n5v9ld7n231zalx [binaries-linux 3/5] LINK COPY --link --from=cni-plugins-export-squashed / /
ax7cov6kizxi9ufvcwsef4occ* local source for context
```
JSON output is also supported and will print as newline delimited JSON:
@ -209,7 +219,7 @@ Total: 133.5GB
Use the `--builder` flag to inspect the disk usage of a particular builder.
```console
$ docker buildx du --builder youthful_shtern
$ docker buildx du --builder mybuilder
ID RECLAIMABLE SIZE LAST ACCESSED
g41agepgdczekxg2mtw0dujsv* true 1.312GB 47 hours ago
e6ycrsa0bn9akigqgzu0sc6kr true 318MB 47 hours ago

View File

@ -9,17 +9,17 @@ Remove build cache
### Options
| Name | Type | Default | Description |
|:------------------------|:---------|:--------|:-------------------------------------------------------|
| `-a`, `--all` | `bool` | | Include internal/frontend images |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| `--filter` | `filter` | | Provide filter values (e.g., `until=24h`) |
| `-f`, `--force` | `bool` | | Do not prompt for confirmation |
| `--max-used-space` | `bytes` | `0` | Maximum amount of disk space allowed to keep for cache |
| `--min-free-space` | `bytes` | `0` | Target amount of free disk space after pruning |
| `--reserved-space` | `bytes` | `0` | Amount of disk space always allowed to keep for cache |
| `--verbose` | `bool` | | Provide a more verbose output |
| Name | Type | Default | Description |
|:--------------------------------------|:---------|:--------|:-------------------------------------------------------|
| [`-a`](#all), [`--all`](#all) | `bool` | | Include internal/frontend images |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
| `-D`, `--debug` | `bool` | | Enable debug logging |
| [`--filter`](#filter) | `filter` | | Provide filter values |
| `-f`, `--force` | `bool` | | Do not prompt for confirmation |
| [`--max-used-space`](#max-used-space) | `bytes` | `0` | Maximum amount of disk space allowed to keep for cache |
| [`--min-free-space`](#min-free-space) | `bytes` | `0` | Target amount of free disk space after pruning |
| [`--reserved-space`](#reserved-space) | `bytes` | `0` | Amount of disk space always allowed to keep for cache |
| `--verbose` | `bool` | | Provide a more verbose output |
<!---MARKER_GEN_END-->
@ -28,24 +28,89 @@ Remove build cache
Clears the build cache of the selected builder.
You can finely control what cache data is kept using:
- The `--filter=until=<duration>` flag to keep images that have been used in
the last `<duration>` time.
`<duration>` is a duration string, e.g. `24h` or `2h30m`, with allowable
units of `(h)ours`, `(m)inutes` and `(s)econds`.
- The `--keep-storage=<size>` flag to keep `<size>` bytes of data in the cache.
`<size>` is a human-readable memory string, e.g. `128mb`, `2gb`, etc. Units
are case-insensitive.
- The `--all` flag to allow clearing internal helper images and frontend images
set using the `#syntax=` directive or the `BUILDKIT_SYNTAX` build argument.
## Examples
### <a name="all"></a> Include internal/frontend images (--all)
The `--all` flag to allow clearing internal helper images and frontend images
set using the `#syntax=` directive or the `BUILDKIT_SYNTAX` build argument.
### <a name="filter"></a> Provide filter values (--filter)
You can finely control which cache records to delete using the `--filter` flag.
The filter format is in the form of `<key><op><value>`, known as selectors. All
selectors must match the target object for the filter to be true. We define the
operators `=` for equality, `!=` for not equal and `~=` for a regular
expression.
Valid filter keys are:
- `until` flag to keep records that have been used in the last duration time.
Value is a duration string, e.g. `24h` or `2h30m`, with allowable units of
`(h)ours`, `(m)inutes` and `(s)econds`.
- `id` flag to target a specific image ID.
- `parents` flag to target records that are parents of the
specified image ID. Multiple parent IDs are separated by a semicolon (`;`).
- `description` flag to target records whose description is the specified
substring.
- `inuse` flag to target records that are actively in use and therefore not
reclaimable.
- `mutable` flag to target records that are mutable.
- `immutable` flag to target records that are immutable.
- `shared` flag to target records that are shared with other resources,
typically images.
- `private` flag to target records that are not shared.
- `type` flag to target records by type. Valid types are:
- `internal`
- `frontend`
- `source.local`
- `source.git.checkout`
- `exec.cachemount`
- `regular`
Examples:
```console
docker buildx prune --filter "until=24h"
docker buildx prune --filter "description~=golang"
docker buildx prune --filter "parents=dpetmoi6n0yqanxjqrbnofz9n;kgoj0q6g57i35gdyrv546alz7"
docker buildx prune --filter "type=source.local"
docker buildx prune --filter "type!=exec.cachemount"
```
> [!NOTE]
> Multiple `--filter` flags are ANDed together.
### <a name="max-used-space"></a> Maximum amount of disk space allowed to keep for cache (--max-used-space)
The `--max-used-space` flag allows setting a maximum amount of disk space
that the build cache can use. If the cache is using more disk space than this
value, the least recently used cache records are deleted until the total
used space is less than or equal to the specified value.
The value is specified in bytes. You can use a human-readable memory string,
e.g. `128mb`, `2gb`, etc. Units are case-insensitive.
### <a name="min-free-space"></a> Target amount of free disk space after pruning (--min-free-space)
The `--min-free-space` flag allows setting a target amount of free disk space
that should be available after pruning. If the available disk space is less
than this value, the least recently used cache records are deleted until
the available free space is greater than or equal to the specified value.
The value is specified in bytes. You can use a human-readable memory string,
e.g. `128mb`, `2gb`, etc. Units are case-insensitive.
### <a name="reserved-space"></a> Amount of disk space always allowed to keep for cache (--reserved-space)
The `--reserved-space` flag allows setting an amount of disk space that
should always be kept for the build cache. If the available disk space is less
than this value, the least recently used cache records are deleted until
the available free space is greater than or equal to the specified value.
The value is specified in bytes. You can use a human-readable memory string,
e.g. `128mb`, `2gb`, etc. Units are case-insensitive.
### <a name="builder"></a> Override the configured builder instance (--builder)
Same as [`buildx --builder`](buildx.md#builder).

6
go.mod
View File

@ -16,9 +16,9 @@ require (
github.com/creack/pty v1.1.24
github.com/davecgh/go-spew v1.1.1
github.com/distribution/reference v0.6.0
github.com/docker/cli v28.3.3+incompatible
github.com/docker/cli v28.4.0+incompatible
github.com/docker/cli-docs-tool v0.10.0
github.com/docker/docker v28.3.3+incompatible
github.com/docker/docker v28.4.0+incompatible
github.com/docker/go-units v0.5.0
github.com/gofrs/flock v0.12.1
github.com/google/go-dap v0.12.0
@ -29,7 +29,7 @@ require (
github.com/hashicorp/hcl/v2 v2.23.0
github.com/in-toto/in-toto-golang v0.9.0
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/moby/buildkit v0.24.0-rc1
github.com/moby/buildkit v0.24.0
github.com/moby/go-archive v0.1.0
github.com/moby/sys/atomicwriter v0.1.0
github.com/moby/sys/mountinfo v0.7.2

12
go.sum
View File

@ -109,15 +109,15 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/docker/cli v28.3.3+incompatible h1:fp9ZHAr1WWPGdIWBM1b3zLtgCF+83gRdVMTJsUeiyAo=
github.com/docker/cli v28.3.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v28.4.0+incompatible h1:RBcf3Kjw2pMtwui5V0DIMdyeab8glEw5QY0UUU4C9kY=
github.com/docker/cli v28.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli-docs-tool v0.10.0 h1:bOD6mKynPQgojQi3s2jgcUWGp/Ebqy1SeCr9VfKQLLU=
github.com/docker/cli-docs-tool v0.10.0/go.mod h1:5EM5zPnT2E7yCLERZmrDA234Vwn09fzRHP4aX1qwp1U=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI=
github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk=
github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
@ -255,8 +255,8 @@ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZX
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/moby/buildkit v0.24.0-rc1 h1:taA+MPeYWtGyRQ1SGbdTHVk5khWKFR7f9WI2coW/Ggs=
github.com/moby/buildkit v0.24.0-rc1/go.mod h1:4qovICAdR2H4C7+EGMRva5zgHW1gyhT4/flHI7F5F9k=
github.com/moby/buildkit v0.24.0 h1:qYfTl7W1SIJzWDIDCcPT8FboHIZCYfi++wvySi3eyFE=
github.com/moby/buildkit v0.24.0/go.mod h1:4qovICAdR2H4C7+EGMRva5zgHW1gyhT4/flHI7F5F9k=
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=

View File

@ -115,28 +115,155 @@ COPY --from=base /etc/bar /bar
}
func testBuildRemote(t *testing.T, sb integration.Sandbox) {
dockerfile := []byte(`
t.Run("default branch", func(t *testing.T) {
dockerfile := []byte(`
FROM busybox:latest
COPY foo /foo
`)
dir := tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
dirDest := t.TempDir()
dir := tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
require.NoError(t, err)
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
require.NoError(t, err)
gittestutil.GitInit(git, t)
gittestutil.GitAdd(git, t, "Dockerfile", "foo")
gittestutil.GitCommit(git, t, "initial commit")
addr := gittestutil.GitServeHTTP(git, t)
gittestutil.GitInit(git, t)
gittestutil.GitAdd(git, t, "Dockerfile", "foo")
gittestutil.GitCommit(git, t, "initial commit")
addr := gittestutil.GitServeHTTP(git, t)
out, err := buildCmd(sb, withDir(dir), withArgs("--output=type=local,dest="+dirDest, addr))
require.NoError(t, err, out)
require.FileExists(t, filepath.Join(dirDest, "foo"))
out, err := buildCmd(sb, withDir(dir), withArgs("--output=type=local,dest="+dirDest, addr))
require.NoError(t, err, out)
require.FileExists(t, filepath.Join(dirDest, "foo"))
})
t.Run("tag ref with url fragment", func(t *testing.T) {
dockerfile := []byte(`
FROM busybox:latest
COPY foo /foo
`)
dir := tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
require.NoError(t, err)
gittestutil.GitInit(git, t)
gittestutil.GitAdd(git, t, "Dockerfile", "foo")
gittestutil.GitCommit(git, t, "initial commit")
gittestutil.GitTag(git, t, "v0.1.0")
addr := gittestutil.GitServeHTTP(git, t)
addr = addr + "#v0.1.0" // tag
out, err := buildCmd(sb, withDir(dir), withArgs("--output=type=local,dest="+dirDest, addr))
require.NoError(t, err, out)
require.FileExists(t, filepath.Join(dirDest, "foo"))
})
t.Run("tag ref with query string", func(t *testing.T) {
dockerfile := []byte(`
FROM busybox:latest
COPY foo /foo
`)
dir := tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
require.NoError(t, err)
gittestutil.GitInit(git, t)
gittestutil.GitAdd(git, t, "Dockerfile", "foo")
gittestutil.GitCommit(git, t, "initial commit")
gittestutil.GitTag(git, t, "v0.1.0")
addr := gittestutil.GitServeHTTP(git, t)
addr = addr + "?tag=v0.1.0" // tag
out, err := buildCmd(sb, withDir(dir), withArgs("--output=type=local,dest="+dirDest, addr))
if matchesBuildKitVersion(t, sb, ">= 0.24.0-0") {
require.NoError(t, err, out)
require.FileExists(t, filepath.Join(dirDest, "foo"))
} else {
require.Error(t, err)
require.Contains(t, out, "current frontend does not support Git URLs with query string components")
}
})
t.Run("tag ref with query string frontend 1.17", func(t *testing.T) {
dockerfile := []byte(`
# syntax=docker/dockerfile:1.17
FROM busybox:latest
COPY foo /foo
`)
dir := tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
require.NoError(t, err)
gittestutil.GitInit(git, t)
gittestutil.GitAdd(git, t, "Dockerfile", "foo")
gittestutil.GitCommit(git, t, "initial commit")
gittestutil.GitTag(git, t, "v0.1.0")
addr := gittestutil.GitServeHTTP(git, t)
addr = addr + "?tag=v0.1.0" // tag
out, err := buildCmd(sb, withDir(dir), withArgs("--output=type=local,dest="+dirDest, addr))
if matchesBuildKitVersion(t, sb, ">= 0.24.0-0") {
require.NoError(t, err, out)
require.FileExists(t, filepath.Join(dirDest, "foo"))
} else {
require.Error(t, err)
require.Contains(t, out, "current frontend does not support Git URLs with query string components")
}
})
t.Run("tag ref with query string frontend 1.18.0", func(t *testing.T) {
dockerfile := []byte(`
# syntax=docker/dockerfile-upstream:1.18.0
FROM busybox:latest
COPY foo /foo
`)
dir := tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
require.NoError(t, err)
gittestutil.GitInit(git, t)
gittestutil.GitAdd(git, t, "Dockerfile", "foo")
gittestutil.GitCommit(git, t, "initial commit")
gittestutil.GitTag(git, t, "v0.1.0")
addr := gittestutil.GitServeHTTP(git, t)
addr = addr + "?tag=v0.1.0" // tag
out, err := buildCmd(sb, withDir(dir), withArgs("--output=type=local,dest="+dirDest, addr))
if matchesBuildKitVersion(t, sb, ">= 0.24.0-0") {
require.NoError(t, err, out)
require.FileExists(t, filepath.Join(dirDest, "foo"))
} else {
require.Error(t, err)
require.Contains(t, out, "current frontend does not support Git URLs with query string components")
}
})
}
func testBuildLocalState(t *testing.T, sb integration.Sandbox) {

View File

@ -49,6 +49,7 @@ func testIntegration(t *testing.T, funcs ...func(t *testing.T, sb integration.Sa
}
}
mirroredImages["moby/buildkit:buildx-stable-1"] = buildkitImage
mirroredImages["docker/dockerfile-upstream:1.18.0"] = "docker.io/docker/dockerfile-upstream:1.18.0"
mirrors := integration.WithMirroredImages(mirroredImages)
tests := integration.TestFuncs(funcs...)

View File

@ -175,11 +175,24 @@ func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta
newMetadataSubcommand(plugin, meta),
)
cli.DisableFlagsInUseLine(cmd)
visitAll(cmd,
// prevent adding "[flags]" to the end of the usage line.
func(c *cobra.Command) { c.DisableFlagsInUseLine = true },
)
return cli.NewTopLevelCommand(cmd, dockerCli, opts, cmd.Flags())
}
// visitAll traverses all commands from the root.
func visitAll(root *cobra.Command, fns ...func(*cobra.Command)) {
for _, cmd := range root.Commands() {
visitAll(cmd, fns...)
}
for _, fn := range fns {
fn(root)
}
}
func newMetadataSubcommand(plugin *cobra.Command, meta metadata.Metadata) *cobra.Command {
if meta.ShortDescription == "" {
meta.ShortDescription = plugin.Short

View File

@ -168,34 +168,30 @@ func (tcmd *TopLevelCommand) Initialize(ops ...command.CLIOption) error {
}
// VisitAll will traverse all commands from the root.
// This is different from the VisitAll of cobra.Command where only parents
// are checked.
//
// Deprecated: this utility was only used internally and will be removed in the next release.
func VisitAll(root *cobra.Command, fn func(*cobra.Command)) {
visitAll(root, fn)
}
func visitAll(root *cobra.Command, fn func(*cobra.Command)) {
for _, cmd := range root.Commands() {
VisitAll(cmd, fn)
visitAll(cmd, fn)
}
fn(root)
}
// DisableFlagsInUseLine sets the DisableFlagsInUseLine flag on all
// commands within the tree rooted at cmd.
//
// Deprecated: this utility was only used internally and will be removed in the next release.
func DisableFlagsInUseLine(cmd *cobra.Command) {
VisitAll(cmd, func(ccmd *cobra.Command) {
visitAll(cmd, func(ccmd *cobra.Command) {
// do not add a `[flags]` to the end of the usage line.
ccmd.DisableFlagsInUseLine = true
})
}
// HasCompletionArg returns true if a cobra completion arg request is found.
func HasCompletionArg(args []string) bool {
for _, arg := range args {
if arg == cobra.ShellCompRequestCmd || arg == cobra.ShellCompNoDescRequestCmd {
return true
}
}
return false
}
var helpCommand = &cobra.Command{
Use: "help [command]",
Short: "Help about the command",

View File

@ -282,6 +282,17 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...CLIOption)
}
filterResourceAttributesEnvvar()
// early return if GODEBUG is already set or the docker context is
// the default context, i.e. is a virtual context where we won't override
// any GODEBUG values.
if v := os.Getenv("GODEBUG"); cli.currentContext == DefaultContextName || v != "" {
return nil
}
meta, err := cli.contextStore.GetMetadata(cli.currentContext)
if err == nil {
setGoDebug(meta)
}
return nil
}
@ -475,6 +486,57 @@ func (cli *DockerCli) getDockerEndPoint() (ep docker.Endpoint, err error) {
return resolveDockerEndpoint(cli.contextStore, cn)
}
// setGoDebug is an escape hatch that sets the GODEBUG environment
// variable value using docker context metadata.
//
// {
// "Name": "my-context",
// "Metadata": { "GODEBUG": "x509negativeserial=1" }
// }
//
// WARNING: Setting x509negativeserial=1 allows Go's x509 library to accept
// X.509 certificates with negative serial numbers.
// This behavior is deprecated and non-compliant with current security
// standards (RFC 5280). Accepting negative serial numbers can introduce
// serious security vulnerabilities, including the risk of certificate
// collision or bypass attacks.
// This option should only be used for legacy compatibility and never in
// production environments.
// Use at your own risk.
func setGoDebug(meta store.Metadata) {
fieldName := "GODEBUG"
godebugEnv := os.Getenv(fieldName)
// early return if GODEBUG is already set. We don't want to override what
// the user already sets.
if godebugEnv != "" {
return
}
var cfg any
var ok bool
switch m := meta.Metadata.(type) {
case DockerContext:
cfg, ok = m.AdditionalFields[fieldName]
if !ok {
return
}
case map[string]any:
cfg, ok = m[fieldName]
if !ok {
return
}
default:
return
}
v, ok := cfg.(string)
if !ok {
return
}
// set the GODEBUG environment variable with whatever was in the context
_ = os.Setenv(fieldName, v)
}
func (cli *DockerCli) initialize() error {
cli.init.Do(func() {
cli.dockerEndpoint, cli.initErr = cli.getDockerEndPoint()

View File

@ -31,11 +31,13 @@ const (
// authConfigKey is the key used to store credentials for Docker Hub. It is
// a copy of [registry.IndexServer].
//
// [registry.IndexServer]: https://pkg.go.dev/github.com/docker/docker/registry#IndexServer
// [registry.IndexServer]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/registry#IndexServer
const authConfigKey = "https://index.docker.io/v1/"
// RegistryAuthenticationPrivilegedFunc returns a RequestPrivilegeFunc from the specified registry index info
// for the given command to prompt the user for username and password.
//
// Deprecated: this function is no longer used and will be removed in the next release.
func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInfo, cmdName string) registrytypes.RequestAuthConfig {
configKey := getAuthConfigKey(index.Name)
isDefaultRegistry := configKey == authConfigKey || index.Official
@ -66,6 +68,8 @@ func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInf
//
// It is similar to [registry.ResolveAuthConfig], but uses the credentials-
// store, instead of looking up credentials from a map.
//
// [registry.ResolveAuthConfig]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/registry#ResolveAuthConfig
func ResolveAuthConfig(cfg *configfile.ConfigFile, index *registrytypes.IndexInfo) registrytypes.AuthConfig {
configKey := index.Name
if index.Official {
@ -97,23 +101,6 @@ func GetDefaultAuthConfig(cfg *configfile.ConfigFile, checkCredStore bool, serve
return registrytypes.AuthConfig(authconfig), nil
}
// ConfigureAuth handles prompting of user's username and password if needed.
//
// Deprecated: use [PromptUserForCredentials] instead.
func ConfigureAuth(ctx context.Context, cli Cli, flUser, flPassword string, authConfig *registrytypes.AuthConfig, _ bool) error {
defaultUsername := authConfig.Username
serverAddress := authConfig.ServerAddress
newAuthConfig, err := PromptUserForCredentials(ctx, cli, flUser, flPassword, defaultUsername, serverAddress)
if err != nil {
return err
}
authConfig.Username = newAuthConfig.Username
authConfig.Password = newAuthConfig.Password
return nil
}
// PromptUserForCredentials handles the CLI prompt for the user to input
// credentials.
// If argUser is not empty, then the user is only prompted for their password.
@ -209,47 +196,38 @@ func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword
}, nil
}
// RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete
// image. The auth configuration is serialized as a base64url encoded RFC4648,
// section 5) JSON string for sending through the X-Registry-Auth header.
// RetrieveAuthTokenFromImage retrieves an encoded auth token given a
// complete image reference. The auth configuration is serialized as a
// base64url encoded ([RFC 4648, Section 5]) JSON string for sending through
// the "X-Registry-Auth" header.
//
// For details on base64url encoding, see:
// - RFC4648, section 5: https://tools.ietf.org/html/rfc4648#section-5
// [RFC 4648, Section 5]: https://tools.ietf.org/html/rfc4648#section-5
func RetrieveAuthTokenFromImage(cfg *configfile.ConfigFile, image string) (string, error) {
// Retrieve encoded auth token from the image reference
authConfig, err := resolveAuthConfigFromImage(cfg, image)
registryRef, err := reference.ParseNormalizedNamed(image)
if err != nil {
return "", err
}
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
configKey := getAuthConfigKey(reference.Domain(registryRef))
authConfig, err := cfg.GetAuthConfig(configKey)
if err != nil {
return "", err
}
encodedAuth, err := registrytypes.EncodeAuthConfig(registrytypes.AuthConfig(authConfig))
if err != nil {
return "", err
}
return encodedAuth, nil
}
// resolveAuthConfigFromImage retrieves that AuthConfig using the image string
func resolveAuthConfigFromImage(cfg *configfile.ConfigFile, image string) (registrytypes.AuthConfig, error) {
registryRef, err := reference.ParseNormalizedNamed(image)
if err != nil {
return registrytypes.AuthConfig{}, err
}
configKey := getAuthConfigKey(reference.Domain(registryRef))
a, err := cfg.GetAuthConfig(configKey)
if err != nil {
return registrytypes.AuthConfig{}, err
}
return registrytypes.AuthConfig(a), nil
}
// getAuthConfigKey special-cases using the full index address of the official
// index as the AuthConfig key, and uses the (host)name[:port] for private indexes.
//
// It is similar to [registry.GetAuthConfigKey], but does not require on
// [registrytypes.IndexInfo] as intermediate.
//
// [registry.GetAuthConfigKey]: https://pkg.go.dev/github.com/docker/docker/registry#GetAuthConfigKey
// [registrytypes.IndexInfo]:https://pkg.go.dev/github.com/docker/docker/api/types/registry#IndexInfo
// [registry.GetAuthConfigKey]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/registry#GetAuthConfigKey
// [registrytypes.IndexInfo]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/api/types/registry#IndexInfo
func getAuthConfigKey(domainName string) string {
if domainName == "docker.io" || domainName == "index.docker.io" {
return authConfigKey

View File

@ -1,15 +0,0 @@
package command
import (
"github.com/spf13/pflag"
)
// AddTrustVerificationFlags adds content trust flags to the provided flagset
func AddTrustVerificationFlags(fs *pflag.FlagSet, v *bool, trusted bool) {
fs.BoolVar(v, "disable-content-trust", !trusted, "Skip image verification")
}
// AddTrustSigningFlags adds "signing" flags to the provided flagset
func AddTrustSigningFlags(fs *pflag.FlagSet, v *bool, trusted bool) {
fs.BoolVar(v, "disable-content-trust", !trusted, "Skip image signing")
}

View File

@ -14,30 +14,20 @@ import (
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/internal/prompt"
"github.com/docker/docker/api/types/filters"
"github.com/moby/sys/atomicwriter"
"github.com/pkg/errors"
"github.com/spf13/pflag"
)
// CopyToFile writes the content of the reader to the specified file
// ErrPromptTerminated is returned if the user terminated the prompt.
//
// Deprecated: use [atomicwriter.New].
func CopyToFile(outfile string, r io.Reader) error {
writer, err := atomicwriter.New(outfile, 0o600)
if err != nil {
return err
}
defer writer.Close()
_, err = io.Copy(writer, r)
return err
}
// Deprecated: this error is for internal use and will be removed in the next release.
const ErrPromptTerminated = prompt.ErrTerminated
// DisableInputEcho disables input echo on the provided streams.In.
// This is useful when the user provides sensitive information like passwords.
// The function returns a restore function that should be called to restore the
// terminal state.
//
// Deprecated: this function is for internal use and will be removed in the next release.
func DisableInputEcho(ins *streams.In) (restore func() error, err error) {
return prompt.DisableInputEcho(ins)
}
@ -49,6 +39,8 @@ func DisableInputEcho(ins *streams.In) (restore func() error, err error) {
// When the prompt returns an error, the caller should propagate the error up
// the stack and close the io.Reader used for the prompt which will prevent the
// background goroutine from blocking indefinitely.
//
// Deprecated: this function is for internal use and will be removed in the next release.
func PromptForInput(ctx context.Context, in io.Reader, out io.Writer, message string) (string, error) {
return prompt.ReadInput(ctx, in, out, message)
}
@ -63,6 +55,8 @@ func PromptForInput(ctx context.Context, in io.Reader, out io.Writer, message st
// When the prompt returns an error, the caller should propagate the error up
// the stack and close the io.Reader used for the prompt which will prevent the
// background goroutine from blocking indefinitely.
//
// Deprecated: this function is for internal use and will be removed in the next release.
func PromptForConfirmation(ctx context.Context, ins io.Reader, outs io.Writer, message string) (bool, error) {
return prompt.Confirm(ctx, ins, outs, message)
}
@ -108,12 +102,6 @@ func PruneFilters(dockerCLI config.Provider, pruneFilters filters.Args) filters.
return pruneFilters
}
// AddPlatformFlag adds `platform` to a set of flags for API version 1.32 and later.
func AddPlatformFlag(flags *pflag.FlagSet, target *string) {
flags.StringVar(target, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
_ = flags.SetAnnotation("platform", "version", []string{"1.32"})
}
// ValidateOutputPath validates the output paths of the "docker cp" command.
func ValidateOutputPath(path string) error {
dir := filepath.Dir(filepath.Clean(path))

View File

@ -7,8 +7,8 @@ type AuthConfig struct {
Auth string `json:"auth,omitempty"`
// Email is an optional value associated with the username.
// This field is deprecated and will be removed in a later
// version of docker.
//
// Deprecated: This field is deprecated since docker 1.11 (API v1.23) and will be removed in the next release.
Email string `json:"email,omitempty"`
ServerAddress string `json:"serveraddress,omitempty"`

View File

@ -1,9 +1,9 @@
package store
import cerrdefs "github.com/containerd/errdefs"
import "github.com/containerd/errdefs"
func invalidParameter(err error) error {
if err == nil || cerrdefs.IsInvalidArgument(err) {
if err == nil || errdefs.IsInvalidArgument(err) {
return err
}
return invalidParameterErr{err}
@ -14,7 +14,7 @@ type invalidParameterErr struct{ error }
func (invalidParameterErr) InvalidParameter() {}
func notFound(err error) error {
if err == nil || cerrdefs.IsNotFound(err) {
if err == nil || errdefs.IsNotFound(err) {
return err
}
return notFoundErr{err}

View File

@ -1,12 +1,12 @@
package flags
import (
"errors"
"fmt"
"os"
"path/filepath"
"github.com/docker/cli/cli/config"
"github.com/docker/cli/opts"
"github.com/docker/docker/client"
"github.com/docker/go-connections/tlsconfig"
"github.com/sirupsen/logrus"
@ -54,6 +54,39 @@ var (
dockerTLS = os.Getenv(EnvEnableTLS) != ""
)
// hostVar is used for the '--host' / '-H' flag to set [ClientOptions.Hosts].
// The [ClientOptions.Hosts] field is a slice because it was originally shared
// with the daemon config. However, the CLI only allows for a single host to
// be specified.
//
// hostVar presents itself as a "string", but stores the value in a string
// slice. It produces an error when trying to set multiple values, matching
// the check in [getServerHost].
//
// [getServerHost]: https://github.com/docker/cli/blob/7eab668982645def1cd46fe1b60894cba6fd17a4/cli/command/cli.go#L542-L551
type hostVar struct {
dst *[]string
set bool
}
func (h *hostVar) String() string {
if h.dst == nil || len(*h.dst) == 0 {
return ""
}
return (*h.dst)[0]
}
func (h *hostVar) Set(s string) error {
if h.set {
return errors.New("specify only one -H")
}
*h.dst = []string{s}
h.set = true
return nil
}
func (*hostVar) Type() string { return "string" }
// ClientOptions are the options used to configure the client cli.
type ClientOptions struct {
Debug bool
@ -90,13 +123,13 @@ func (o *ClientOptions) InstallFlags(flags *pflag.FlagSet) {
KeyFile: filepath.Join(dockerCertPath, DefaultKeyFile),
}
tlsOptions := o.TLSOptions
flags.Var(opts.NewQuotedString(&tlsOptions.CAFile), "tlscacert", "Trust certs signed only by this CA")
flags.Var(opts.NewQuotedString(&tlsOptions.CertFile), "tlscert", "Path to TLS certificate file")
flags.Var(opts.NewQuotedString(&tlsOptions.KeyFile), "tlskey", "Path to TLS key file")
flags.Var(&quotedString{&tlsOptions.CAFile}, "tlscacert", "Trust certs signed only by this CA")
flags.Var(&quotedString{&tlsOptions.CertFile}, "tlscert", "Path to TLS certificate file")
flags.Var(&quotedString{&tlsOptions.KeyFile}, "tlskey", "Path to TLS key file")
// opts.ValidateHost is not used here, so as to allow connection helpers
hostOpt := opts.NewNamedListOptsRef("hosts", &o.Hosts, nil)
flags.VarP(hostOpt, "host", "H", "Daemon socket to connect to")
// TODO(thaJeztah): show the default host.
// TODO(thaJeztah): this should be a string, not an "array" as we only allow a single host.
flags.VarP(&hostVar{dst: &o.Hosts}, "host", "H", "Daemon socket to connect to")
flags.StringVarP(&o.Context, "context", "c", "",
`Name of the context to use to connect to the daemon (overrides `+client.EnvOverrideHost+` env var and default context set with "docker context use")`)
}
@ -146,3 +179,33 @@ func SetLogLevel(logLevel string) {
logrus.SetLevel(logrus.InfoLevel)
}
}
type quotedString struct {
value *string
}
func (s *quotedString) Set(val string) error {
*s.value = trimQuotes(val)
return nil
}
func (*quotedString) Type() string {
return "string"
}
func (s *quotedString) String() string {
return *s.value
}
func trimQuotes(value string) string {
if len(value) < 2 {
return value
}
lastIndex := len(value) - 1
for _, char := range []byte{'\'', '"'} {
if value[0] == char && value[lastIndex] == char {
return value[1:lastIndex]
}
}
return value
}

View File

@ -7,13 +7,14 @@ import (
)
// ValidateEnv validates an environment variable and returns it.
// If no value is specified, it obtains its value from the current environment
// If no value is specified, it obtains its value from the current environment.
//
// As on ParseEnvFile and related to #16585, environment variable names
// are not validated, and it's up to the application inside the container
// to validate them or not.
// Environment variable names are not validated, and it's up to the application
// inside the container to validate them (see [moby-16585]). The only validation
// here is to check if name is empty, per [moby-25099].
//
// The only validation here is to check if name is empty, per #25099
// [moby-16585]: https://github.com/moby/moby/issues/16585
// [moby-25099]: https://github.com/moby/moby/issues/25099
func ValidateEnv(val string) (string, error) {
k, _, hasValue := strings.Cut(val, "=")
if k == "" {

View File

@ -1,24 +0,0 @@
package opts
import (
"os"
"github.com/docker/cli/pkg/kvfile"
)
// ParseEnvFile reads a file with environment variables enumerated by lines
//
// “Environment variable names used by the utilities in the Shell and
// Utilities volume of IEEE Std 1003.1-2001 consist solely of uppercase
// letters, digits, and the '_' (underscore) from the characters defined in
// Portable Character Set and do not begin with a digit. *But*, other
// characters may be permitted by an implementation; applications shall
// tolerate the presence of such names.”
// -- http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
//
// As of #16585, it's up to application inside docker to validate or not
// environment variables, that's why we just strip leading whitespace and
// nothing more.
func ParseEnvFile(filename string) ([]string, error) {
return kvfile.Parse(filename, os.LookupEnv)
}

View File

@ -0,0 +1,14 @@
package opts
import (
"os"
"github.com/docker/cli/pkg/kvfile"
)
// ParseEnvFile reads a file with environment variables enumerated by lines
//
// Deprecated: use [kvfile.Parse] and pass [os.LookupEnv] to lookup env-vars from the current environment.
func ParseEnvFile(filename string) ([]string, error) {
return kvfile.Parse(filename, os.LookupEnv)
}

View File

@ -34,7 +34,7 @@ const (
// ValidateHost validates that the specified string is a valid host and returns it.
//
// TODO(thaJeztah): ValidateHost appears to be unused; deprecate it.
// Deprecated: this function is no longer used, and will be removed in the next release.
func ValidateHost(val string) (string, error) {
host := strings.TrimSpace(val)
// The empty string means default and is not handled by parseDockerDaemonHost

View File

@ -134,6 +134,8 @@ func (opts *ListOpts) WithValidator(validator ValidatorFctType) *ListOpts {
// NamedOption is an interface that list and map options
// with names implement.
//
// Deprecated: NamedOption is no longer used and will be removed in the next release.
type NamedOption interface {
Name() string
}
@ -141,6 +143,8 @@ type NamedOption interface {
// NamedListOpts is a ListOpts with a configuration name.
// This struct is useful to keep reference to the assigned
// field name in the internal configuration struct.
//
// Deprecated: NamedListOpts is no longer used and will be removed in the next release.
type NamedListOpts struct {
name string
ListOpts
@ -149,6 +153,8 @@ type NamedListOpts struct {
var _ NamedOption = &NamedListOpts{}
// NewNamedListOptsRef creates a reference to a new NamedListOpts struct.
//
// Deprecated: NewNamedListOptsRef is no longer used and will be removed in the next release.
func NewNamedListOptsRef(name string, values *[]string, validator ValidatorFctType) *NamedListOpts {
return &NamedListOpts{
name: name,
@ -157,6 +163,8 @@ func NewNamedListOptsRef(name string, values *[]string, validator ValidatorFctTy
}
// Name returns the name of the NamedListOpts in the configuration.
//
// Deprecated: NamedListOpts is no longer used and will be removed in the next release.
func (o *NamedListOpts) Name() string {
return o.name
}
@ -210,6 +218,8 @@ func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts {
// NamedMapOpts is a MapOpts struct with a configuration name.
// This struct is useful to keep reference to the assigned
// field name in the internal configuration struct.
//
// Deprecated: NamedMapOpts is no longer used and will be removed in the next release.
type NamedMapOpts struct {
name string
MapOpts
@ -218,6 +228,8 @@ type NamedMapOpts struct {
var _ NamedOption = &NamedMapOpts{}
// NewNamedMapOpts creates a reference to a new NamedMapOpts struct.
//
// Deprecated: NamedMapOpts is no longer used and will be removed in the next release.
func NewNamedMapOpts(name string, values map[string]string, validator ValidatorFctType) *NamedMapOpts {
return &NamedMapOpts{
name: name,
@ -226,6 +238,8 @@ func NewNamedMapOpts(name string, values map[string]string, validator ValidatorF
}
// Name returns the name of the NamedMapOpts in the configuration.
//
// Deprecated: NamedMapOpts is no longer used and will be removed in the next release.
func (o *NamedMapOpts) Name() string {
return o.name
}

View File

@ -2,6 +2,8 @@ package opts
// QuotedString is a string that may have extra quotes around the value. The
// quotes are stripped from the value.
//
// Deprecated: This option type is no longer used and will be removed in the next release.
type QuotedString struct {
value *string
}
@ -35,6 +37,8 @@ func trimQuotes(value string) string {
}
// NewQuotedString returns a new quoted string option
//
// Deprecated: This option type is no longer used and will be removed in the next release.
func NewQuotedString(value *string) *QuotedString {
return &QuotedString{value: value}
}

View File

@ -1608,6 +1608,8 @@ definitions:
Bridge:
description: |
Name of the default bridge interface when dockerd's --bridge flag is set.
Deprecated: This field is only set when the daemon is started with the --bridge flag specified.
type: "string"
example: "docker0"
SandboxID:
@ -2234,6 +2236,10 @@ definitions:
password:
type: "string"
email:
description: |
Email is an optional value associated with the username.
> **Deprecated**: This field is deprecated since docker 1.11 (API v1.23) and will be removed in a future release.
type: "string"
serveraddress:
type: "string"
@ -4392,6 +4398,7 @@ definitions:
A counter that triggers an update even if no relevant parameters have
been changed.
type: "integer"
format: "uint64"
Runtime:
description: |
Runtime is the type of runtime specified for the task executor.

View File

@ -1,6 +1,8 @@
package build
// CacheDiskUsage contains disk usage for the build cache.
//
// Deprecated: this type is no longer used and will be removed in the next release.
type CacheDiskUsage struct {
TotalSize int64
Reclaimable int64

View File

@ -1,6 +1,8 @@
package container
// DiskUsage contains disk usage for containers.
//
// Deprecated: this type is no longer used and will be removed in the next release.
type DiskUsage struct {
TotalSize int64
Reclaimable int64

View File

@ -13,8 +13,11 @@ type NetworkSettings struct {
}
// NetworkSettingsBase holds networking state for a container when inspecting it.
//
// Deprecated: Most fields in NetworkSettingsBase are deprecated. Fields which aren't deprecated will move to
// NetworkSettings in v29.0, and this struct will be removed.
type NetworkSettingsBase struct {
Bridge string // Bridge contains the name of the default bridge interface iff it was set through the daemon --bridge flag.
Bridge string // Deprecated: This field is only set when the daemon is started with the --bridge flag specified.
SandboxID string // SandboxID uniquely represents a container's network stack
SandboxKey string // SandboxKey identifies the sandbox
Ports nat.PortMap // Ports is a collection of PortBinding indexed by Port
@ -35,18 +38,44 @@ type NetworkSettingsBase struct {
SecondaryIPv6Addresses []network.Address // Deprecated: This field is never set and will be removed in a future release.
}
// DefaultNetworkSettings holds network information
// during the 2 release deprecation period.
// It will be removed in Docker 1.11.
// DefaultNetworkSettings holds the networking state for the default bridge, if the container is connected to that
// network.
//
// Deprecated: this struct is deprecated since Docker v1.11 and will be removed in v29. You should look for the default
// network in NetworkSettings.Networks instead.
type DefaultNetworkSettings struct {
EndpointID string // EndpointID uniquely represents a service endpoint in a Sandbox
Gateway string // Gateway holds the gateway address for the network
GlobalIPv6Address string // GlobalIPv6Address holds network's global IPv6 address
GlobalIPv6PrefixLen int // GlobalIPv6PrefixLen represents mask length of network's global IPv6 address
IPAddress string // IPAddress holds the IPv4 address for the network
IPPrefixLen int // IPPrefixLen represents mask length of network's IPv4 address
IPv6Gateway string // IPv6Gateway holds gateway address specific for IPv6
MacAddress string // MacAddress holds the MAC address for the network
// EndpointID uniquely represents a service endpoint in a Sandbox
//
// Deprecated: This field will be removed in v29. You should look for the default network in NetworkSettings.Networks instead.
EndpointID string
// Gateway holds the gateway address for the network
//
// Deprecated: This field will be removed in v29. You should look for the default network in NetworkSettings.Networks instead.
Gateway string
// GlobalIPv6Address holds network's global IPv6 address
//
// Deprecated: This field will be removed in v29. You should look for the default network in NetworkSettings.Networks instead.
GlobalIPv6Address string
// GlobalIPv6PrefixLen represents mask length of network's global IPv6 address
//
// Deprecated: This field will be removed in v29. You should look for the default network in NetworkSettings.Networks instead.
GlobalIPv6PrefixLen int
// IPAddress holds the IPv4 address for the network
//
// Deprecated: This field will be removed in v29. You should look for the default network in NetworkSettings.Networks instead.
IPAddress string
// IPPrefixLen represents mask length of network's IPv4 address
//
// Deprecated: This field will be removed in v29. You should look for the default network in NetworkSettings.Networks instead.
IPPrefixLen int
// IPv6Gateway holds gateway address specific for IPv6
//
// Deprecated: This field will be removed in v29. You should look for the default network in NetworkSettings.Networks instead.
IPv6Gateway string
// MacAddress holds the MAC address for the network
//
// Deprecated: This field will be removed in v29. You should look for the default network in NetworkSettings.Networks instead.
MacAddress string
}
// NetworkSettingsSummary provides a summary of container's networks

View File

@ -0,0 +1,61 @@
package filters
import (
"encoding/json"
"github.com/docker/docker/api/types/versions"
)
// ToParamWithVersion encodes Args as a JSON string. If version is less than 1.22
// then the encoded format will use an older legacy format where the values are a
// list of strings, instead of a set.
//
// Deprecated: do not use in any new code; use ToJSON instead
func ToParamWithVersion(version string, a Args) (string, error) {
out, err := ToJSON(a)
if out == "" || err != nil {
return "", nil
}
if version != "" && versions.LessThan(version, "1.22") {
return encodeLegacyFilters(out)
}
return out, nil
}
// encodeLegacyFilters encodes Args in the legacy format as used in API v1.21 and older.
// where values are a list of strings, instead of a set.
//
// Don't use in any new code; use [filters.ToJSON]] instead.
func encodeLegacyFilters(currentFormat string) (string, error) {
// The Args.fields field is not exported, but used to marshal JSON,
// so we'll marshal to the new format, then unmarshal to get the
// fields, and marshal again.
//
// This is far from optimal, but this code is only used for deprecated
// API versions, so should not be hit commonly.
var argsFields map[string]map[string]bool
err := json.Unmarshal([]byte(currentFormat), &argsFields)
if err != nil {
return "", err
}
buf, err := json.Marshal(convertArgsToSlice(argsFields))
if err != nil {
return "", err
}
return string(buf), nil
}
func convertArgsToSlice(f map[string]map[string]bool) map[string][]string {
m := map[string][]string{}
for k, v := range f {
values := []string{}
for kk := range v {
if v[kk] {
values = append(values, kk)
}
}
m[k] = values
}
return m
}

View File

@ -8,8 +8,6 @@ import (
"encoding/json"
"regexp"
"strings"
"github.com/docker/docker/api/types/versions"
)
// Args stores a mapping of keys to a set of multiple values.
@ -63,24 +61,6 @@ func ToJSON(a Args) (string, error) {
return string(buf), err
}
// ToParamWithVersion encodes Args as a JSON string. If version is less than 1.22
// then the encoded format will use an older legacy format where the values are a
// list of strings, instead of a set.
//
// Deprecated: do not use in any new code; use ToJSON instead
func ToParamWithVersion(version string, a Args) (string, error) {
if a.Len() == 0 {
return "", nil
}
if version != "" && versions.LessThan(version, "1.22") {
buf, err := json.Marshal(convertArgsToSlice(a.fields))
return string(buf), err
}
return ToJSON(a)
}
// FromJSON decodes a JSON encoded string into Args
func FromJSON(p string) (Args, error) {
args := NewArgs()
@ -320,17 +300,3 @@ func deprecatedArgs(d map[string][]string) map[string]map[string]bool {
}
return m
}
func convertArgsToSlice(f map[string]map[string]bool) map[string][]string {
m := map[string][]string{}
for k, v := range f {
values := []string{}
for kk := range v {
if v[kk] {
values = append(values, kk)
}
}
m[k] = values
}
return m
}

View File

@ -1,6 +1,8 @@
package image
// DiskUsage contains disk usage for images.
//
// Deprecated: this type is no longer used and will be removed in the next release.
type DiskUsage struct {
TotalSize int64
Reclaimable int64

View File

@ -4,8 +4,6 @@ import (
"errors"
"fmt"
"net"
"github.com/docker/docker/internal/multierror"
)
// EndpointSettings stores the network endpoint details
@ -99,7 +97,7 @@ func (cfg *EndpointIPAMConfig) IsInRange(v4Subnets []NetworkSubnet, v6Subnets []
errs = append(errs, err)
}
return multierror.Join(errs...)
return errJoin(errs...)
}
func validateEndpointIPAddress(epAddr string, ipamSubnets []NetworkSubnet) error {
@ -149,5 +147,5 @@ func (cfg *EndpointIPAMConfig) Validate() error {
}
}
return multierror.Join(errs...)
return errJoin(errs...)
}

View File

@ -4,8 +4,7 @@ import (
"errors"
"fmt"
"net/netip"
"github.com/docker/docker/internal/multierror"
"strings"
)
// IPAM represents IP Address Management
@ -72,7 +71,7 @@ func ValidateIPAM(ipam *IPAM, enableIPv6 bool) error {
}
}
if err := multierror.Join(errs...); err != nil {
if err := errJoin(errs...); err != nil {
return fmt.Errorf("invalid network config:\n%w", err)
}
@ -132,3 +131,43 @@ func validateAddress(address string, subnet netip.Prefix, subnetFamily ipFamily)
return nil
}
func errJoin(errs ...error) error {
n := 0
for _, err := range errs {
if err != nil {
n++
}
}
if n == 0 {
return nil
}
e := &joinError{
errs: make([]error, 0, n),
}
for _, err := range errs {
if err != nil {
e.errs = append(e.errs, err)
}
}
return e
}
type joinError struct {
errs []error
}
func (e *joinError) Error() string {
if len(e.errs) == 1 {
return strings.TrimSpace(e.errs[0].Error())
}
stringErrs := make([]string, 0, len(e.errs))
for _, subErr := range e.errs {
stringErrs = append(stringErrs, strings.ReplaceAll(subErr.Error(), "\n", "\n\t"))
}
return "* " + strings.Join(stringErrs, "\n* ")
}
func (e *joinError) Unwrap() []error {
return e.errs
}

View File

@ -32,8 +32,8 @@ type AuthConfig struct {
Auth string `json:"auth,omitempty"`
// Email is an optional value associated with the username.
// This field is deprecated and will be removed in a later
// version of docker.
//
// Deprecated: This field is deprecated since docker 1.11 (API v1.23) and will be removed in the next release.
Email string `json:"email,omitempty"`
ServerAddress string `json:"serveraddress,omitempty"`

View File

@ -1,5 +1,7 @@
package swarm
import "github.com/docker/docker/api/types/swarm/runtime"
// RuntimeType is the type of runtime used for the TaskSpec
type RuntimeType string
@ -25,3 +27,11 @@ const (
type NetworkAttachmentSpec struct {
ContainerID string
}
// RuntimeSpec defines the base payload which clients can specify for creating
// a service with the plugin runtime.
type RuntimeSpec = runtime.PluginSpec
// RuntimePrivilege describes a permission the user has to accept
// upon installing a plugin.
type RuntimePrivilege = runtime.PluginPrivilege

View File

@ -1,3 +0,0 @@
//go:generate protoc --gogofaster_out=import_path=github.com/docker/docker/api/types/swarm/runtime:. plugin.proto
package runtime

View File

@ -1,808 +0,0 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: plugin.proto
package runtime
import (
fmt "fmt"
proto "github.com/gogo/protobuf/proto"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
// PluginSpec defines the base payload which clients can specify for creating
// a service with the plugin runtime.
type PluginSpec struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Remote string `protobuf:"bytes,2,opt,name=remote,proto3" json:"remote,omitempty"`
Privileges []*PluginPrivilege `protobuf:"bytes,3,rep,name=privileges,proto3" json:"privileges,omitempty"`
Disabled bool `protobuf:"varint,4,opt,name=disabled,proto3" json:"disabled,omitempty"`
Env []string `protobuf:"bytes,5,rep,name=env,proto3" json:"env,omitempty"`
}
func (m *PluginSpec) Reset() { *m = PluginSpec{} }
func (m *PluginSpec) String() string { return proto.CompactTextString(m) }
func (*PluginSpec) ProtoMessage() {}
func (*PluginSpec) Descriptor() ([]byte, []int) {
return fileDescriptor_22a625af4bc1cc87, []int{0}
}
func (m *PluginSpec) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *PluginSpec) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_PluginSpec.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *PluginSpec) XXX_Merge(src proto.Message) {
xxx_messageInfo_PluginSpec.Merge(m, src)
}
func (m *PluginSpec) XXX_Size() int {
return m.Size()
}
func (m *PluginSpec) XXX_DiscardUnknown() {
xxx_messageInfo_PluginSpec.DiscardUnknown(m)
}
var xxx_messageInfo_PluginSpec proto.InternalMessageInfo
func (m *PluginSpec) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *PluginSpec) GetRemote() string {
if m != nil {
return m.Remote
}
return ""
}
func (m *PluginSpec) GetPrivileges() []*PluginPrivilege {
if m != nil {
return m.Privileges
}
return nil
}
func (m *PluginSpec) GetDisabled() bool {
if m != nil {
return m.Disabled
}
return false
}
func (m *PluginSpec) GetEnv() []string {
if m != nil {
return m.Env
}
return nil
}
// PluginPrivilege describes a permission the user has to accept
// upon installing a plugin.
type PluginPrivilege struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
Value []string `protobuf:"bytes,3,rep,name=value,proto3" json:"value,omitempty"`
}
func (m *PluginPrivilege) Reset() { *m = PluginPrivilege{} }
func (m *PluginPrivilege) String() string { return proto.CompactTextString(m) }
func (*PluginPrivilege) ProtoMessage() {}
func (*PluginPrivilege) Descriptor() ([]byte, []int) {
return fileDescriptor_22a625af4bc1cc87, []int{1}
}
func (m *PluginPrivilege) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *PluginPrivilege) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_PluginPrivilege.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *PluginPrivilege) XXX_Merge(src proto.Message) {
xxx_messageInfo_PluginPrivilege.Merge(m, src)
}
func (m *PluginPrivilege) XXX_Size() int {
return m.Size()
}
func (m *PluginPrivilege) XXX_DiscardUnknown() {
xxx_messageInfo_PluginPrivilege.DiscardUnknown(m)
}
var xxx_messageInfo_PluginPrivilege proto.InternalMessageInfo
func (m *PluginPrivilege) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *PluginPrivilege) GetDescription() string {
if m != nil {
return m.Description
}
return ""
}
func (m *PluginPrivilege) GetValue() []string {
if m != nil {
return m.Value
}
return nil
}
func init() {
proto.RegisterType((*PluginSpec)(nil), "PluginSpec")
proto.RegisterType((*PluginPrivilege)(nil), "PluginPrivilege")
}
func init() { proto.RegisterFile("plugin.proto", fileDescriptor_22a625af4bc1cc87) }
var fileDescriptor_22a625af4bc1cc87 = []byte{
// 225 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0xc8, 0x29, 0x4d,
0xcf, 0xcc, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x57, 0x9a, 0xc1, 0xc8, 0xc5, 0x15, 0x00, 0x16,
0x08, 0x2e, 0x48, 0x4d, 0x16, 0x12, 0xe2, 0x62, 0xc9, 0x4b, 0xcc, 0x4d, 0x95, 0x60, 0x54, 0x60,
0xd4, 0xe0, 0x0c, 0x02, 0xb3, 0x85, 0xc4, 0xb8, 0xd8, 0x8a, 0x52, 0x73, 0xf3, 0x4b, 0x52, 0x25,
0x98, 0xc0, 0xa2, 0x50, 0x9e, 0x90, 0x01, 0x17, 0x57, 0x41, 0x51, 0x66, 0x59, 0x66, 0x4e, 0x6a,
0x7a, 0x6a, 0xb1, 0x04, 0xb3, 0x02, 0xb3, 0x06, 0xb7, 0x91, 0x80, 0x1e, 0xc4, 0xb0, 0x00, 0x98,
0x44, 0x10, 0x92, 0x1a, 0x21, 0x29, 0x2e, 0x8e, 0x94, 0xcc, 0xe2, 0xc4, 0xa4, 0x9c, 0xd4, 0x14,
0x09, 0x16, 0x05, 0x46, 0x0d, 0x8e, 0x20, 0x38, 0x5f, 0x48, 0x80, 0x8b, 0x39, 0x35, 0xaf, 0x4c,
0x82, 0x55, 0x81, 0x59, 0x83, 0x33, 0x08, 0xc4, 0x54, 0x8a, 0xe5, 0xe2, 0x47, 0x33, 0x0c, 0xab,
0xf3, 0x14, 0xb8, 0xb8, 0x53, 0x52, 0x8b, 0x93, 0x8b, 0x32, 0x0b, 0x4a, 0x32, 0xf3, 0xf3, 0xa0,
0x6e, 0x44, 0x16, 0x12, 0x12, 0xe1, 0x62, 0x2d, 0x4b, 0xcc, 0x29, 0x4d, 0x05, 0xbb, 0x91, 0x33,
0x08, 0xc2, 0x71, 0x92, 0x38, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4,
0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x24, 0x36,
0x70, 0xd0, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x37, 0xea, 0xe2, 0xca, 0x2a, 0x01, 0x00,
0x00,
}
func (m *PluginSpec) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *PluginSpec) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *PluginSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Env) > 0 {
for iNdEx := len(m.Env) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.Env[iNdEx])
copy(dAtA[i:], m.Env[iNdEx])
i = encodeVarintPlugin(dAtA, i, uint64(len(m.Env[iNdEx])))
i--
dAtA[i] = 0x2a
}
}
if m.Disabled {
i--
if m.Disabled {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x20
}
if len(m.Privileges) > 0 {
for iNdEx := len(m.Privileges) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Privileges[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintPlugin(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
}
}
if len(m.Remote) > 0 {
i -= len(m.Remote)
copy(dAtA[i:], m.Remote)
i = encodeVarintPlugin(dAtA, i, uint64(len(m.Remote)))
i--
dAtA[i] = 0x12
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintPlugin(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *PluginPrivilege) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *PluginPrivilege) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *PluginPrivilege) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Value) > 0 {
for iNdEx := len(m.Value) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.Value[iNdEx])
copy(dAtA[i:], m.Value[iNdEx])
i = encodeVarintPlugin(dAtA, i, uint64(len(m.Value[iNdEx])))
i--
dAtA[i] = 0x1a
}
}
if len(m.Description) > 0 {
i -= len(m.Description)
copy(dAtA[i:], m.Description)
i = encodeVarintPlugin(dAtA, i, uint64(len(m.Description)))
i--
dAtA[i] = 0x12
}
if len(m.Name) > 0 {
i -= len(m.Name)
copy(dAtA[i:], m.Name)
i = encodeVarintPlugin(dAtA, i, uint64(len(m.Name)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintPlugin(dAtA []byte, offset int, v uint64) int {
offset -= sovPlugin(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *PluginSpec) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovPlugin(uint64(l))
}
l = len(m.Remote)
if l > 0 {
n += 1 + l + sovPlugin(uint64(l))
}
if len(m.Privileges) > 0 {
for _, e := range m.Privileges {
l = e.Size()
n += 1 + l + sovPlugin(uint64(l))
}
}
if m.Disabled {
n += 2
}
if len(m.Env) > 0 {
for _, s := range m.Env {
l = len(s)
n += 1 + l + sovPlugin(uint64(l))
}
}
return n
}
func (m *PluginPrivilege) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Name)
if l > 0 {
n += 1 + l + sovPlugin(uint64(l))
}
l = len(m.Description)
if l > 0 {
n += 1 + l + sovPlugin(uint64(l))
}
if len(m.Value) > 0 {
for _, s := range m.Value {
l = len(s)
n += 1 + l + sovPlugin(uint64(l))
}
}
return n
}
func sovPlugin(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozPlugin(x uint64) (n int) {
return sovPlugin(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *PluginSpec) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPlugin
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: PluginSpec: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: PluginSpec: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPlugin
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthPlugin
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthPlugin
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Remote", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPlugin
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthPlugin
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthPlugin
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Remote = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Privileges", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPlugin
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthPlugin
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthPlugin
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Privileges = append(m.Privileges, &PluginPrivilege{})
if err := m.Privileges[len(m.Privileges)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 4:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Disabled", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPlugin
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.Disabled = bool(v != 0)
case 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Env", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPlugin
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthPlugin
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthPlugin
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Env = append(m.Env, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipPlugin(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthPlugin
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *PluginPrivilege) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPlugin
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: PluginPrivilege: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: PluginPrivilege: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPlugin
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthPlugin
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthPlugin
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Name = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPlugin
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthPlugin
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthPlugin
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Description = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPlugin
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthPlugin
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthPlugin
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Value = append(m.Value, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipPlugin(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthPlugin
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipPlugin(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowPlugin
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowPlugin
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowPlugin
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthPlugin
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupPlugin
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthPlugin
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthPlugin = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowPlugin = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupPlugin = fmt.Errorf("proto: unexpected end of group")
)

View File

@ -1,19 +0,0 @@
syntax = "proto3";
// PluginSpec defines the base payload which clients can specify for creating
// a service with the plugin runtime.
message PluginSpec {
string name = 1;
string remote = 2;
repeated PluginPrivilege privileges = 3;
bool disabled = 4;
repeated string env = 5;
}
// PluginPrivilege describes a permission the user has to accept
// upon installing a plugin.
message PluginPrivilege {
string name = 1;
string description = 2;
repeated string value = 3;
}

View File

@ -0,0 +1,27 @@
package runtime
import "fmt"
// PluginSpec defines the base payload which clients can specify for creating
// a service with the plugin runtime.
type PluginSpec struct {
Name string `json:"name,omitempty"`
Remote string `json:"remote,omitempty"`
Privileges []*PluginPrivilege `json:"privileges,omitempty"`
Disabled bool `json:"disabled,omitempty"`
Env []string `json:"env,omitempty"`
}
// PluginPrivilege describes a permission the user has to accept
// upon installing a plugin.
type PluginPrivilege struct {
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Value []string `json:"value,omitempty"`
}
var (
ErrInvalidLengthPlugin = fmt.Errorf("proto: negative length found during unmarshaling") // Deprecated: this error was only used internally and is no longer used.
ErrIntOverflowPlugin = fmt.Errorf("proto: integer overflow") // Deprecated: this error was only used internally and is no longer used.
ErrUnexpectedEndOfGroupPlugin = fmt.Errorf("proto: unexpected end of group") // Deprecated: this error was only used internally and is no longer used.
)

View File

@ -4,7 +4,6 @@ import (
"time"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/swarm/runtime"
)
// TaskState represents the state of a task.
@ -77,7 +76,7 @@ type TaskSpec struct {
// NetworkAttachmentSpec is used if the `Runtime` field is set to
// `attachment`.
ContainerSpec *ContainerSpec `json:",omitempty"`
PluginSpec *runtime.PluginSpec `json:",omitempty"`
PluginSpec *RuntimeSpec `json:",omitempty"`
NetworkAttachmentSpec *NetworkAttachmentSpec `json:",omitempty"`
Resources *ResourceRequirements `json:",omitempty"`

View File

@ -1,17 +0,0 @@
package system
import (
"github.com/docker/docker/api/types/build"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/volume"
)
// DiskUsage contains response of Engine API for API 1.49 and greater:
// GET "/system/df"
type DiskUsage struct {
Images *image.DiskUsage
Containers *container.DiskUsage
Volumes *volume.DiskUsage
BuildCache *build.CacheDiskUsage
}

View File

@ -46,15 +46,16 @@ type NetworkSettings = container.NetworkSettings
// NetworkSettingsBase holds networking state for a container when inspecting it.
//
// Deprecated: use [container.NetworkSettingsBase].
type NetworkSettingsBase = container.NetworkSettingsBase
// Deprecated: [container.NetworkSettingsBase] will be removed in v29. Prefer
// accessing the fields it contains through [container.NetworkSettings].
type NetworkSettingsBase = container.NetworkSettingsBase //nolint:staticcheck // ignore SA1019: NetworkSettingsBase is deprecated in v28.4.
// DefaultNetworkSettings holds network information
// during the 2 release deprecation period.
// It will be removed in Docker 1.11.
//
// Deprecated: use [container.DefaultNetworkSettings].
type DefaultNetworkSettings = container.DefaultNetworkSettings
type DefaultNetworkSettings = container.DefaultNetworkSettings //nolint:staticcheck // ignore SA1019: DefaultNetworkSettings is deprecated in v28.4.
// SummaryNetworkSettings provides a summary of container's networks
// in /containers/json.

View File

@ -1,6 +1,8 @@
package volume
// DiskUsage contains disk usage for volumes.
//
// Deprecated: this type is no longer used and will be removed in the next release.
type DiskUsage struct {
TotalSize int64
Reclaimable int64

View File

@ -463,7 +463,9 @@ func (cli *Client) dialer() func(context.Context) (net.Conn, error) {
case "unix":
return net.Dial(cli.proto, cli.addr)
case "npipe":
return sockets.DialPipe(cli.addr, 32*time.Second)
ctx, cancel := context.WithTimeout(ctx, 32*time.Second)
defer cancel()
return dialPipeContext(ctx, cli.addr)
default:
if tlsConfig := cli.tlsConfig(); tlsConfig != nil {
return tls.Dial(cli.proto, cli.addr, tlsConfig)

View File

@ -2,6 +2,17 @@
package client
import (
"context"
"net"
"syscall"
)
// DefaultDockerHost defines OS-specific default host if the DOCKER_HOST
// (EnvOverrideHost) environment variable is unset or empty.
const DefaultDockerHost = "unix:///var/run/docker.sock"
// dialPipeContext connects to a Windows named pipe. It is not supported on non-Windows.
func dialPipeContext(_ context.Context, _ string) (net.Conn, error) {
return nil, syscall.EAFNOSUPPORT
}

View File

@ -1,5 +1,17 @@
package client
import (
"context"
"net"
"github.com/Microsoft/go-winio"
)
// DefaultDockerHost defines OS-specific default host if the DOCKER_HOST
// (EnvOverrideHost) environment variable is unset or empty.
const DefaultDockerHost = "npipe:////./pipe/docker_engine"
// dialPipeContext connects to a Windows named pipe. It is not supported on non-Windows.
func dialPipeContext(ctx context.Context, addr string) (net.Conn, error) {
return winio.DialPipeContext(ctx, addr)
}

View File

@ -28,7 +28,7 @@ func (cli *Client) ContainerStats(ctx context.Context, containerID string, strea
return container.StatsResponseReader{
Body: resp.Body,
OSType: getDockerOS(resp.Header.Get("Server")),
OSType: resp.Header.Get("Ostype"),
}, nil
}
@ -51,6 +51,6 @@ func (cli *Client) ContainerStatsOneShot(ctx context.Context, containerID string
return container.StatsResponseReader{
Body: resp.Body,
OSType: getDockerOS(resp.Header.Get("Server")),
OSType: resp.Header.Get("Ostype"),
}, nil
}

View File

@ -40,7 +40,7 @@ func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, optio
return build.ImageBuildResponse{
Body: resp.Body,
OSType: getDockerOS(resp.Header.Get("Server")),
OSType: resp.Header.Get("Ostype"),
}, nil
}

View File

@ -8,12 +8,9 @@ import (
cerrdefs "github.com/containerd/errdefs"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/internal/lazyregexp"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
var headerRegexp = lazyregexp.New(`\ADocker/.+\s\((.+)\)\z`)
type emptyIDError string
func (e emptyIDError) InvalidParameter() {}
@ -31,16 +28,6 @@ func trimID(objType, id string) (string, error) {
return id, nil
}
// getDockerOS returns the operating system based on the server header from the daemon.
func getDockerOS(serverHeader string) string {
var osType string
matches := headerRegexp.FindStringSubmatch(serverHeader)
if len(matches) > 0 {
osType = matches[1]
}
return osType
}
// getFiltersQuery returns a url query with "filters" query term, based on the
// filters provided.
func getFiltersQuery(f filters.Args) (url.Values, error) {

View File

@ -1,90 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code below was largely copied from golang.org/x/mod@v0.22;
// https://github.com/golang/mod/blob/v0.22.0/internal/lazyregexp/lazyre.go
// with some additional methods added.
// Package lazyregexp is a thin wrapper over regexp, allowing the use of global
// regexp variables without forcing them to be compiled at init.
package lazyregexp
import (
"os"
"regexp"
"strings"
"sync"
)
// Regexp is a wrapper around [regexp.Regexp], where the underlying regexp will be
// compiled the first time it is needed.
type Regexp struct {
str string
once sync.Once
rx *regexp.Regexp
}
func (r *Regexp) re() *regexp.Regexp {
r.once.Do(r.build)
return r.rx
}
func (r *Regexp) build() {
r.rx = regexp.MustCompile(r.str)
r.str = ""
}
func (r *Regexp) FindSubmatch(s []byte) [][]byte {
return r.re().FindSubmatch(s)
}
func (r *Regexp) FindAllStringSubmatch(s string, n int) [][]string {
return r.re().FindAllStringSubmatch(s, n)
}
func (r *Regexp) FindStringSubmatch(s string) []string {
return r.re().FindStringSubmatch(s)
}
func (r *Regexp) FindStringSubmatchIndex(s string) []int {
return r.re().FindStringSubmatchIndex(s)
}
func (r *Regexp) ReplaceAllString(src, repl string) string {
return r.re().ReplaceAllString(src, repl)
}
func (r *Regexp) FindString(s string) string {
return r.re().FindString(s)
}
func (r *Regexp) FindAllString(s string, n int) []string {
return r.re().FindAllString(s, n)
}
func (r *Regexp) MatchString(s string) bool {
return r.re().MatchString(s)
}
func (r *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string {
return r.re().ReplaceAllStringFunc(src, repl)
}
func (r *Regexp) SubexpNames() []string {
return r.re().SubexpNames()
}
var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test")
// New creates a new lazy regexp, delaying the compiling work until it is first
// needed. If the code is being run as part of tests, the regexp compiling will
// happen immediately.
func New(str string) *Regexp {
lr := &Regexp{str: str}
if inTest {
// In tests, always compile the regexps early.
lr.re()
}
return lr
}

View File

@ -1,46 +0,0 @@
package multierror
import (
"strings"
)
// Join is a drop-in replacement for errors.Join with better formatting.
func Join(errs ...error) error {
n := 0
for _, err := range errs {
if err != nil {
n++
}
}
if n == 0 {
return nil
}
e := &joinError{
errs: make([]error, 0, n),
}
for _, err := range errs {
if err != nil {
e.errs = append(e.errs, err)
}
}
return e
}
type joinError struct {
errs []error
}
func (e *joinError) Error() string {
if len(e.errs) == 1 {
return strings.TrimSpace(e.errs[0].Error())
}
stringErrs := make([]string, 0, len(e.errs))
for _, subErr := range e.errs {
stringErrs = append(stringErrs, strings.ReplaceAll(subErr.Error(), "\n", "\n\t"))
}
return "* " + strings.Join(stringErrs, "\n* ")
}
func (e *joinError) Unwrap() []error {
return e.errs
}

View File

@ -151,9 +151,9 @@ type JSONMessage struct {
// Deprecated: this field is deprecated since docker v0.7.1 / API v1.8. Use the information in [Progress] instead. This field will be omitted in a future release.
ProgressMessage string `json:"progress,omitempty"`
ID string `json:"id,omitempty"`
From string `json:"from,omitempty"`
Time int64 `json:"time,omitempty"`
TimeNano int64 `json:"timeNano,omitempty"`
From string `json:"from,omitempty"` // Deprecated: this field is no longer set in stream responses and should not be used.
Time int64 `json:"time,omitempty"` // Deprecated: this field is no longer set in stream responses and should not be used.
TimeNano int64 `json:"timeNano,omitempty"` // Deprecated: this field is no longer set in stream responses and should not be used.
Error *JSONError `json:"errorDetail,omitempty"`
// ErrorMessage contains errors encountered during the operation.

View File

@ -247,9 +247,13 @@ const (
// Formats that utilize SSH may need to supply credentials as a [GitOption].
// You may need to check the source code for a full list of supported formats.
//
// Fragment can be used to pass ref:subdir format that can set in (old-style)
// Docker Git URL format after # . This is provided for backwards compatibility.
// It is recommended to leave it empty and call GitRef(), GitSubdir() options instead.
//
// By default the git repository is cloned with `--depth=1` to reduce the amount of data downloaded.
// Additionally the ".git" directory is removed after the clone, you can keep ith with the [KeepGitDir] [GitOption].
func Git(url, ref string, opts ...GitOption) State {
func Git(url, fragment string, opts ...GitOption) State {
remote, err := gitutil.ParseURL(url)
if errors.Is(err, gitutil.ErrUnknownProtocol) {
url = "https://" + url
@ -259,6 +263,20 @@ func Git(url, ref string, opts ...GitOption) State {
url = remote.Remote
}
gi := &GitInfo{
AuthHeaderSecret: GitAuthHeaderKey,
AuthTokenSecret: GitAuthTokenKey,
}
ref, subdir, ok := strings.Cut(fragment, ":")
if ref != "" {
GitRef(ref).SetGitOption(gi)
}
if ok && subdir != "" {
GitSubDir(subdir).SetGitOption(gi)
}
for _, o := range opts {
o.SetGitOption(gi)
}
var id string
if err != nil {
// If we can't parse the URL, just use the full URL as the ID. The git
@ -269,18 +287,13 @@ func Git(url, ref string, opts ...GitOption) State {
// for different protocols (e.g. https and ssh) that have the same
// host/path/fragment combination.
id = remote.Host + path.Join("/", remote.Path)
if ref != "" {
id += "#" + ref
if gi.Ref != "" || gi.SubDir != "" {
id += "#" + gi.Ref
if gi.SubDir != "" {
id += ":" + gi.SubDir
}
}
}
gi := &GitInfo{
AuthHeaderSecret: GitAuthHeaderKey,
AuthTokenSecret: GitAuthTokenKey,
}
for _, o := range opts {
o.SetGitOption(gi)
}
attrs := map[string]string{}
if gi.KeepGitDir {
attrs[pb.AttrKeepGitDir] = "true"
@ -328,6 +341,11 @@ func Git(url, ref string, opts ...GitOption) State {
addCap(&gi.Constraints, pb.CapSourceGitChecksum)
}
if gi.SkipSubmodules {
attrs[pb.AttrGitSkipSubmodules] = "true"
addCap(&gi.Constraints, pb.CapSourceGitSkipSubmodules)
}
addCap(&gi.Constraints, pb.CapSourceGit)
source := NewSource("git://"+id, attrs, gi.Constraints)
@ -352,6 +370,27 @@ type GitInfo struct {
KnownSSHHosts string
MountSSHSock string
Checksum string
Ref string
SubDir string
SkipSubmodules bool
}
func GitRef(v string) GitOption {
return gitOptionFunc(func(gi *GitInfo) {
gi.Ref = v
})
}
func GitSubDir(v string) GitOption {
return gitOptionFunc(func(gi *GitInfo) {
gi.SubDir = v
})
}
func GitSkipSubmodules() GitOption {
return gitOptionFunc(func(gi *GitInfo) {
gi.SkipSubmodules = true
})
}
func KeepGitDir() GitOption {

View File

@ -3,6 +3,7 @@ package dfgitutil
import (
"net/url"
"strconv"
"strings"
cerrdefs "github.com/containerd/errdefs"
@ -23,9 +24,12 @@ type GitRef struct {
// e.g., "bar" for "https://github.com/foo/bar.git"
ShortName string
// Commit is a commit hash, a tag, or branch name.
// Commit is optional.
Commit string
// Ref is a commit hash, a tag, or branch name.
// Ref is optional.
Ref string
// Checksum is a commit hash.
Checksum string
// SubDir is a directory path inside the repo.
// SubDir is optional.
@ -46,12 +50,16 @@ type GitRef struct {
// Discouraged, although not deprecated.
// Instead, consider using an encrypted TCP connection such as "git@github.com/foo/bar.git" or "https://github.com/foo/bar.git".
UnencryptedTCP bool
// KeepGitDir is true for URL that controls whether to keep the .git directory.
KeepGitDir *bool
// Submodules is true for URL that controls whether to fetch git submodules.
Submodules *bool
}
// var gitURLPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`)
// ParseGitRef parses a git ref.
func ParseGitRef(ref string) (*GitRef, error) {
func ParseGitRef(ref string) (*GitRef, bool, error) {
res := &GitRef{}
var (
@ -60,21 +68,25 @@ func ParseGitRef(ref string) (*GitRef, error) {
)
if strings.HasPrefix(ref, "./") || strings.HasPrefix(ref, "../") {
return nil, cerrdefs.ErrInvalidArgument
return nil, false, errors.WithStack(cerrdefs.ErrInvalidArgument)
} else if strings.HasPrefix(ref, "github.com/") {
res.IndistinguishableFromLocal = true // Deprecated
remote = gitutil.FromURL(&url.URL{
Scheme: "https",
Host: "github.com",
Path: strings.TrimPrefix(ref, "github.com/"),
})
u, err := url.Parse(ref)
if err != nil {
return nil, false, err
}
u.Scheme = "https"
remote, err = gitutil.FromURL(u)
if err != nil {
return nil, false, err
}
} else {
remote, err = gitutil.ParseURL(ref)
if errors.Is(err, gitutil.ErrUnknownProtocol) {
return nil, err
return nil, false, err
}
if err != nil {
return nil, err
return nil, false, err
}
switch remote.Scheme {
@ -86,7 +98,7 @@ func ParseGitRef(ref string) (*GitRef, error) {
// An HTTP(S) URL is considered to be a valid git ref only when it has the ".git[...]" suffix.
case gitutil.HTTPProtocol, gitutil.HTTPSProtocol:
if !strings.HasSuffix(remote.Path, ".git") {
return nil, cerrdefs.ErrInvalidArgument
return nil, false, errors.WithStack(cerrdefs.ErrInvalidArgument)
}
}
}
@ -96,11 +108,107 @@ func ParseGitRef(ref string) (*GitRef, error) {
_, res.Remote, _ = strings.Cut(res.Remote, "://")
}
if remote.Opts != nil {
res.Commit, res.SubDir = remote.Opts.Ref, remote.Opts.Subdir
res.Ref, res.SubDir = remote.Opts.Ref, remote.Opts.Subdir
}
repoSplitBySlash := strings.Split(res.Remote, "/")
res.ShortName = strings.TrimSuffix(repoSplitBySlash[len(repoSplitBySlash)-1], ".git")
return res, nil
if err := res.loadQuery(remote.Query); err != nil {
return nil, true, err
}
return res, true, nil
}
func (gf *GitRef) loadQuery(query url.Values) error {
if len(query) == 0 {
return nil
}
var tag, branch string
for k, v := range query {
switch len(v) {
case 0, 1:
if len(v) == 0 || v[0] == "" {
switch k {
case "submodules", "keep-git-dir":
v = nil
default:
return errors.Errorf("query %q has no value", k)
}
}
// NOP
default:
return errors.Errorf("query %q has multiple values", k)
}
switch k {
case "ref":
if gf.Ref != "" && gf.Ref != v[0] {
return errors.Errorf("ref conflicts: %q vs %q", gf.Ref, v[0])
}
gf.Ref = v[0]
case "tag":
tag = v[0]
case "branch":
branch = v[0]
case "subdir":
if gf.SubDir != "" && gf.SubDir != v[0] {
return errors.Errorf("subdir conflicts: %q vs %q", gf.SubDir, v[0])
}
gf.SubDir = v[0]
case "checksum", "commit":
gf.Checksum = v[0]
case "keep-git-dir":
var vv bool
if len(v) == 0 {
vv = true
} else {
var err error
vv, err = strconv.ParseBool(v[0])
if err != nil {
return errors.Errorf("invalid keep-git-dir value: %q", v[0])
}
}
gf.KeepGitDir = &vv
case "submodules":
var vv bool
if len(v) == 0 {
vv = true
} else {
var err error
vv, err = strconv.ParseBool(v[0])
if err != nil {
return errors.Errorf("invalid submodules value: %q", v[0])
}
}
gf.Submodules = &vv
default:
return errors.Errorf("unexpected query %q", k)
}
}
if tag != "" {
const tagPrefix = "refs/tags/"
if !strings.HasPrefix(tag, tagPrefix) {
tag = tagPrefix + tag
}
if gf.Ref != "" && gf.Ref != tag {
return errors.Errorf("ref conflicts: %q vs %q", gf.Ref, tag)
}
gf.Ref = tag
}
if branch != "" {
if tag != "" {
// TODO: consider allowing this, when the tag actually exists on the branch
return errors.New("branch conflicts with tag")
}
const branchPrefix = "refs/heads/"
if !strings.HasPrefix(branch, branchPrefix) {
branch = branchPrefix + branch
}
if gf.Ref != "" && gf.Ref != branch {
return errors.Errorf("ref conflicts: %q vs %q", gf.Ref, branch)
}
gf.Ref = branch
}
return nil
}

View File

@ -69,11 +69,14 @@ func (bc *Client) initContext(ctx context.Context) (*buildContext, error) {
bctx.dockerfileLocalName = v
}
keepGit := false
var keepGit *bool
if v, err := strconv.ParseBool(opts[keyContextKeepGitDirArg]); err == nil {
keepGit = v
keepGit = &v
}
if st, ok := DetectGitContext(opts[localNameContext], keepGit); ok {
if st, ok, err := DetectGitContext(opts[localNameContext], keepGit); ok {
if err != nil {
return nil, err
}
bctx.context = st
bctx.dockerfile = st
} else if st, filename, ok := DetectHTTPContext(opts[localNameContext]); ok {
@ -140,22 +143,33 @@ func (bc *Client) initContext(ctx context.Context) (*buildContext, error) {
return bctx, nil
}
func DetectGitContext(ref string, keepGit bool) (*llb.State, bool) {
g, err := dfgitutil.ParseGitRef(ref)
func DetectGitContext(ref string, keepGit *bool) (*llb.State, bool, error) {
g, isGit, err := dfgitutil.ParseGitRef(ref)
if err != nil {
return nil, false
return nil, isGit, err
}
commit := g.Commit
if g.SubDir != "" {
commit += ":" + g.SubDir
gitOpts := []llb.GitOption{
llb.GitRef(g.Ref),
WithInternalName("load git source " + ref),
}
gitOpts := []llb.GitOption{WithInternalName("load git source " + ref)}
if keepGit {
if g.KeepGitDir != nil && *g.KeepGitDir {
gitOpts = append(gitOpts, llb.KeepGitDir())
}
if keepGit != nil && *keepGit {
gitOpts = append(gitOpts, llb.KeepGitDir())
}
if g.SubDir != "" {
gitOpts = append(gitOpts, llb.GitSubDir(g.SubDir))
}
if g.Checksum != "" {
gitOpts = append(gitOpts, llb.GitChecksum(g.Checksum))
}
if g.Submodules != nil && !*g.Submodules {
gitOpts = append(gitOpts, llb.GitSkipSubmodules())
}
st := llb.Git(g.Remote, commit, gitOpts...)
return &st, true
st := llb.Git(g.Remote, "", gitOpts...)
return &st, true, nil
}
func DetectHTTPContext(ref string) (*llb.State, string, bool) {

View File

@ -138,17 +138,23 @@ func (nc *NamedContext) load(ctx context.Context, count int) (*llb.State, *docke
}
return &st, &img, nil
case "git":
st, ok := DetectGitContext(nc.input, true)
st, ok, err := DetectGitContext(nc.input, nil)
if !ok {
return nil, nil, errors.Errorf("invalid git context %s", nc.input)
}
if err != nil {
return nil, nil, err
}
return st, nil, nil
case "http", "https":
st, ok := DetectGitContext(nc.input, true)
st, ok, err := DetectGitContext(nc.input, nil)
if !ok {
httpst := llb.HTTP(nc.input, llb.WithCustomName("[context "+nc.nameWithPlatform+"] "+nc.input))
st = &httpst
}
if err != nil {
return nil, nil, err
}
return st, nil, nil
case "oci-layout":
refSpec := strings.TrimPrefix(vv[1], "//")

View File

@ -7,6 +7,7 @@ const AttrAuthTokenSecret = "git.authtokensecret"
const AttrKnownSSHHosts = "git.knownsshhosts"
const AttrMountSSHSock = "git.mountsshsock"
const AttrGitChecksum = "git.checksum"
const AttrGitSkipSubmodules = "git.skipsubmodules"
const AttrLocalSessionID = "local.session"
const AttrLocalUniqueID = "local.unique"

View File

@ -23,14 +23,15 @@ const (
CapSourceLocalDiffer apicaps.CapID = "source.local.differ"
CapSourceMetadataTransfer apicaps.CapID = "source.local.metadatatransfer"
CapSourceGit apicaps.CapID = "source.git"
CapSourceGitKeepDir apicaps.CapID = "source.git.keepgitdir"
CapSourceGitFullURL apicaps.CapID = "source.git.fullurl"
CapSourceGitHTTPAuth apicaps.CapID = "source.git.httpauth"
CapSourceGitKnownSSHHosts apicaps.CapID = "source.git.knownsshhosts"
CapSourceGitMountSSHSock apicaps.CapID = "source.git.mountsshsock"
CapSourceGitSubdir apicaps.CapID = "source.git.subdir"
CapSourceGitChecksum apicaps.CapID = "source.git.checksum"
CapSourceGit apicaps.CapID = "source.git"
CapSourceGitKeepDir apicaps.CapID = "source.git.keepgitdir"
CapSourceGitFullURL apicaps.CapID = "source.git.fullurl"
CapSourceGitHTTPAuth apicaps.CapID = "source.git.httpauth"
CapSourceGitKnownSSHHosts apicaps.CapID = "source.git.knownsshhosts"
CapSourceGitMountSSHSock apicaps.CapID = "source.git.mountsshsock"
CapSourceGitSubdir apicaps.CapID = "source.git.subdir"
CapSourceGitChecksum apicaps.CapID = "source.git.checksum"
CapSourceGitSkipSubmodules apicaps.CapID = "source.git.skipsubmodules"
CapSourceHTTP apicaps.CapID = "source.http"
CapSourceHTTPAuth apicaps.CapID = "source.http.auth"
@ -229,6 +230,12 @@ func init() {
Status: apicaps.CapStatusExperimental,
})
Caps.Init(apicaps.Cap{
ID: CapSourceGitSkipSubmodules,
Enabled: true,
Status: apicaps.CapStatusExperimental,
})
Caps.Init(apicaps.Cap{
ID: CapSourceHTTP,
Enabled: true,

View File

@ -47,16 +47,17 @@ type GitURL struct {
Path string
// User is the username/password to access the host
User *url.Userinfo
// Query is the query parameters for the URL
Query url.Values
// Opts can contain additional metadata
Opts *GitURLOpts
// Remote is a valid URL remote to pass into the Git CLI tooling (i.e.
// without the fragment metadata)
Remote string
}
// GitURLOpts is the buildkit-specific metadata extracted from the fragment
// of a remote URL.
// or the query of a remote URL.
type GitURLOpts struct {
// Ref is the git reference
Ref string
@ -86,11 +87,11 @@ func ParseURL(remote string) (*GitURL, error) {
if err != nil {
return nil, err
}
return FromURL(url), nil
return FromURL(url)
}
if url, err := sshutil.ParseSCPStyleURL(remote); err == nil {
return fromSCPStyleURL(url), nil
return fromSCPStyleURL(url)
}
return nil, ErrUnknownProtocol
@ -105,28 +106,40 @@ func IsGitTransport(remote string) bool {
return sshutil.IsImplicitSSHTransport(remote)
}
func FromURL(url *url.URL) *GitURL {
func FromURL(url *url.URL) (*GitURL, error) {
withoutOpts := *url
withoutOpts.Fragment = ""
withoutOpts.RawQuery = ""
q := url.Query()
if len(q) == 0 {
q = nil
}
return &GitURL{
Scheme: url.Scheme,
User: url.User,
Host: url.Host,
Path: url.Path,
Query: q,
Opts: parseOpts(url.Fragment),
Remote: withoutOpts.String(),
}
}, nil
}
func fromSCPStyleURL(url *sshutil.SCPStyleURL) *GitURL {
func fromSCPStyleURL(url *sshutil.SCPStyleURL) (*GitURL, error) {
withoutOpts := *url
withoutOpts.Fragment = ""
withoutOpts.Query = nil
q := url.Query
if len(q) == 0 {
q = nil
}
return &GitURL{
Scheme: SSHProtocol,
User: url.User,
Host: url.Host,
Path: url.Path,
Query: q,
Opts: parseOpts(url.Fragment),
Remote: withoutOpts.String(),
}
}, nil
}

View File

@ -1,13 +1,14 @@
package sshutil
import (
"errors"
"fmt"
"net/url"
"regexp"
"github.com/pkg/errors"
)
var gitSSHRegex = regexp.MustCompile("^([a-zA-Z0-9-_]+)@([a-zA-Z0-9-.]+):(.*?)(?:#(.*))?$")
var gitSSHRegex = regexp.MustCompile(`^([a-zA-Z0-9-_]+)@([a-zA-Z0-9-.]+):(.*?)(?:\?(.*?))?(?:#(.*))?$`)
func IsImplicitSSHTransport(s string) bool {
return gitSSHRegex.MatchString(s)
@ -18,6 +19,7 @@ type SCPStyleURL struct {
Host string
Path string
Query url.Values
Fragment string
}
@ -26,18 +28,34 @@ func ParseSCPStyleURL(raw string) (*SCPStyleURL, error) {
if matches == nil {
return nil, errors.New("invalid scp-style url")
}
rawQuery := matches[4]
vals := url.Values{}
if rawQuery != "" {
var err error
vals, err = url.ParseQuery(rawQuery)
if err != nil {
return nil, errors.Wrap(err, "invalid query in scp-style url")
}
}
return &SCPStyleURL{
User: url.User(matches[1]),
Host: matches[2],
Path: matches[3],
Fragment: matches[4],
Query: vals,
Fragment: matches[5],
}, nil
}
func (url *SCPStyleURL) String() string {
base := fmt.Sprintf("%s@%s:%s", url.User.String(), url.Host, url.Path)
if url.Fragment == "" {
return base
func (u *SCPStyleURL) String() string {
s := fmt.Sprintf("%s@%s:%s", u.User.String(), u.Host, u.Path)
if len(u.Query) > 0 {
s += "?" + u.Query.Encode()
}
return base + "#" + url.Fragment
if u.Fragment != "" {
s += "#" + u.Fragment
}
return s
}

8
vendor/modules.txt vendored
View File

@ -218,7 +218,7 @@ github.com/davecgh/go-spew/spew
# github.com/distribution/reference v0.6.0
## explicit; go 1.20
github.com/distribution/reference
# github.com/docker/cli v28.3.3+incompatible
# github.com/docker/cli v28.4.0+incompatible
## explicit
github.com/docker/cli/cli
github.com/docker/cli/cli-plugins/metadata
@ -256,7 +256,7 @@ github.com/docker/cli-docs-tool
github.com/docker/cli-docs-tool/annotation
# github.com/docker/distribution v2.8.3+incompatible
## explicit
# github.com/docker/docker v28.3.3+incompatible
# github.com/docker/docker v28.4.0+incompatible
## explicit
github.com/docker/docker/api
github.com/docker/docker/api/types
@ -280,8 +280,6 @@ github.com/docker/docker/api/types/time
github.com/docker/docker/api/types/versions
github.com/docker/docker/api/types/volume
github.com/docker/docker/client
github.com/docker/docker/internal/lazyregexp
github.com/docker/docker/internal/multierror
github.com/docker/docker/pkg/jsonmessage
github.com/docker/docker/pkg/namesgenerator
github.com/docker/docker/pkg/stdcopy
@ -450,7 +448,7 @@ github.com/mitchellh/go-wordwrap
# github.com/mitchellh/hashstructure/v2 v2.0.2
## explicit; go 1.14
github.com/mitchellh/hashstructure/v2
# github.com/moby/buildkit v0.24.0-rc1
# github.com/moby/buildkit v0.24.0
## explicit; go 1.23.0
github.com/moby/buildkit/api/services/control
github.com/moby/buildkit/api/types