mirror of https://github.com/docker/buildx.git
Compare commits
24 Commits
v0.28.0-rc
...
master
Author | SHA1 | Date |
---|---|---|
|
ae4e7ee6a4 | |
|
70487beecb | |
|
86ddc5de4e | |
|
7bcaf399b9 | |
|
dc10c680f3 | |
|
9c9fb2a12a | |
|
b4d5ec9bc2 | |
|
a923dbc1d9 | |
|
bafc4e207e | |
|
2109c9d80d | |
|
8841b2dfc8 | |
|
643322cbc3 | |
|
056780314b | |
|
d136d2ba53 | |
|
e4f23adf3f | |
|
5e6951c571 | |
|
d873cae872 | |
|
4df89d89fc | |
|
1f39ad2001 | |
|
ce3592e4ab | |
|
67218bef58 | |
|
07b99ae7bf | |
|
ebe66a8e2e | |
|
ce07ae04cd |
|
@ -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 }}"
|
||||
-
|
||||
|
|
|
@ -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 }}
|
||||
-
|
||||
|
|
|
@ -27,6 +27,6 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Run
|
||||
uses: actions/labeler@v5
|
||||
uses: actions/labeler@v6
|
||||
with:
|
||||
sync-labels: true
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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"), ","),
|
||||
|
|
23
build/opt.go
23
build/opt.go
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
6
go.mod
|
@ -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
12
go.sum
|
@ -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=
|
||||
|
|
159
tests/build.go
159
tests/build.go
|
@ -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) {
|
||||
|
|
|
@ -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...)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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))
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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("edString{&tlsOptions.CAFile}, "tlscacert", "Trust certs signed only by this CA")
|
||||
flags.Var("edString{&tlsOptions.CertFile}, "tlscert", "Path to TLS certificate file")
|
||||
flags.Var("edString{&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
|
||||
}
|
||||
|
|
|
@ -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 == "" {
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
61
vendor/github.com/docker/docker/api/types/filters/filters_deprecated.go
generated
vendored
Normal file
61
vendor/github.com/docker/docker/api/types/filters/filters_deprecated.go
generated
vendored
Normal 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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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...)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
//go:generate protoc --gogofaster_out=import_path=github.com/docker/docker/api/types/swarm/runtime:. plugin.proto
|
||||
|
||||
package runtime
|
|
@ -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")
|
||||
)
|
|
@ -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;
|
||||
}
|
27
vendor/github.com/docker/docker/api/types/swarm/runtime/runtime.go
generated
vendored
Normal file
27
vendor/github.com/docker/docker/api/types/swarm/runtime/runtime.go
generated
vendored
Normal 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.
|
||||
)
|
|
@ -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"`
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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], "//")
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue