mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-05-18 00:47:48 +08:00
Merge pull request #3071 from thaJeztah/bump_engine_28.0.2
vendor: github.com/docker/docker, docker/cli v28.0.2
This commit is contained in:
commit
c682742de0
@ -11,7 +11,7 @@ import (
|
||||
"github.com/docker/buildx/util/desktop"
|
||||
"github.com/docker/buildx/version"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli-plugins/plugin"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/debug"
|
||||
@ -64,7 +64,7 @@ func flushMetrics(cmd *command.DockerCli) {
|
||||
|
||||
func runPlugin(cmd *command.DockerCli) error {
|
||||
rootCmd := commands.NewRootCmd("buildx", true, cmd)
|
||||
return plugin.RunPlugin(cmd, rootCmd, manager.Metadata{
|
||||
return plugin.RunPlugin(cmd, rootCmd, metadata.Metadata{
|
||||
SchemaVersion: "0.1.0",
|
||||
Vendor: "Docker Inc.",
|
||||
Version: version.Version,
|
||||
|
6
go.mod
6
go.mod
@ -8,7 +8,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27
|
||||
github.com/compose-spec/compose-go/v2 v2.4.8
|
||||
github.com/containerd/console v1.0.4
|
||||
github.com/containerd/containerd/v2 v2.0.3
|
||||
github.com/containerd/containerd/v2 v2.0.4
|
||||
github.com/containerd/continuity v0.4.5
|
||||
github.com/containerd/errdefs v1.0.0
|
||||
github.com/containerd/log v0.1.0
|
||||
@ -17,9 +17,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.0.1+incompatible
|
||||
github.com/docker/cli v28.0.2+incompatible
|
||||
github.com/docker/cli-docs-tool v0.9.0
|
||||
github.com/docker/docker v28.0.1+incompatible
|
||||
github.com/docker/docker v28.0.2+incompatible
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/gofrs/flock v0.12.1
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
|
12
go.sum
12
go.sum
@ -85,8 +85,8 @@ github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn
|
||||
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||
github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0=
|
||||
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
|
||||
github.com/containerd/containerd/v2 v2.0.3 h1:zBKgwgZsuu+LPCMzCLgA4sC4MiZzZ59ZT31XkmiISQM=
|
||||
github.com/containerd/containerd/v2 v2.0.3/go.mod h1:5j9QUUaV/cy9ZeAx4S+8n9ffpf+iYnEj4jiExgcbuLY=
|
||||
github.com/containerd/containerd/v2 v2.0.4 h1:+r7yJMwhTfMm3CDyiBjMBQO8a9CTBxL2Bg/JtqtIwB8=
|
||||
github.com/containerd/containerd/v2 v2.0.4/go.mod h1:5j9QUUaV/cy9ZeAx4S+8n9ffpf+iYnEj4jiExgcbuLY=
|
||||
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
|
||||
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
@ -122,15 +122,15 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/cli v28.0.1+incompatible h1:g0h5NQNda3/CxIsaZfH4Tyf6vpxFth7PYl3hgCPOKzs=
|
||||
github.com/docker/cli v28.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v28.0.2+incompatible h1:cRPZ77FK3/IXTAIQQj1vmhlxiLS5m+MIUDwS6f57lrE=
|
||||
github.com/docker/cli v28.0.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli-docs-tool v0.9.0 h1:CVwQbE+ZziwlPqrJ7LRyUF6GvCA+6gj7MTCsayaK9t0=
|
||||
github.com/docker/cli-docs-tool v0.9.0/go.mod h1:ClrwlNW+UioiRyH9GiAOe1o3J/TsY3Tr1ipoypjAUtc=
|
||||
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.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0=
|
||||
github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v28.0.2+incompatible h1:9BILleFwug5FSSqWBgVevgL3ewDJfWWWyZVqlDMttE8=
|
||||
github.com/docker/docker v28.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||
|
4
vendor/github.com/containerd/containerd/v2/core/images/image.go
generated
vendored
4
vendor/github.com/containerd/containerd/v2/core/images/image.go
generated
vendored
@ -369,8 +369,8 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr
|
||||
}
|
||||
|
||||
return append([]ocispec.Descriptor{}, index.Manifests...), nil
|
||||
} else if !IsLayerType(desc.MediaType) && !IsKnownConfig(desc.MediaType) {
|
||||
// Layers and configs are childless data types and should not be logged.
|
||||
} else if !IsLayerType(desc.MediaType) && !IsKnownConfig(desc.MediaType) && !IsAttestationType(desc.MediaType) {
|
||||
// Layers, configs, and attestations are childless data types and should not be logged.
|
||||
log.G(ctx).Debugf("encountered unknown type %v; children may not be fetched", desc.MediaType)
|
||||
}
|
||||
return nil, nil
|
||||
|
13
vendor/github.com/containerd/containerd/v2/core/images/mediatypes.go
generated
vendored
13
vendor/github.com/containerd/containerd/v2/core/images/mediatypes.go
generated
vendored
@ -58,6 +58,9 @@ const (
|
||||
|
||||
MediaTypeImageLayerEncrypted = ocispec.MediaTypeImageLayer + "+encrypted"
|
||||
MediaTypeImageLayerGzipEncrypted = ocispec.MediaTypeImageLayerGzip + "+encrypted"
|
||||
|
||||
// In-toto attestation
|
||||
MediaTypeInToto = "application/vnd.in-toto+json"
|
||||
)
|
||||
|
||||
// DiffCompression returns the compression as defined by the layer diff media
|
||||
@ -193,6 +196,16 @@ func IsKnownConfig(mt string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsAttestationType returns true if the media type is an attestation type
|
||||
func IsAttestationType(mt string) bool {
|
||||
switch mt {
|
||||
case MediaTypeInToto:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// ChildGCLabels returns the label for a given descriptor to reference it
|
||||
func ChildGCLabels(desc ocispec.Descriptor) []string {
|
||||
mt := desc.MediaType
|
||||
|
2
vendor/github.com/containerd/containerd/v2/core/remotes/handlers.go
generated
vendored
2
vendor/github.com/containerd/containerd/v2/core/remotes/handlers.go
generated
vendored
@ -80,6 +80,8 @@ func MakeRefKey(ctx context.Context, desc ocispec.Descriptor) string {
|
||||
return "layer-" + key
|
||||
case images.IsKnownConfig(desc.MediaType):
|
||||
return "config-" + key
|
||||
case images.IsAttestationType(desc.MediaType):
|
||||
return "attestation-" + key
|
||||
default:
|
||||
log.G(ctx).Warnf("reference for unknown type: %s", desc.MediaType)
|
||||
return "unknown-" + key
|
||||
|
3
vendor/github.com/containerd/containerd/v2/version/version.go
generated
vendored
3
vendor/github.com/containerd/containerd/v2/version/version.go
generated
vendored
@ -19,11 +19,12 @@ package version
|
||||
import "runtime"
|
||||
|
||||
var (
|
||||
Name = "containerd"
|
||||
// Package is filled at linking time
|
||||
Package = "github.com/containerd/containerd/v2"
|
||||
|
||||
// Version holds the complete version number. Filled in at linking time.
|
||||
Version = "2.0.3+unknown"
|
||||
Version = "2.0.4+unknown"
|
||||
|
||||
// Revision is filled with the VCS (e.g. git) revision being used to build
|
||||
// the program at linking time.
|
||||
|
18
vendor/github.com/docker/cli/cli-plugins/hooks/printer.go
generated
vendored
18
vendor/github.com/docker/cli/cli-plugins/hooks/printer.go
generated
vendored
@ -1,18 +0,0 @@
|
||||
package hooks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/morikuni/aec"
|
||||
)
|
||||
|
||||
func PrintNextSteps(out io.Writer, messages []string) {
|
||||
if len(messages) == 0 {
|
||||
return
|
||||
}
|
||||
_, _ = fmt.Fprintln(out, aec.Bold.Apply("\nWhat's next:"))
|
||||
for _, n := range messages {
|
||||
_, _ = fmt.Fprintln(out, " ", n)
|
||||
}
|
||||
}
|
116
vendor/github.com/docker/cli/cli-plugins/hooks/template.go
generated
vendored
116
vendor/github.com/docker/cli/cli-plugins/hooks/template.go
generated
vendored
@ -1,116 +0,0 @@
|
||||
package hooks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type HookType int
|
||||
|
||||
const (
|
||||
NextSteps = iota
|
||||
)
|
||||
|
||||
// HookMessage represents a plugin hook response. Plugins
|
||||
// declaring support for CLI hooks need to print a json
|
||||
// representation of this type when their hook subcommand
|
||||
// is invoked.
|
||||
type HookMessage struct {
|
||||
Type HookType
|
||||
Template string
|
||||
}
|
||||
|
||||
// TemplateReplaceSubcommandName returns a hook template string
|
||||
// that will be replaced by the CLI subcommand being executed
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// "you ran the subcommand: " + TemplateReplaceSubcommandName()
|
||||
//
|
||||
// when being executed after the command:
|
||||
// `docker run --name "my-container" alpine`
|
||||
// will result in the message:
|
||||
// `you ran the subcommand: run`
|
||||
func TemplateReplaceSubcommandName() string {
|
||||
return hookTemplateCommandName
|
||||
}
|
||||
|
||||
// TemplateReplaceFlagValue returns a hook template string
|
||||
// that will be replaced by the flags value.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// "you ran a container named: " + TemplateReplaceFlagValue("name")
|
||||
//
|
||||
// when being executed after the command:
|
||||
// `docker run --name "my-container" alpine`
|
||||
// will result in the message:
|
||||
// `you ran a container named: my-container`
|
||||
func TemplateReplaceFlagValue(flag string) string {
|
||||
return fmt.Sprintf(hookTemplateFlagValue, flag)
|
||||
}
|
||||
|
||||
// TemplateReplaceArg takes an index i and returns a hook
|
||||
// template string that the CLI will replace the template with
|
||||
// the ith argument, after processing the passed flags.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// "run this image with `docker run " + TemplateReplaceArg(0) + "`"
|
||||
//
|
||||
// when being executed after the command:
|
||||
// `docker pull alpine`
|
||||
// will result in the message:
|
||||
// "Run this image with `docker run alpine`"
|
||||
func TemplateReplaceArg(i int) string {
|
||||
return fmt.Sprintf(hookTemplateArg, strconv.Itoa(i))
|
||||
}
|
||||
|
||||
func ParseTemplate(hookTemplate string, cmd *cobra.Command) ([]string, error) {
|
||||
tmpl := template.New("").Funcs(commandFunctions)
|
||||
tmpl, err := tmpl.Parse(hookTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := bytes.Buffer{}
|
||||
err = tmpl.Execute(&b, cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return strings.Split(b.String(), "\n"), nil
|
||||
}
|
||||
|
||||
var ErrHookTemplateParse = errors.New("failed to parse hook template")
|
||||
|
||||
const (
|
||||
hookTemplateCommandName = "{{.Name}}"
|
||||
hookTemplateFlagValue = `{{flag . "%s"}}`
|
||||
hookTemplateArg = "{{arg . %s}}"
|
||||
)
|
||||
|
||||
var commandFunctions = template.FuncMap{
|
||||
"flag": getFlagValue,
|
||||
"arg": getArgValue,
|
||||
}
|
||||
|
||||
func getFlagValue(cmd *cobra.Command, flag string) (string, error) {
|
||||
cmdFlag := cmd.Flag(flag)
|
||||
if cmdFlag == nil {
|
||||
return "", ErrHookTemplateParse
|
||||
}
|
||||
return cmdFlag.Value.String(), nil
|
||||
}
|
||||
|
||||
func getArgValue(cmd *cobra.Command, i int) (string, error) {
|
||||
flags := cmd.Flags()
|
||||
if flags == nil {
|
||||
return "", ErrHookTemplateParse
|
||||
}
|
||||
return flags.Arg(i), nil
|
||||
}
|
21
vendor/github.com/docker/cli/cli-plugins/manager/candidate.go
generated
vendored
21
vendor/github.com/docker/cli/cli-plugins/manager/candidate.go
generated
vendored
@ -1,21 +0,0 @@
|
||||
package manager
|
||||
|
||||
import "os/exec"
|
||||
|
||||
// Candidate represents a possible plugin candidate, for mocking purposes
|
||||
type Candidate interface {
|
||||
Path() string
|
||||
Metadata() ([]byte, error)
|
||||
}
|
||||
|
||||
type candidate struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (c *candidate) Path() string {
|
||||
return c.path
|
||||
}
|
||||
|
||||
func (c *candidate) Metadata() ([]byte, error) {
|
||||
return exec.Command(c.path, MetadataSubcommandName).Output() // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
|
||||
}
|
147
vendor/github.com/docker/cli/cli-plugins/manager/cobra.go
generated
vendored
147
vendor/github.com/docker/cli/cli-plugins/manager/cobra.go
generated
vendored
@ -1,147 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
const (
|
||||
// CommandAnnotationPlugin is added to every stub command added by
|
||||
// AddPluginCommandStubs with the value "true" and so can be
|
||||
// used to distinguish plugin stubs from regular commands.
|
||||
CommandAnnotationPlugin = "com.docker.cli.plugin"
|
||||
|
||||
// CommandAnnotationPluginVendor is added to every stub command
|
||||
// added by AddPluginCommandStubs and contains the vendor of
|
||||
// that plugin.
|
||||
CommandAnnotationPluginVendor = "com.docker.cli.plugin.vendor"
|
||||
|
||||
// CommandAnnotationPluginVersion is added to every stub command
|
||||
// added by AddPluginCommandStubs and contains the version of
|
||||
// that plugin.
|
||||
CommandAnnotationPluginVersion = "com.docker.cli.plugin.version"
|
||||
|
||||
// CommandAnnotationPluginInvalid is added to any stub command
|
||||
// added by AddPluginCommandStubs for an invalid command (that
|
||||
// is, one which failed it's candidate test) and contains the
|
||||
// reason for the failure.
|
||||
CommandAnnotationPluginInvalid = "com.docker.cli.plugin-invalid"
|
||||
|
||||
// CommandAnnotationPluginCommandPath is added to overwrite the
|
||||
// command path for a plugin invocation.
|
||||
CommandAnnotationPluginCommandPath = "com.docker.cli.plugin.command_path"
|
||||
)
|
||||
|
||||
var pluginCommandStubsOnce sync.Once
|
||||
|
||||
// AddPluginCommandStubs adds a stub cobra.Commands for each valid and invalid
|
||||
// plugin. The command stubs will have several annotations added, see
|
||||
// `CommandAnnotationPlugin*`.
|
||||
func AddPluginCommandStubs(dockerCli command.Cli, rootCmd *cobra.Command) (err error) {
|
||||
pluginCommandStubsOnce.Do(func() {
|
||||
var plugins []Plugin
|
||||
plugins, err = ListPlugins(dockerCli, rootCmd)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, p := range plugins {
|
||||
vendor := p.Vendor
|
||||
if vendor == "" {
|
||||
vendor = "unknown"
|
||||
}
|
||||
annotations := map[string]string{
|
||||
CommandAnnotationPlugin: "true",
|
||||
CommandAnnotationPluginVendor: vendor,
|
||||
CommandAnnotationPluginVersion: p.Version,
|
||||
}
|
||||
if p.Err != nil {
|
||||
annotations[CommandAnnotationPluginInvalid] = p.Err.Error()
|
||||
}
|
||||
rootCmd.AddCommand(&cobra.Command{
|
||||
Use: p.Name,
|
||||
Short: p.ShortDescription,
|
||||
Run: func(_ *cobra.Command, _ []string) {},
|
||||
Annotations: annotations,
|
||||
DisableFlagParsing: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
flags := rootCmd.PersistentFlags()
|
||||
flags.SetOutput(nil)
|
||||
perr := flags.Parse(args)
|
||||
if perr != nil {
|
||||
return err
|
||||
}
|
||||
if flags.Changed("help") {
|
||||
cmd.HelpFunc()(rootCmd, args)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("docker: unknown command: docker %s\n\nRun 'docker --help' for more information", cmd.Name())
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
// Delegate completion to plugin
|
||||
cargs := []string{p.Path, cobra.ShellCompRequestCmd, p.Name}
|
||||
cargs = append(cargs, args...)
|
||||
cargs = append(cargs, toComplete)
|
||||
os.Args = cargs
|
||||
runCommand, runErr := PluginRunCommand(dockerCli, p.Name, cmd)
|
||||
if runErr != nil {
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
runErr = runCommand.Run()
|
||||
if runErr == nil {
|
||||
os.Exit(0) // plugin already rendered complete data
|
||||
}
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
const (
|
||||
dockerCliAttributePrefix = attribute.Key("docker.cli")
|
||||
|
||||
cobraCommandPath = attribute.Key("cobra.command_path")
|
||||
)
|
||||
|
||||
func getPluginResourceAttributes(cmd *cobra.Command, plugin Plugin) attribute.Set {
|
||||
commandPath := cmd.Annotations[CommandAnnotationPluginCommandPath]
|
||||
if commandPath == "" {
|
||||
commandPath = fmt.Sprintf("%s %s", cmd.CommandPath(), plugin.Name)
|
||||
}
|
||||
|
||||
attrSet := attribute.NewSet(
|
||||
cobraCommandPath.String(commandPath),
|
||||
)
|
||||
|
||||
kvs := make([]attribute.KeyValue, 0, attrSet.Len())
|
||||
for iter := attrSet.Iter(); iter.Next(); {
|
||||
attr := iter.Attribute()
|
||||
kvs = append(kvs, attribute.KeyValue{
|
||||
Key: dockerCliAttributePrefix + "." + attr.Key,
|
||||
Value: attr.Value,
|
||||
})
|
||||
}
|
||||
return attribute.NewSet(kvs...)
|
||||
}
|
||||
|
||||
func appendPluginResourceAttributesEnvvar(env []string, cmd *cobra.Command, plugin Plugin) []string {
|
||||
if attrs := getPluginResourceAttributes(cmd, plugin); attrs.Len() > 0 {
|
||||
// values in environment variables need to be in baggage format
|
||||
// otel/baggage package can be used after update to v1.22, currently it encodes incorrectly
|
||||
attrsSlice := make([]string, attrs.Len())
|
||||
for iter := attrs.Iter(); iter.Next(); {
|
||||
i, v := iter.IndexedAttribute()
|
||||
attrsSlice[i] = string(v.Key) + "=" + url.PathEscape(v.Value.AsString())
|
||||
}
|
||||
env = append(env, ResourceAttributesEnvvar+"="+strings.Join(attrsSlice, ","))
|
||||
}
|
||||
return env
|
||||
}
|
54
vendor/github.com/docker/cli/cli-plugins/manager/error.go
generated
vendored
54
vendor/github.com/docker/cli/cli-plugins/manager/error.go
generated
vendored
@ -1,54 +0,0 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.22
|
||||
|
||||
package manager
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// pluginError is set as Plugin.Err by NewPlugin if the plugin
|
||||
// candidate fails one of the candidate tests. This exists primarily
|
||||
// to implement encoding.TextMarshaller such that rendering a plugin as JSON
|
||||
// (e.g. for `docker info -f '{{json .CLIPlugins}}'`) renders the Err
|
||||
// field as a useful string and not just `{}`. See
|
||||
// https://github.com/golang/go/issues/10748 for some discussion
|
||||
// around why the builtin error type doesn't implement this.
|
||||
type pluginError struct {
|
||||
cause error
|
||||
}
|
||||
|
||||
// Error satisfies the core error interface for pluginError.
|
||||
func (e *pluginError) Error() string {
|
||||
return e.cause.Error()
|
||||
}
|
||||
|
||||
// Cause satisfies the errors.causer interface for pluginError.
|
||||
func (e *pluginError) Cause() error {
|
||||
return e.cause
|
||||
}
|
||||
|
||||
// Unwrap provides compatibility for Go 1.13 error chains.
|
||||
func (e *pluginError) Unwrap() error {
|
||||
return e.cause
|
||||
}
|
||||
|
||||
// MarshalText marshalls the pluginError into a textual form.
|
||||
func (e *pluginError) MarshalText() (text []byte, err error) {
|
||||
return []byte(e.cause.Error()), nil
|
||||
}
|
||||
|
||||
// wrapAsPluginError wraps an error in a pluginError with an
|
||||
// additional message, analogous to errors.Wrapf.
|
||||
func wrapAsPluginError(err error, msg string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &pluginError{cause: errors.Wrap(err, msg)}
|
||||
}
|
||||
|
||||
// NewPluginError creates a new pluginError, analogous to
|
||||
// errors.Errorf.
|
||||
func NewPluginError(msg string, args ...any) error {
|
||||
return &pluginError{cause: errors.Errorf(msg, args...)}
|
||||
}
|
199
vendor/github.com/docker/cli/cli-plugins/manager/hooks.go
generated
vendored
199
vendor/github.com/docker/cli/cli-plugins/manager/hooks.go
generated
vendored
@ -1,199 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli-plugins/hooks"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// HookPluginData is the type representing the information
|
||||
// that plugins declaring support for hooks get passed when
|
||||
// being invoked following a CLI command execution.
|
||||
type HookPluginData struct {
|
||||
// RootCmd is a string representing the matching hook configuration
|
||||
// which is currently being invoked. If a hook for `docker context` is
|
||||
// configured and the user executes `docker context ls`, the plugin will
|
||||
// be invoked with `context`.
|
||||
RootCmd string
|
||||
Flags map[string]string
|
||||
CommandError string
|
||||
}
|
||||
|
||||
// RunCLICommandHooks is the entrypoint into the hooks execution flow after
|
||||
// a main CLI command was executed. It calls the hook subcommand for all
|
||||
// present CLI plugins that declare support for hooks in their metadata and
|
||||
// parses/prints their responses.
|
||||
func RunCLICommandHooks(ctx context.Context, dockerCli command.Cli, rootCmd, subCommand *cobra.Command, cmdErrorMessage string) {
|
||||
commandName := strings.TrimPrefix(subCommand.CommandPath(), rootCmd.Name()+" ")
|
||||
flags := getCommandFlags(subCommand)
|
||||
|
||||
runHooks(ctx, dockerCli, rootCmd, subCommand, commandName, flags, cmdErrorMessage)
|
||||
}
|
||||
|
||||
// RunPluginHooks is the entrypoint for the hooks execution flow
|
||||
// after a plugin command was just executed by the CLI.
|
||||
func RunPluginHooks(ctx context.Context, dockerCli command.Cli, rootCmd, subCommand *cobra.Command, args []string) {
|
||||
commandName := strings.Join(args, " ")
|
||||
flags := getNaiveFlags(args)
|
||||
|
||||
runHooks(ctx, dockerCli, rootCmd, subCommand, commandName, flags, "")
|
||||
}
|
||||
|
||||
func runHooks(ctx context.Context, dockerCli command.Cli, rootCmd, subCommand *cobra.Command, invokedCommand string, flags map[string]string, cmdErrorMessage string) {
|
||||
nextSteps := invokeAndCollectHooks(ctx, dockerCli, rootCmd, subCommand, invokedCommand, flags, cmdErrorMessage)
|
||||
|
||||
hooks.PrintNextSteps(dockerCli.Err(), nextSteps)
|
||||
}
|
||||
|
||||
func invokeAndCollectHooks(ctx context.Context, dockerCli command.Cli, rootCmd, subCmd *cobra.Command, subCmdStr string, flags map[string]string, cmdErrorMessage string) []string {
|
||||
// check if the context was cancelled before invoking hooks
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
pluginsCfg := dockerCli.ConfigFile().Plugins
|
||||
if pluginsCfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
nextSteps := make([]string, 0, len(pluginsCfg))
|
||||
for pluginName, cfg := range pluginsCfg {
|
||||
match, ok := pluginMatch(cfg, subCmdStr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
p, err := GetPlugin(pluginName, dockerCli, rootCmd)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
hookReturn, err := p.RunHook(ctx, HookPluginData{
|
||||
RootCmd: match,
|
||||
Flags: flags,
|
||||
CommandError: cmdErrorMessage,
|
||||
})
|
||||
if err != nil {
|
||||
// skip misbehaving plugins, but don't halt execution
|
||||
continue
|
||||
}
|
||||
|
||||
var hookMessageData hooks.HookMessage
|
||||
err = json.Unmarshal(hookReturn, &hookMessageData)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// currently the only hook type
|
||||
if hookMessageData.Type != hooks.NextSteps {
|
||||
continue
|
||||
}
|
||||
|
||||
processedHook, err := hooks.ParseTemplate(hookMessageData.Template, subCmd)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var appended bool
|
||||
nextSteps, appended = appendNextSteps(nextSteps, processedHook)
|
||||
if !appended {
|
||||
logrus.Debugf("Plugin %s responded with an empty hook message %q. Ignoring.", pluginName, string(hookReturn))
|
||||
}
|
||||
}
|
||||
return nextSteps
|
||||
}
|
||||
|
||||
// appendNextSteps appends the processed hook output to the nextSteps slice.
|
||||
// If the processed hook output is empty, it is not appended.
|
||||
// Empty lines are not stripped if there's at least one non-empty line.
|
||||
func appendNextSteps(nextSteps []string, processed []string) ([]string, bool) {
|
||||
empty := true
|
||||
for _, l := range processed {
|
||||
if strings.TrimSpace(l) != "" {
|
||||
empty = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if empty {
|
||||
return nextSteps, false
|
||||
}
|
||||
|
||||
return append(nextSteps, processed...), true
|
||||
}
|
||||
|
||||
// pluginMatch takes a plugin configuration and a string representing the
|
||||
// command being executed (such as 'image ls' – the root 'docker' is omitted)
|
||||
// and, if the configuration includes a hook for the invoked command, returns
|
||||
// the configured hook string.
|
||||
func pluginMatch(pluginCfg map[string]string, subCmd string) (string, bool) {
|
||||
configuredPluginHooks, ok := pluginCfg["hooks"]
|
||||
if !ok || configuredPluginHooks == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
commands := strings.Split(configuredPluginHooks, ",")
|
||||
for _, hookCmd := range commands {
|
||||
if hookMatch(hookCmd, subCmd) {
|
||||
return hookCmd, true
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
func hookMatch(hookCmd, subCmd string) bool {
|
||||
hookCmdTokens := strings.Split(hookCmd, " ")
|
||||
subCmdTokens := strings.Split(subCmd, " ")
|
||||
|
||||
if len(hookCmdTokens) > len(subCmdTokens) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, v := range hookCmdTokens {
|
||||
if v != subCmdTokens[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func getCommandFlags(cmd *cobra.Command) map[string]string {
|
||||
flags := make(map[string]string)
|
||||
cmd.Flags().Visit(func(f *pflag.Flag) {
|
||||
var fValue string
|
||||
if f.Value.Type() == "bool" {
|
||||
fValue = f.Value.String()
|
||||
}
|
||||
flags[f.Name] = fValue
|
||||
})
|
||||
return flags
|
||||
}
|
||||
|
||||
// getNaiveFlags string-matches argv and parses them into a map.
|
||||
// This is used when calling hooks after a plugin command, since
|
||||
// in this case we can't rely on the cobra command tree to parse
|
||||
// flags in this case. In this case, no values are ever passed,
|
||||
// since we don't have enough information to process them.
|
||||
func getNaiveFlags(args []string) map[string]string {
|
||||
flags := make(map[string]string)
|
||||
for _, arg := range args {
|
||||
if strings.HasPrefix(arg, "--") {
|
||||
flags[arg[2:]] = ""
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(arg, "-") {
|
||||
flags[arg[1:]] = ""
|
||||
}
|
||||
}
|
||||
return flags
|
||||
}
|
247
vendor/github.com/docker/cli/cli-plugins/manager/manager.go
generated
vendored
247
vendor/github.com/docker/cli/cli-plugins/manager/manager.go
generated
vendored
@ -1,247 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/fvbommel/sortorder"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
const (
|
||||
// ReexecEnvvar is the name of an ennvar which is set to the command
|
||||
// used to originally invoke the docker CLI when executing a
|
||||
// plugin. Assuming $PATH and $CWD remain unchanged this should allow
|
||||
// the plugin to re-execute the original CLI.
|
||||
ReexecEnvvar = "DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND"
|
||||
|
||||
// ResourceAttributesEnvvar is the name of the envvar that includes additional
|
||||
// resource attributes for OTEL.
|
||||
ResourceAttributesEnvvar = "OTEL_RESOURCE_ATTRIBUTES"
|
||||
)
|
||||
|
||||
// errPluginNotFound is the error returned when a plugin could not be found.
|
||||
type errPluginNotFound string
|
||||
|
||||
func (errPluginNotFound) NotFound() {}
|
||||
|
||||
func (e errPluginNotFound) Error() string {
|
||||
return "Error: No such CLI plugin: " + string(e)
|
||||
}
|
||||
|
||||
type notFound interface{ NotFound() }
|
||||
|
||||
// IsNotFound is true if the given error is due to a plugin not being found.
|
||||
func IsNotFound(err error) bool {
|
||||
if e, ok := err.(*pluginError); ok {
|
||||
err = e.Cause()
|
||||
}
|
||||
_, ok := err.(notFound)
|
||||
return ok
|
||||
}
|
||||
|
||||
// getPluginDirs returns the platform-specific locations to search for plugins
|
||||
// in order of preference.
|
||||
//
|
||||
// Plugin-discovery is performed in the following order of preference:
|
||||
//
|
||||
// 1. The "cli-plugins" directory inside the CLIs [config.Path] (usually "~/.docker/cli-plugins").
|
||||
// 2. Additional plugin directories as configured through [ConfigFile.CLIPluginsExtraDirs].
|
||||
// 3. Platform-specific defaultSystemPluginDirs.
|
||||
//
|
||||
// [ConfigFile.CLIPluginsExtraDirs]: https://pkg.go.dev/github.com/docker/cli@v26.1.4+incompatible/cli/config/configfile#ConfigFile.CLIPluginsExtraDirs
|
||||
func getPluginDirs(cfg *configfile.ConfigFile) ([]string, error) {
|
||||
var pluginDirs []string
|
||||
|
||||
if cfg != nil {
|
||||
pluginDirs = append(pluginDirs, cfg.CLIPluginsExtraDirs...)
|
||||
}
|
||||
pluginDir, err := config.Path("cli-plugins")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pluginDirs = append(pluginDirs, pluginDir)
|
||||
pluginDirs = append(pluginDirs, defaultSystemPluginDirs...)
|
||||
return pluginDirs, nil
|
||||
}
|
||||
|
||||
func addPluginCandidatesFromDir(res map[string][]string, d string) {
|
||||
dentries, err := os.ReadDir(d)
|
||||
// Silently ignore any directories which we cannot list (e.g. due to
|
||||
// permissions or anything else) or which is not a directory
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, dentry := range dentries {
|
||||
switch dentry.Type() & os.ModeType {
|
||||
case 0, os.ModeSymlink:
|
||||
// Regular file or symlink, keep going
|
||||
default:
|
||||
// Something else, ignore.
|
||||
continue
|
||||
}
|
||||
name := dentry.Name()
|
||||
if !strings.HasPrefix(name, NamePrefix) {
|
||||
continue
|
||||
}
|
||||
name = strings.TrimPrefix(name, NamePrefix)
|
||||
var err error
|
||||
if name, err = trimExeSuffix(name); err != nil {
|
||||
continue
|
||||
}
|
||||
res[name] = append(res[name], filepath.Join(d, dentry.Name()))
|
||||
}
|
||||
}
|
||||
|
||||
// listPluginCandidates returns a map from plugin name to the list of (unvalidated) Candidates. The list is in descending order of priority.
|
||||
func listPluginCandidates(dirs []string) map[string][]string {
|
||||
result := make(map[string][]string)
|
||||
for _, d := range dirs {
|
||||
addPluginCandidatesFromDir(result, d)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetPlugin returns a plugin on the system by its name
|
||||
func GetPlugin(name string, dockerCli command.Cli, rootcmd *cobra.Command) (*Plugin, error) {
|
||||
pluginDirs, err := getPluginDirs(dockerCli.ConfigFile())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
candidates := listPluginCandidates(pluginDirs)
|
||||
if paths, ok := candidates[name]; ok {
|
||||
if len(paths) == 0 {
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
c := &candidate{paths[0]}
|
||||
p, err := newPlugin(c, rootcmd.Commands())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !IsNotFound(p.Err) {
|
||||
p.ShadowedPaths = paths[1:]
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
|
||||
// ListPlugins produces a list of the plugins available on the system
|
||||
func ListPlugins(dockerCli command.Cli, rootcmd *cobra.Command) ([]Plugin, error) {
|
||||
pluginDirs, err := getPluginDirs(dockerCli.ConfigFile())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
candidates := listPluginCandidates(pluginDirs)
|
||||
|
||||
var plugins []Plugin
|
||||
var mu sync.Mutex
|
||||
eg, _ := errgroup.WithContext(context.TODO())
|
||||
cmds := rootcmd.Commands()
|
||||
for _, paths := range candidates {
|
||||
func(paths []string) {
|
||||
eg.Go(func() error {
|
||||
if len(paths) == 0 {
|
||||
return nil
|
||||
}
|
||||
c := &candidate{paths[0]}
|
||||
p, err := newPlugin(c, cmds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !IsNotFound(p.Err) {
|
||||
p.ShadowedPaths = paths[1:]
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
plugins = append(plugins, p)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}(paths)
|
||||
}
|
||||
if err := eg.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Slice(plugins, func(i, j int) bool {
|
||||
return sortorder.NaturalLess(plugins[i].Name, plugins[j].Name)
|
||||
})
|
||||
|
||||
return plugins, nil
|
||||
}
|
||||
|
||||
// PluginRunCommand returns an "os/exec".Cmd which when .Run() will execute the named plugin.
|
||||
// The rootcmd argument is referenced to determine the set of builtin commands in order to detect conficts.
|
||||
// The error returned satisfies the IsNotFound() predicate if no plugin was found or if the first candidate plugin was invalid somehow.
|
||||
func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command) (*exec.Cmd, error) {
|
||||
// This uses the full original args, not the args which may
|
||||
// have been provided by cobra to our caller. This is because
|
||||
// they lack e.g. global options which we must propagate here.
|
||||
args := os.Args[1:]
|
||||
if !pluginNameRe.MatchString(name) {
|
||||
// We treat this as "not found" so that callers will
|
||||
// fallback to their "invalid" command path.
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
exename := addExeSuffix(NamePrefix + name)
|
||||
pluginDirs, err := getPluginDirs(dockerCli.ConfigFile())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, d := range pluginDirs {
|
||||
path := filepath.Join(d, exename)
|
||||
|
||||
// We stat here rather than letting the exec tell us
|
||||
// ENOENT because the latter does not distinguish a
|
||||
// file not existing from its dynamic loader or one of
|
||||
// its libraries not existing.
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
c := &candidate{path: path}
|
||||
plugin, err := newPlugin(c, rootcmd.Commands())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if plugin.Err != nil {
|
||||
// TODO: why are we not returning plugin.Err?
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
cmd := exec.Command(plugin.Path, args...) // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
|
||||
|
||||
// Using dockerCli.{In,Out,Err}() here results in a hang until something is input.
|
||||
// See: - https://github.com/golang/go/issues/10338
|
||||
// - https://github.com/golang/go/commit/d000e8742a173aa0659584aa01b7ba2834ba28ab
|
||||
// os.Stdin is a *os.File which avoids this behaviour. We don't need the functionality
|
||||
// of the wrappers here anyway.
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
cmd.Env = append(cmd.Environ(), ReexecEnvvar+"="+os.Args[0])
|
||||
cmd.Env = appendPluginResourceAttributesEnvvar(cmd.Env, rootcmd, plugin)
|
||||
|
||||
return cmd, nil
|
||||
}
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
|
||||
// IsPluginCommand checks if the given cmd is a plugin-stub.
|
||||
func IsPluginCommand(cmd *cobra.Command) bool {
|
||||
return cmd.Annotations[CommandAnnotationPlugin] == "true"
|
||||
}
|
20
vendor/github.com/docker/cli/cli-plugins/manager/manager_unix.go
generated
vendored
20
vendor/github.com/docker/cli/cli-plugins/manager/manager_unix.go
generated
vendored
@ -1,20 +0,0 @@
|
||||
//go:build !windows
|
||||
|
||||
package manager
|
||||
|
||||
// defaultSystemPluginDirs are the platform-specific locations to search
|
||||
// for plugins in order of preference.
|
||||
//
|
||||
// Plugin-discovery is performed in the following order of preference:
|
||||
//
|
||||
// 1. The "cli-plugins" directory inside the CLIs config-directory (usually "~/.docker/cli-plugins").
|
||||
// 2. Additional plugin directories as configured through [ConfigFile.CLIPluginsExtraDirs].
|
||||
// 3. Platform-specific defaultSystemPluginDirs (as defined below).
|
||||
//
|
||||
// [ConfigFile.CLIPluginsExtraDirs]: https://pkg.go.dev/github.com/docker/cli@v26.1.4+incompatible/cli/config/configfile#ConfigFile.CLIPluginsExtraDirs
|
||||
var defaultSystemPluginDirs = []string{
|
||||
"/usr/local/lib/docker/cli-plugins",
|
||||
"/usr/local/libexec/docker/cli-plugins",
|
||||
"/usr/lib/docker/cli-plugins",
|
||||
"/usr/libexec/docker/cli-plugins",
|
||||
}
|
21
vendor/github.com/docker/cli/cli-plugins/manager/manager_windows.go
generated
vendored
21
vendor/github.com/docker/cli/cli-plugins/manager/manager_windows.go
generated
vendored
@ -1,21 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// defaultSystemPluginDirs are the platform-specific locations to search
|
||||
// for plugins in order of preference.
|
||||
//
|
||||
// Plugin-discovery is performed in the following order of preference:
|
||||
//
|
||||
// 1. The "cli-plugins" directory inside the CLIs config-directory (usually "~/.docker/cli-plugins").
|
||||
// 2. Additional plugin directories as configured through [ConfigFile.CLIPluginsExtraDirs].
|
||||
// 3. Platform-specific defaultSystemPluginDirs (as defined below).
|
||||
//
|
||||
// [ConfigFile.CLIPluginsExtraDirs]: https://pkg.go.dev/github.com/docker/cli@v26.1.4+incompatible/cli/config/configfile#ConfigFile.CLIPluginsExtraDirs
|
||||
var defaultSystemPluginDirs = []string{
|
||||
filepath.Join(os.Getenv("ProgramData"), "Docker", "cli-plugins"),
|
||||
filepath.Join(os.Getenv("ProgramFiles"), "Docker", "cli-plugins"),
|
||||
}
|
124
vendor/github.com/docker/cli/cli-plugins/manager/plugin.go
generated
vendored
124
vendor/github.com/docker/cli/cli-plugins/manager/plugin.go
generated
vendored
@ -1,124 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var pluginNameRe = regexp.MustCompile("^[a-z][a-z0-9]*$")
|
||||
|
||||
// Plugin represents a potential plugin with all it's metadata.
|
||||
type Plugin struct {
|
||||
Metadata
|
||||
|
||||
Name string `json:",omitempty"`
|
||||
Path string `json:",omitempty"`
|
||||
|
||||
// Err is non-nil if the plugin failed one of the candidate tests.
|
||||
Err error `json:",omitempty"`
|
||||
|
||||
// ShadowedPaths contains the paths of any other plugins which this plugin takes precedence over.
|
||||
ShadowedPaths []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// newPlugin determines if the given candidate is valid and returns a
|
||||
// Plugin. If the candidate fails one of the tests then `Plugin.Err`
|
||||
// is set, and is always a `pluginError`, but the `Plugin` is still
|
||||
// returned with no error. An error is only returned due to a
|
||||
// non-recoverable error.
|
||||
func newPlugin(c Candidate, cmds []*cobra.Command) (Plugin, error) {
|
||||
path := c.Path()
|
||||
if path == "" {
|
||||
return Plugin{}, errors.New("plugin candidate path cannot be empty")
|
||||
}
|
||||
|
||||
// The candidate listing process should have skipped anything
|
||||
// which would fail here, so there are all real errors.
|
||||
fullname := filepath.Base(path)
|
||||
if fullname == "." {
|
||||
return Plugin{}, errors.Errorf("unable to determine basename of plugin candidate %q", path)
|
||||
}
|
||||
var err error
|
||||
if fullname, err = trimExeSuffix(fullname); err != nil {
|
||||
return Plugin{}, errors.Wrapf(err, "plugin candidate %q", path)
|
||||
}
|
||||
if !strings.HasPrefix(fullname, NamePrefix) {
|
||||
return Plugin{}, errors.Errorf("plugin candidate %q: does not have %q prefix", path, NamePrefix)
|
||||
}
|
||||
|
||||
p := Plugin{
|
||||
Name: strings.TrimPrefix(fullname, NamePrefix),
|
||||
Path: path,
|
||||
}
|
||||
|
||||
// Now apply the candidate tests, so these update p.Err.
|
||||
if !pluginNameRe.MatchString(p.Name) {
|
||||
p.Err = NewPluginError("plugin candidate %q did not match %q", p.Name, pluginNameRe.String())
|
||||
return p, nil
|
||||
}
|
||||
|
||||
for _, cmd := range cmds {
|
||||
// Ignore conflicts with commands which are
|
||||
// just plugin stubs (i.e. from a previous
|
||||
// call to AddPluginCommandStubs).
|
||||
if IsPluginCommand(cmd) {
|
||||
continue
|
||||
}
|
||||
if cmd.Name() == p.Name {
|
||||
p.Err = NewPluginError("plugin %q duplicates builtin command", p.Name)
|
||||
return p, nil
|
||||
}
|
||||
if cmd.HasAlias(p.Name) {
|
||||
p.Err = NewPluginError("plugin %q duplicates an alias of builtin command %q", p.Name, cmd.Name())
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
// We are supposed to check for relevant execute permissions here. Instead we rely on an attempt to execute.
|
||||
meta, err := c.Metadata()
|
||||
if err != nil {
|
||||
p.Err = wrapAsPluginError(err, "failed to fetch metadata")
|
||||
return p, nil
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(meta, &p.Metadata); err != nil {
|
||||
p.Err = wrapAsPluginError(err, "invalid metadata")
|
||||
return p, nil
|
||||
}
|
||||
if p.Metadata.SchemaVersion != "0.1.0" {
|
||||
p.Err = NewPluginError("plugin SchemaVersion %q is not valid, must be 0.1.0", p.Metadata.SchemaVersion)
|
||||
return p, nil
|
||||
}
|
||||
if p.Metadata.Vendor == "" {
|
||||
p.Err = NewPluginError("plugin metadata does not define a vendor")
|
||||
return p, nil
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// RunHook executes the plugin's hooks command
|
||||
// and returns its unprocessed output.
|
||||
func (p *Plugin) RunHook(ctx context.Context, hookData HookPluginData) ([]byte, error) {
|
||||
hDataBytes, err := json.Marshal(hookData)
|
||||
if err != nil {
|
||||
return nil, wrapAsPluginError(err, "failed to marshall hook data")
|
||||
}
|
||||
|
||||
pCmd := exec.CommandContext(ctx, p.Path, p.Name, HookSubcommandName, string(hDataBytes)) // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
|
||||
pCmd.Env = os.Environ()
|
||||
pCmd.Env = append(pCmd.Env, ReexecEnvvar+"="+os.Args[0])
|
||||
hookCmdOutput, err := pCmd.Output()
|
||||
if err != nil {
|
||||
return nil, wrapAsPluginError(err, "failed to execute plugin hook subcommand")
|
||||
}
|
||||
|
||||
return hookCmdOutput, nil
|
||||
}
|
11
vendor/github.com/docker/cli/cli-plugins/manager/suffix_unix.go
generated
vendored
11
vendor/github.com/docker/cli/cli-plugins/manager/suffix_unix.go
generated
vendored
@ -1,11 +0,0 @@
|
||||
//go:build !windows
|
||||
|
||||
package manager
|
||||
|
||||
func trimExeSuffix(s string) (string, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func addExeSuffix(s string) string {
|
||||
return s
|
||||
}
|
26
vendor/github.com/docker/cli/cli-plugins/manager/suffix_windows.go
generated
vendored
26
vendor/github.com/docker/cli/cli-plugins/manager/suffix_windows.go
generated
vendored
@ -1,26 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// This is made slightly more complex due to needing to be case insensitive.
|
||||
func trimExeSuffix(s string) (string, error) {
|
||||
ext := filepath.Ext(s)
|
||||
if ext == "" {
|
||||
return "", errors.Errorf("path %q lacks required file extension", s)
|
||||
}
|
||||
|
||||
exe := ".exe"
|
||||
if !strings.EqualFold(ext, exe) {
|
||||
return "", errors.Errorf("path %q lacks required %q suffix", s, exe)
|
||||
}
|
||||
return strings.TrimSuffix(s, ext), nil
|
||||
}
|
||||
|
||||
func addExeSuffix(s string) string {
|
||||
return s + ".exe"
|
||||
}
|
28
vendor/github.com/docker/cli/cli-plugins/metadata/annotations.go
generated
vendored
Normal file
28
vendor/github.com/docker/cli/cli-plugins/metadata/annotations.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package metadata
|
||||
|
||||
const (
|
||||
// CommandAnnotationPlugin is added to every stub command added by
|
||||
// AddPluginCommandStubs with the value "true" and so can be
|
||||
// used to distinguish plugin stubs from regular commands.
|
||||
CommandAnnotationPlugin = "com.docker.cli.plugin"
|
||||
|
||||
// CommandAnnotationPluginVendor is added to every stub command
|
||||
// added by AddPluginCommandStubs and contains the vendor of
|
||||
// that plugin.
|
||||
CommandAnnotationPluginVendor = "com.docker.cli.plugin.vendor"
|
||||
|
||||
// CommandAnnotationPluginVersion is added to every stub command
|
||||
// added by AddPluginCommandStubs and contains the version of
|
||||
// that plugin.
|
||||
CommandAnnotationPluginVersion = "com.docker.cli.plugin.version"
|
||||
|
||||
// CommandAnnotationPluginInvalid is added to any stub command
|
||||
// added by AddPluginCommandStubs for an invalid command (that
|
||||
// is, one which failed it's candidate test) and contains the
|
||||
// reason for the failure.
|
||||
CommandAnnotationPluginInvalid = "com.docker.cli.plugin-invalid"
|
||||
|
||||
// CommandAnnotationPluginCommandPath is added to overwrite the
|
||||
// command path for a plugin invocation.
|
||||
CommandAnnotationPluginCommandPath = "com.docker.cli.plugin.command_path"
|
||||
)
|
@ -1,4 +1,4 @@
|
||||
package manager
|
||||
package metadata
|
||||
|
||||
const (
|
||||
// NamePrefix is the prefix required on all plugin binary names
|
||||
@ -13,6 +13,12 @@ const (
|
||||
// which must be implemented by plugins declaring support
|
||||
// for hooks in their metadata.
|
||||
HookSubcommandName = "docker-cli-plugin-hooks"
|
||||
|
||||
// ReexecEnvvar is the name of an ennvar which is set to the command
|
||||
// used to originally invoke the docker CLI when executing a
|
||||
// plugin. Assuming $PATH and $CWD remain unchanged this should allow
|
||||
// the plugin to re-execute the original CLI.
|
||||
ReexecEnvvar = "DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND"
|
||||
)
|
||||
|
||||
// Metadata provided by the plugin.
|
20
vendor/github.com/docker/cli/cli-plugins/plugin/plugin.go
generated
vendored
20
vendor/github.com/docker/cli/cli-plugins/plugin/plugin.go
generated
vendored
@ -9,7 +9,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli-plugins/socket"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/connhelper"
|
||||
@ -30,7 +30,7 @@ import (
|
||||
var PersistentPreRunE func(*cobra.Command, []string) error
|
||||
|
||||
// RunPlugin executes the specified plugin command
|
||||
func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager.Metadata) error {
|
||||
func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta metadata.Metadata) error {
|
||||
tcmd := newPluginCommand(dockerCli, plugin, meta)
|
||||
|
||||
var persistentPreRunOnce sync.Once
|
||||
@ -81,7 +81,7 @@ func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager
|
||||
}
|
||||
|
||||
// Run is the top-level entry point to the CLI plugin framework. It should be called from your plugin's `main()` function.
|
||||
func Run(makeCmd func(command.Cli) *cobra.Command, meta manager.Metadata) {
|
||||
func Run(makeCmd func(command.Cli) *cobra.Command, meta metadata.Metadata) {
|
||||
otel.SetErrorHandler(debug.OTELErrorHandler)
|
||||
|
||||
dockerCli, err := command.NewDockerCli()
|
||||
@ -111,7 +111,7 @@ func Run(makeCmd func(command.Cli) *cobra.Command, meta manager.Metadata) {
|
||||
func withPluginClientConn(name string) command.CLIOption {
|
||||
return command.WithInitializeClient(func(dockerCli *command.DockerCli) (client.APIClient, error) {
|
||||
cmd := "docker"
|
||||
if x := os.Getenv(manager.ReexecEnvvar); x != "" {
|
||||
if x := os.Getenv(metadata.ReexecEnvvar); x != "" {
|
||||
cmd = x
|
||||
}
|
||||
var flags []string
|
||||
@ -140,9 +140,9 @@ func withPluginClientConn(name string) command.CLIOption {
|
||||
})
|
||||
}
|
||||
|
||||
func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager.Metadata) *cli.TopLevelCommand {
|
||||
func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta metadata.Metadata) *cli.TopLevelCommand {
|
||||
name := plugin.Name()
|
||||
fullname := manager.NamePrefix + name
|
||||
fullname := metadata.NamePrefix + name
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: fmt.Sprintf("docker [OPTIONS] %s [ARG...]", name),
|
||||
@ -177,12 +177,12 @@ func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta
|
||||
return cli.NewTopLevelCommand(cmd, dockerCli, opts, cmd.Flags())
|
||||
}
|
||||
|
||||
func newMetadataSubcommand(plugin *cobra.Command, meta manager.Metadata) *cobra.Command {
|
||||
func newMetadataSubcommand(plugin *cobra.Command, meta metadata.Metadata) *cobra.Command {
|
||||
if meta.ShortDescription == "" {
|
||||
meta.ShortDescription = plugin.Short
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: manager.MetadataSubcommandName,
|
||||
Use: metadata.MetadataSubcommandName,
|
||||
Hidden: true,
|
||||
// Suppress the global/parent PersistentPreRunE, which
|
||||
// needlessly initializes the client and tries to
|
||||
@ -200,8 +200,8 @@ func newMetadataSubcommand(plugin *cobra.Command, meta manager.Metadata) *cobra.
|
||||
|
||||
// RunningStandalone tells a CLI plugin it is run standalone by direct execution
|
||||
func RunningStandalone() bool {
|
||||
if os.Getenv(manager.ReexecEnvvar) != "" {
|
||||
if os.Getenv(metadata.ReexecEnvvar) != "" {
|
||||
return false
|
||||
}
|
||||
return len(os.Args) < 2 || os.Args[1] != manager.MetadataSubcommandName
|
||||
return len(os.Args) < 2 || os.Args[1] != metadata.MetadataSubcommandName
|
||||
}
|
||||
|
20
vendor/github.com/docker/cli/cli/cobra.go
generated
vendored
20
vendor/github.com/docker/cli/cli/cobra.go
generated
vendored
@ -3,15 +3,12 @@ package cli
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
pluginmanager "github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli/command"
|
||||
cliflags "github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/docker/pkg/homedir"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/fvbommel/sortorder"
|
||||
"github.com/moby/term"
|
||||
"github.com/morikuni/aec"
|
||||
@ -62,13 +59,6 @@ func setupCommonRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *c
|
||||
"docs.code-delimiter": `"`, // https://github.com/docker/cli-docs-tool/blob/77abede22166eaea4af7335096bdcedd043f5b19/annotation/annotation.go#L20-L22
|
||||
}
|
||||
|
||||
// Configure registry.CertsDir() when running in rootless-mode
|
||||
if os.Getenv("ROOTLESSKIT_STATE_DIR") != "" {
|
||||
if configHome, err := homedir.GetConfigHome(); err == nil {
|
||||
registry.SetCertsDir(filepath.Join(configHome, "docker/certs.d"))
|
||||
}
|
||||
}
|
||||
|
||||
return opts, helpCommand
|
||||
}
|
||||
|
||||
@ -252,7 +242,7 @@ func hasAdditionalHelp(cmd *cobra.Command) bool {
|
||||
}
|
||||
|
||||
func isPlugin(cmd *cobra.Command) bool {
|
||||
return pluginmanager.IsPluginCommand(cmd)
|
||||
return cmd.Annotations[metadata.CommandAnnotationPlugin] == "true"
|
||||
}
|
||||
|
||||
func hasAliases(cmd *cobra.Command) bool {
|
||||
@ -356,9 +346,9 @@ func decoratedName(cmd *cobra.Command) string {
|
||||
}
|
||||
|
||||
func vendorAndVersion(cmd *cobra.Command) string {
|
||||
if vendor, ok := cmd.Annotations[pluginmanager.CommandAnnotationPluginVendor]; ok && isPlugin(cmd) {
|
||||
if vendor, ok := cmd.Annotations[metadata.CommandAnnotationPluginVendor]; ok && isPlugin(cmd) {
|
||||
version := ""
|
||||
if v, ok := cmd.Annotations[pluginmanager.CommandAnnotationPluginVersion]; ok && v != "" {
|
||||
if v, ok := cmd.Annotations[metadata.CommandAnnotationPluginVersion]; ok && v != "" {
|
||||
version = ", " + v
|
||||
}
|
||||
return fmt.Sprintf("(%s%s)", vendor, version)
|
||||
@ -417,7 +407,7 @@ func invalidPlugins(cmd *cobra.Command) []*cobra.Command {
|
||||
}
|
||||
|
||||
func invalidPluginReason(cmd *cobra.Command) string {
|
||||
return cmd.Annotations[pluginmanager.CommandAnnotationPluginInvalid]
|
||||
return cmd.Annotations[metadata.CommandAnnotationPluginInvalid]
|
||||
}
|
||||
|
||||
const usageTemplate = `Usage:
|
||||
|
59
vendor/github.com/docker/cli/cli/command/cli.go
generated
vendored
59
vendor/github.com/docker/cli/cli/command/cli.go
generated
vendored
@ -8,7 +8,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
@ -21,21 +20,15 @@ import (
|
||||
"github.com/docker/cli/cli/context/store"
|
||||
"github.com/docker/cli/cli/debug"
|
||||
cliflags "github.com/docker/cli/cli/flags"
|
||||
manifeststore "github.com/docker/cli/cli/manifest/store"
|
||||
registryclient "github.com/docker/cli/cli/registry/client"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/docker/cli/cli/version"
|
||||
dopts "github.com/docker/cli/opts"
|
||||
"github.com/docker/docker/api"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
notaryclient "github.com/theupdateframework/notary/client"
|
||||
)
|
||||
|
||||
const defaultInitTimeout = 2 * time.Second
|
||||
@ -53,19 +46,18 @@ type Cli interface {
|
||||
Streams
|
||||
SetIn(in *streams.In)
|
||||
Apply(ops ...CLIOption) error
|
||||
ConfigFile() *configfile.ConfigFile
|
||||
config.Provider
|
||||
ServerInfo() ServerInfo
|
||||
NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (notaryclient.Repository, error)
|
||||
DefaultVersion() string
|
||||
CurrentVersion() string
|
||||
ManifestStore() manifeststore.Store
|
||||
RegistryClient(bool) registryclient.RegistryClient
|
||||
ContentTrustEnabled() bool
|
||||
BuildKitEnabled() (bool, error)
|
||||
ContextStore() store.Store
|
||||
CurrentContext() string
|
||||
DockerEndpoint() docker.Endpoint
|
||||
TelemetryClient
|
||||
DeprecatedNotaryClient
|
||||
DeprecatedManifestClient
|
||||
}
|
||||
|
||||
// DockerCli is an instance the docker command line client.
|
||||
@ -96,7 +88,7 @@ type DockerCli struct {
|
||||
enableGlobalMeter, enableGlobalTracer bool
|
||||
}
|
||||
|
||||
// DefaultVersion returns api.defaultVersion.
|
||||
// DefaultVersion returns [api.DefaultVersion].
|
||||
func (*DockerCli) DefaultVersion() string {
|
||||
return api.DefaultVersion
|
||||
}
|
||||
@ -202,16 +194,16 @@ func (cli *DockerCli) BuildKitEnabled() (bool, error) {
|
||||
|
||||
// HooksEnabled returns whether plugin hooks are enabled.
|
||||
func (cli *DockerCli) HooksEnabled() bool {
|
||||
// legacy support DOCKER_CLI_HINTS env var
|
||||
if v := os.Getenv("DOCKER_CLI_HINTS"); v != "" {
|
||||
// use DOCKER_CLI_HOOKS env var value if set and not empty
|
||||
if v := os.Getenv("DOCKER_CLI_HOOKS"); v != "" {
|
||||
enabled, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return enabled
|
||||
}
|
||||
// use DOCKER_CLI_HOOKS env var value if set and not empty
|
||||
if v := os.Getenv("DOCKER_CLI_HOOKS"); v != "" {
|
||||
// legacy support DOCKER_CLI_HINTS env var
|
||||
if v := os.Getenv("DOCKER_CLI_HINTS"); v != "" {
|
||||
enabled, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return false
|
||||
@ -230,21 +222,6 @@ func (cli *DockerCli) HooksEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ManifestStore returns a store for local manifests
|
||||
func (*DockerCli) ManifestStore() manifeststore.Store {
|
||||
// TODO: support override default location from config file
|
||||
return manifeststore.NewStore(filepath.Join(config.Dir(), "manifests"))
|
||||
}
|
||||
|
||||
// RegistryClient returns a client for communicating with a Docker distribution
|
||||
// registry
|
||||
func (cli *DockerCli) RegistryClient(allowInsecure bool) registryclient.RegistryClient {
|
||||
resolver := func(ctx context.Context, index *registry.IndexInfo) registry.AuthConfig {
|
||||
return ResolveAuthConfig(cli.ConfigFile(), index)
|
||||
}
|
||||
return registryclient.NewRegistryClient(resolver, UserAgent(), allowInsecure)
|
||||
}
|
||||
|
||||
// WithInitializeClient is passed to DockerCli.Initialize by callers who wish to set a particular API Client for use by the CLI.
|
||||
func WithInitializeClient(makeClient func(dockerCli *DockerCli) (client.APIClient, error)) CLIOption {
|
||||
return func(dockerCli *DockerCli) error {
|
||||
@ -292,6 +269,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...CLIOption)
|
||||
if cli.enableGlobalTracer {
|
||||
cli.createGlobalTracerProvider(cli.baseCtx)
|
||||
}
|
||||
filterResourceAttributesEnvvar()
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -345,7 +323,10 @@ func resolveDockerEndpoint(s store.Reader, contextName string) (docker.Endpoint,
|
||||
|
||||
// Resolve the Docker endpoint for the default context (based on config, env vars and CLI flags)
|
||||
func resolveDefaultDockerEndpoint(opts *cliflags.ClientOptions) (docker.Endpoint, error) {
|
||||
host, err := getServerHost(opts.Hosts, opts.TLSOptions)
|
||||
// defaultToTLS determines whether we should use a TLS host as default
|
||||
// if nothing was configured by the user.
|
||||
defaultToTLS := opts.TLSOptions != nil
|
||||
host, err := getServerHost(opts.Hosts, defaultToTLS)
|
||||
if err != nil {
|
||||
return docker.Endpoint{}, err
|
||||
}
|
||||
@ -403,11 +384,6 @@ func (cli *DockerCli) initializeFromClient() {
|
||||
cli.client.NegotiateAPIVersionPing(ping)
|
||||
}
|
||||
|
||||
// NotaryClient provides a Notary Repository to interact with signed metadata for an image
|
||||
func (cli *DockerCli) NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (notaryclient.Repository, error) {
|
||||
return trust.GetNotaryRepository(cli.In(), cli.Out(), UserAgent(), imgRefAndAuth.RepoInfo(), imgRefAndAuth.AuthConfig(), actions...)
|
||||
}
|
||||
|
||||
// ContextStore returns the ContextStore
|
||||
func (cli *DockerCli) ContextStore() store.Store {
|
||||
return cli.contextStore
|
||||
@ -553,18 +529,15 @@ func NewDockerCli(ops ...CLIOption) (*DockerCli, error) {
|
||||
return cli, nil
|
||||
}
|
||||
|
||||
func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (string, error) {
|
||||
var host string
|
||||
func getServerHost(hosts []string, defaultToTLS bool) (string, error) {
|
||||
switch len(hosts) {
|
||||
case 0:
|
||||
host = os.Getenv(client.EnvOverrideHost)
|
||||
return dopts.ParseHost(defaultToTLS, os.Getenv(client.EnvOverrideHost))
|
||||
case 1:
|
||||
host = hosts[0]
|
||||
return dopts.ParseHost(defaultToTLS, hosts[0])
|
||||
default:
|
||||
return "", errors.New("Specify only one -H")
|
||||
}
|
||||
|
||||
return dopts.ParseHost(tlsOptions != nil, host)
|
||||
}
|
||||
|
||||
// UserAgent returns the user agent string used for making API requests
|
||||
|
56
vendor/github.com/docker/cli/cli/command/cli_deprecated.go
generated
vendored
Normal file
56
vendor/github.com/docker/cli/cli/command/cli_deprecated.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/cli/cli/config"
|
||||
manifeststore "github.com/docker/cli/cli/manifest/store"
|
||||
registryclient "github.com/docker/cli/cli/registry/client"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
notaryclient "github.com/theupdateframework/notary/client"
|
||||
)
|
||||
|
||||
type DeprecatedNotaryClient interface {
|
||||
// NotaryClient provides a Notary Repository to interact with signed metadata for an image
|
||||
//
|
||||
// Deprecated: use [trust.GetNotaryRepository] instead. This method is no longer used and will be removed in the next release.
|
||||
NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (notaryclient.Repository, error)
|
||||
}
|
||||
|
||||
type DeprecatedManifestClient interface {
|
||||
// ManifestStore returns a store for local manifests
|
||||
//
|
||||
// Deprecated: use [manifeststore.NewStore] instead. This method is no longer used and will be removed in the next release.
|
||||
ManifestStore() manifeststore.Store
|
||||
|
||||
// RegistryClient returns a client for communicating with a Docker distribution
|
||||
// registry.
|
||||
//
|
||||
// Deprecated: use [registryclient.NewRegistryClient]. This method is no longer used and will be removed in the next release.
|
||||
RegistryClient(bool) registryclient.RegistryClient
|
||||
}
|
||||
|
||||
// NotaryClient provides a Notary Repository to interact with signed metadata for an image
|
||||
func (cli *DockerCli) NotaryClient(imgRefAndAuth trust.ImageRefAndAuth, actions []string) (notaryclient.Repository, error) {
|
||||
return trust.GetNotaryRepository(cli.In(), cli.Out(), UserAgent(), imgRefAndAuth.RepoInfo(), imgRefAndAuth.AuthConfig(), actions...)
|
||||
}
|
||||
|
||||
// ManifestStore returns a store for local manifests
|
||||
//
|
||||
// Deprecated: use [manifeststore.NewStore] instead. This method is no longer used and will be removed in the next release.
|
||||
func (*DockerCli) ManifestStore() manifeststore.Store {
|
||||
return manifeststore.NewStore(filepath.Join(config.Dir(), "manifests"))
|
||||
}
|
||||
|
||||
// RegistryClient returns a client for communicating with a Docker distribution
|
||||
// registry
|
||||
//
|
||||
// Deprecated: use [registryclient.NewRegistryClient]. This method is no longer used and will be removed in the next release.
|
||||
func (cli *DockerCli) RegistryClient(allowInsecure bool) registryclient.RegistryClient {
|
||||
resolver := func(ctx context.Context, index *registry.IndexInfo) registry.AuthConfig {
|
||||
return ResolveAuthConfig(cli.ConfigFile(), index)
|
||||
}
|
||||
return registryclient.NewRegistryClient(resolver, UserAgent(), allowInsecure)
|
||||
}
|
29
vendor/github.com/docker/cli/cli/command/formatter/displayutils.go
generated
vendored
29
vendor/github.com/docker/cli/cli/command/formatter/displayutils.go
generated
vendored
@ -1,6 +1,11 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.22
|
||||
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/text/width"
|
||||
@ -59,3 +64,27 @@ func Ellipsis(s string, maxDisplayWidth int) string {
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// capitalizeFirst capitalizes the first character of string
|
||||
func capitalizeFirst(s string) string {
|
||||
switch l := len(s); l {
|
||||
case 0:
|
||||
return s
|
||||
case 1:
|
||||
return strings.ToLower(s)
|
||||
default:
|
||||
return strings.ToUpper(string(s[0])) + strings.ToLower(s[1:])
|
||||
}
|
||||
}
|
||||
|
||||
// PrettyPrint outputs arbitrary data for human formatted output by uppercasing the first letter.
|
||||
func PrettyPrint(i any) string {
|
||||
switch t := i.(type) {
|
||||
case nil:
|
||||
return "None"
|
||||
case string:
|
||||
return capitalizeFirst(t)
|
||||
default:
|
||||
return capitalizeFirst(fmt.Sprintf("%s", t))
|
||||
}
|
||||
}
|
||||
|
4
vendor/github.com/docker/cli/cli/command/formatter/formatter.go
generated
vendored
4
vendor/github.com/docker/cli/cli/command/formatter/formatter.go
generated
vendored
@ -76,9 +76,9 @@ func (c *Context) preFormat() {
|
||||
func (c *Context) parseFormat() (*template.Template, error) {
|
||||
tmpl, err := templates.Parse(c.finalFormat)
|
||||
if err != nil {
|
||||
return tmpl, errors.Wrap(err, "template parsing error")
|
||||
return nil, errors.Wrap(err, "template parsing error")
|
||||
}
|
||||
return tmpl, err
|
||||
return tmpl, nil
|
||||
}
|
||||
|
||||
func (c *Context) postFormat(tmpl *template.Template, subContext SubContext) {
|
||||
|
51
vendor/github.com/docker/cli/cli/command/telemetry.go
generated
vendored
51
vendor/github.com/docker/cli/cli/command/telemetry.go
generated
vendored
@ -4,10 +4,11 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/uuid"
|
||||
"github.com/google/uuid"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
|
||||
@ -142,7 +143,7 @@ func defaultResourceOptions() []resource.Option {
|
||||
// of the CLI is its own instance. Without this, downstream
|
||||
// OTEL processors may think the same process is restarting
|
||||
// continuously.
|
||||
semconv.ServiceInstanceID(uuid.Generate().String()),
|
||||
semconv.ServiceInstanceID(uuid.NewString()),
|
||||
),
|
||||
resource.WithFromEnv(),
|
||||
resource.WithTelemetrySDK(),
|
||||
@ -216,3 +217,49 @@ func (r *cliReader) ForceFlush(ctx context.Context) error {
|
||||
func deltaTemporality(_ sdkmetric.InstrumentKind) metricdata.Temporality {
|
||||
return metricdata.DeltaTemporality
|
||||
}
|
||||
|
||||
// resourceAttributesEnvVar is the name of the envvar that includes additional
|
||||
// resource attributes for OTEL as defined in the [OpenTelemetry specification].
|
||||
//
|
||||
// [OpenTelemetry specification]: https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/#general-sdk-configuration
|
||||
const resourceAttributesEnvVar = "OTEL_RESOURCE_ATTRIBUTES"
|
||||
|
||||
func filterResourceAttributesEnvvar() {
|
||||
if v := os.Getenv(resourceAttributesEnvVar); v != "" {
|
||||
if filtered := filterResourceAttributes(v); filtered != "" {
|
||||
_ = os.Setenv(resourceAttributesEnvVar, filtered)
|
||||
} else {
|
||||
_ = os.Unsetenv(resourceAttributesEnvVar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dockerCLIAttributePrefix is the prefix for any docker cli OTEL attributes.
|
||||
// When updating, make sure to also update the copy in cli-plugins/manager.
|
||||
//
|
||||
// TODO(thaJeztah): move telemetry-related code to an (internal) package to reduce dependency on cli/command in cli-plugins, which has too many imports.
|
||||
const dockerCLIAttributePrefix = "docker.cli."
|
||||
|
||||
func filterResourceAttributes(s string) string {
|
||||
if trimmed := strings.TrimSpace(s); trimmed == "" {
|
||||
return trimmed
|
||||
}
|
||||
|
||||
pairs := strings.Split(s, ",")
|
||||
elems := make([]string, 0, len(pairs))
|
||||
for _, p := range pairs {
|
||||
k, _, found := strings.Cut(p, "=")
|
||||
if !found {
|
||||
// Do not interact with invalid otel resources.
|
||||
elems = append(elems, p)
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip attributes that have our docker.cli prefix.
|
||||
if strings.HasPrefix(k, dockerCLIAttributePrefix) {
|
||||
continue
|
||||
}
|
||||
elems = append(elems, p)
|
||||
}
|
||||
return strings.Join(elems, ",")
|
||||
}
|
||||
|
79
vendor/github.com/docker/cli/cli/command/utils.go
generated
vendored
79
vendor/github.com/docker/cli/cli/command/utils.go
generated
vendored
@ -13,10 +13,9 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
mounttypes "github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/moby/sys/sequential"
|
||||
"github.com/moby/term"
|
||||
@ -51,30 +50,6 @@ func CopyToFile(outfile string, r io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// capitalizeFirst capitalizes the first character of string
|
||||
func capitalizeFirst(s string) string {
|
||||
switch l := len(s); l {
|
||||
case 0:
|
||||
return s
|
||||
case 1:
|
||||
return strings.ToLower(s)
|
||||
default:
|
||||
return strings.ToUpper(string(s[0])) + strings.ToLower(s[1:])
|
||||
}
|
||||
}
|
||||
|
||||
// PrettyPrint outputs arbitrary data for human formatted output by uppercasing the first letter.
|
||||
func PrettyPrint(i any) string {
|
||||
switch t := i.(type) {
|
||||
case nil:
|
||||
return "None"
|
||||
case string:
|
||||
return capitalizeFirst(t)
|
||||
default:
|
||||
return capitalizeFirst(fmt.Sprintf("%s", t))
|
||||
}
|
||||
}
|
||||
|
||||
var ErrPromptTerminated = errdefs.Cancelled(errors.New("prompt terminated"))
|
||||
|
||||
// DisableInputEcho disables input echo on the provided streams.In.
|
||||
@ -166,11 +141,12 @@ func PromptForConfirmation(ctx context.Context, ins io.Reader, outs io.Writer, m
|
||||
}
|
||||
|
||||
// PruneFilters returns consolidated prune filters obtained from config.json and cli
|
||||
func PruneFilters(dockerCli Cli, pruneFilters filters.Args) filters.Args {
|
||||
if dockerCli.ConfigFile() == nil {
|
||||
func PruneFilters(dockerCLI config.Provider, pruneFilters filters.Args) filters.Args {
|
||||
cfg := dockerCLI.ConfigFile()
|
||||
if cfg == nil {
|
||||
return pruneFilters
|
||||
}
|
||||
for _, f := range dockerCli.ConfigFile().PruneFilters {
|
||||
for _, f := range cfg.PruneFilters {
|
||||
k, v, ok := strings.Cut(f, "=")
|
||||
if !ok {
|
||||
continue
|
||||
@ -239,48 +215,3 @@ func ValidateOutputPathFileMode(fileMode os.FileMode) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stringSliceIndex(s, subs []string) int {
|
||||
j := 0
|
||||
if len(subs) > 0 {
|
||||
for i, x := range s {
|
||||
if j < len(subs) && subs[j] == x {
|
||||
j++
|
||||
} else {
|
||||
j = 0
|
||||
}
|
||||
if len(subs) == j {
|
||||
return i + 1 - j
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// StringSliceReplaceAt replaces the sub-slice find, with the sub-slice replace, in the string
|
||||
// slice s, returning a new slice and a boolean indicating if the replacement happened.
|
||||
// requireIdx is the index at which old needs to be found at (or -1 to disregard that).
|
||||
func StringSliceReplaceAt(s, find, replace []string, requireIndex int) ([]string, bool) {
|
||||
idx := stringSliceIndex(s, find)
|
||||
if (requireIndex != -1 && requireIndex != idx) || idx == -1 {
|
||||
return s, false
|
||||
}
|
||||
out := append([]string{}, s[:idx]...)
|
||||
out = append(out, replace...)
|
||||
out = append(out, s[idx+len(find):]...)
|
||||
return out, true
|
||||
}
|
||||
|
||||
// ValidateMountWithAPIVersion validates a mount with the server API version.
|
||||
func ValidateMountWithAPIVersion(m mounttypes.Mount, serverAPIVersion string) error {
|
||||
if m.BindOptions != nil {
|
||||
if m.BindOptions.NonRecursive && versions.LessThan(serverAPIVersion, "1.40") {
|
||||
return errors.Errorf("bind-recursive=disabled requires API v1.40 or later")
|
||||
}
|
||||
// ReadOnlyNonRecursive can be safely ignored when API < 1.44
|
||||
if m.BindOptions.ReadOnlyForceRecursive && versions.LessThan(serverAPIVersion, "1.44") {
|
||||
return errors.Errorf("bind-recursive=readonly requires API v1.44 or later")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
5
vendor/github.com/docker/cli/cli/config/config.go
generated
vendored
5
vendor/github.com/docker/cli/cli/config/config.go
generated
vendored
@ -69,6 +69,11 @@ func getHomeDir() string {
|
||||
return home
|
||||
}
|
||||
|
||||
// Provider defines an interface for providing the CLI config.
|
||||
type Provider interface {
|
||||
ConfigFile() *configfile.ConfigFile
|
||||
}
|
||||
|
||||
// Dir returns the directory the configuration file is stored in
|
||||
func Dir() string {
|
||||
initConfigDir.Do(func() {
|
||||
|
68
vendor/github.com/docker/cli/cli/internal/jsonstream/display.go
generated
vendored
Normal file
68
vendor/github.com/docker/cli/cli/internal/jsonstream/display.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
package jsonstream
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
)
|
||||
|
||||
type (
|
||||
Stream = jsonmessage.Stream
|
||||
JSONMessage = jsonmessage.JSONMessage
|
||||
JSONError = jsonmessage.JSONError
|
||||
JSONProgress = jsonmessage.JSONProgress
|
||||
)
|
||||
|
||||
type ctxReader struct {
|
||||
err chan error
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (r *ctxReader) Read(p []byte) (n int, err error) {
|
||||
select {
|
||||
case err = <-r.err:
|
||||
return 0, err
|
||||
default:
|
||||
return r.r.Read(p)
|
||||
}
|
||||
}
|
||||
|
||||
type Options func(*options)
|
||||
|
||||
type options struct {
|
||||
AuxCallback func(JSONMessage)
|
||||
}
|
||||
|
||||
func WithAuxCallback(cb func(JSONMessage)) Options {
|
||||
return func(o *options) {
|
||||
o.AuxCallback = cb
|
||||
}
|
||||
}
|
||||
|
||||
// Display prints the JSON messages from the given reader to the given stream.
|
||||
//
|
||||
// It wraps the [jsonmessage.DisplayJSONMessagesStream] function to make it
|
||||
// "context aware" and appropriately returns why the function was canceled.
|
||||
//
|
||||
// It returns an error if the context is canceled, but not if the input reader / stream is closed.
|
||||
func Display(ctx context.Context, in io.Reader, stream Stream, opts ...Options) error {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
reader := &ctxReader{err: make(chan error, 1), r: in}
|
||||
stopFunc := context.AfterFunc(ctx, func() { reader.err <- ctx.Err() })
|
||||
defer stopFunc()
|
||||
|
||||
o := options{}
|
||||
for _, opt := range opts {
|
||||
opt(&o)
|
||||
}
|
||||
|
||||
if err := jsonmessage.DisplayJSONMessagesStream(reader, stream, stream.FD(), stream.IsTerminal(), o.AuxCallback); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.Err()
|
||||
}
|
33
vendor/github.com/docker/cli/cli/registry/client/client.go
generated
vendored
33
vendor/github.com/docker/cli/cli/registry/client/client.go
generated
vendored
@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/distribution/reference"
|
||||
manifesttypes "github.com/docker/cli/cli/manifest/types"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/docker/distribution"
|
||||
distributionclient "github.com/docker/distribution/registry/client"
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
@ -38,12 +37,6 @@ func NewRegistryClient(resolver AuthConfigResolver, userAgent string, insecure b
|
||||
// AuthConfigResolver returns Auth Configuration for an index
|
||||
type AuthConfigResolver func(ctx context.Context, index *registrytypes.IndexInfo) registrytypes.AuthConfig
|
||||
|
||||
// PutManifestOptions is the data sent to push a manifest
|
||||
type PutManifestOptions struct {
|
||||
MediaType string
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
type client struct {
|
||||
authConfigResolver AuthConfigResolver
|
||||
insecureRegistry bool
|
||||
@ -61,13 +54,13 @@ func (err ErrBlobCreated) Error() string {
|
||||
err.From, err.Target)
|
||||
}
|
||||
|
||||
// ErrHTTPProto returned if attempting to use TLS with a non-TLS registry
|
||||
type ErrHTTPProto struct {
|
||||
OrigErr string
|
||||
// httpProtoError returned if attempting to use TLS with a non-TLS registry
|
||||
type httpProtoError struct {
|
||||
cause error
|
||||
}
|
||||
|
||||
func (err ErrHTTPProto) Error() string {
|
||||
return err.OrigErr
|
||||
func (e httpProtoError) Error() string {
|
||||
return e.cause.Error()
|
||||
}
|
||||
|
||||
var _ RegistryClient = &client{}
|
||||
@ -78,7 +71,7 @@ func (c *client) MountBlob(ctx context.Context, sourceRef reference.Canonical, t
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repoEndpoint.actions = trust.ActionsPushAndPull
|
||||
repoEndpoint.actions = []string{"pull", "push"}
|
||||
repo, err := c.getRepositoryForReference(ctx, targetRef, repoEndpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -104,7 +97,7 @@ func (c *client) PutManifest(ctx context.Context, ref reference.Named, manifest
|
||||
return "", err
|
||||
}
|
||||
|
||||
repoEndpoint.actions = trust.ActionsPushAndPull
|
||||
repoEndpoint.actions = []string{"pull", "push"}
|
||||
repo, err := c.getRepositoryForReference(ctx, ref, repoEndpoint)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -121,7 +114,10 @@ func (c *client) PutManifest(ctx context.Context, ref reference.Named, manifest
|
||||
}
|
||||
|
||||
dgst, err := manifestService.Put(ctx, manifest, opts...)
|
||||
return dgst, errors.Wrapf(err, "failed to put manifest %s", ref)
|
||||
if err != nil {
|
||||
return dgst, errors.Wrapf(err, "failed to put manifest %s", ref)
|
||||
}
|
||||
return dgst, nil
|
||||
}
|
||||
|
||||
func (c *client) getRepositoryForReference(ctx context.Context, ref reference.Named, repoEndpoint repositoryEndpoint) (distribution.Repository, error) {
|
||||
@ -135,7 +131,7 @@ func (c *client) getRepositoryForReference(ctx context.Context, ref reference.Na
|
||||
return nil, err
|
||||
}
|
||||
if !repoEndpoint.endpoint.TLSConfig.InsecureSkipVerify {
|
||||
return nil, ErrHTTPProto{OrigErr: err.Error()}
|
||||
return nil, httpProtoError{cause: err}
|
||||
}
|
||||
// --insecure was set; fall back to plain HTTP
|
||||
if url := repoEndpoint.endpoint.URL; url != nil && url.Scheme == "https" {
|
||||
@ -157,7 +153,10 @@ func (c *client) getHTTPTransportForRepoEndpoint(ctx context.Context, repoEndpoi
|
||||
c.userAgent,
|
||||
repoEndpoint.actions,
|
||||
)
|
||||
return httpTransport, errors.Wrap(err, "failed to configure transport")
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to configure transport")
|
||||
}
|
||||
return httpTransport, nil
|
||||
}
|
||||
|
||||
// GetManifest returns an ImageManifest for the reference
|
||||
|
19
vendor/github.com/docker/cli/cli/registry/client/endpoint.go
generated
vendored
19
vendor/github.com/docker/cli/cli/registry/client/endpoint.go
generated
vendored
@ -6,7 +6,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/cli/cli/trust"
|
||||
"github.com/docker/distribution/registry/client/auth"
|
||||
"github.com/docker/distribution/registry/client/transport"
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
@ -31,10 +30,7 @@ func (r repositoryEndpoint) BaseURL() string {
|
||||
}
|
||||
|
||||
func newDefaultRepositoryEndpoint(ref reference.Named, insecure bool) (repositoryEndpoint, error) {
|
||||
repoInfo, err := registry.ParseRepositoryInfo(ref)
|
||||
if err != nil {
|
||||
return repositoryEndpoint{}, err
|
||||
}
|
||||
repoInfo, _ := registry.ParseRepositoryInfo(ref)
|
||||
endpoint, err := getDefaultEndpointFromRepoInfo(repoInfo)
|
||||
if err != nil {
|
||||
return repositoryEndpoint{}, err
|
||||
@ -94,7 +90,7 @@ func getHTTPTransport(authConfig registrytypes.AuthConfig, endpoint registry.API
|
||||
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, passThruTokenHandler))
|
||||
} else {
|
||||
if len(actions) == 0 {
|
||||
actions = trust.ActionsPullOnly
|
||||
actions = []string{"pull"}
|
||||
}
|
||||
creds := registry.NewStaticCredentialStore(&authConfig)
|
||||
tokenHandler := auth.NewTokenHandler(authTransport, creds, repoName, actions...)
|
||||
@ -104,14 +100,11 @@ func getHTTPTransport(authConfig registrytypes.AuthConfig, endpoint registry.API
|
||||
return transport.NewTransport(base, modifiers...), nil
|
||||
}
|
||||
|
||||
// RepoNameForReference returns the repository name from a reference
|
||||
// RepoNameForReference returns the repository name from a reference.
|
||||
//
|
||||
// Deprecated: this function is no longer used and will be removed in the next release.
|
||||
func RepoNameForReference(ref reference.Named) (string, error) {
|
||||
// insecure is fine since this only returns the name
|
||||
repo, err := newDefaultRepositoryEndpoint(ref, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return repo.Name(), nil
|
||||
return reference.Path(reference.TrimNamed(ref)), nil
|
||||
}
|
||||
|
||||
type existingTokenHandler struct {
|
||||
|
14
vendor/github.com/docker/cli/cli/registry/client/fetcher.go
generated
vendored
14
vendor/github.com/docker/cli/cli/registry/client/fetcher.go
generated
vendored
@ -220,10 +220,7 @@ func (c *client) iterateEndpoints(ctx context.Context, namedRef reference.Named,
|
||||
return err
|
||||
}
|
||||
|
||||
repoInfo, err := registry.ParseRepositoryInfo(namedRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
repoInfo, _ := registry.ParseRepositoryInfo(namedRef)
|
||||
|
||||
confirmedTLSRegistries := make(map[string]bool)
|
||||
for _, endpoint := range endpoints {
|
||||
@ -241,7 +238,8 @@ func (c *client) iterateEndpoints(ctx context.Context, namedRef reference.Named,
|
||||
repo, err := c.getRepositoryForReference(ctx, namedRef, repoEndpoint)
|
||||
if err != nil {
|
||||
logrus.Debugf("error %s with repo endpoint %+v", err, repoEndpoint)
|
||||
if _, ok := err.(ErrHTTPProto); ok {
|
||||
var protoErr httpProtoError
|
||||
if errors.As(err, &protoErr) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
@ -272,11 +270,6 @@ func (c *client) iterateEndpoints(ctx context.Context, namedRef reference.Named,
|
||||
|
||||
// allEndpoints returns a list of endpoints ordered by priority (v2, http).
|
||||
func allEndpoints(namedRef reference.Named, insecure bool) ([]registry.APIEndpoint, error) {
|
||||
repoInfo, err := registry.ParseRepositoryInfo(namedRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var serviceOpts registry.ServiceOptions
|
||||
if insecure {
|
||||
logrus.Debugf("allowing insecure registry for: %s", reference.Domain(namedRef))
|
||||
@ -286,6 +279,7 @@ func allEndpoints(namedRef reference.Named, insecure bool) ([]registry.APIEndpoi
|
||||
if err != nil {
|
||||
return []registry.APIEndpoint{}, err
|
||||
}
|
||||
repoInfo, _ := registry.ParseRepositoryInfo(namedRef)
|
||||
endpoints, err := registryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
|
||||
logrus.Debugf("endpoints for %s: %v", namedRef, endpoints)
|
||||
return endpoints, err
|
||||
|
25
vendor/github.com/docker/cli/cli/trust/trust.go
generated
vendored
25
vendor/github.com/docker/cli/cli/trust/trust.go
generated
vendored
@ -40,10 +40,11 @@ var (
|
||||
ActionsPullOnly = []string{"pull"}
|
||||
// ActionsPushAndPull defines the actions for read-write interactions with a Notary Repository
|
||||
ActionsPushAndPull = []string{"pull", "push"}
|
||||
// NotaryServer is the endpoint serving the Notary trust server
|
||||
NotaryServer = "https://notary.docker.io"
|
||||
)
|
||||
|
||||
// NotaryServer is the endpoint serving the Notary trust server
|
||||
const NotaryServer = "https://notary.docker.io"
|
||||
|
||||
// GetTrustDirectory returns the base trust directory name
|
||||
func GetTrustDirectory() string {
|
||||
return filepath.Join(config.Dir(), "trust")
|
||||
@ -238,6 +239,20 @@ func NotaryError(repoName string, err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// AddToAllSignableRoles attempts to add the image target to all the top level
|
||||
// delegation roles we can (based on whether we have the signing key and whether
|
||||
// the role's path allows us to).
|
||||
//
|
||||
// If there are no delegation roles, we add to the targets role.
|
||||
func AddToAllSignableRoles(repo client.Repository, target *client.Target) error {
|
||||
signableRoles, err := GetSignableRoles(repo, target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return repo.AddTarget(target, signableRoles...)
|
||||
}
|
||||
|
||||
// GetSignableRoles returns a list of roles for which we have valid signing
|
||||
// keys, given a notary repository and a target
|
||||
func GetSignableRoles(repo client.Repository, target *client.Target) ([]data.RoleName, error) {
|
||||
@ -307,11 +322,7 @@ func GetImageReferencesAndAuth(ctx context.Context,
|
||||
}
|
||||
|
||||
// Resolve the Repository name from fqn to RepositoryInfo
|
||||
repoInfo, err := registry.ParseRepositoryInfo(ref)
|
||||
if err != nil {
|
||||
return ImageRefAndAuth{}, err
|
||||
}
|
||||
|
||||
repoInfo, _ := registry.ParseRepositoryInfo(ref)
|
||||
authConfig := authResolver(ctx, repoInfo.Index)
|
||||
return ImageRefAndAuth{
|
||||
original: imgName,
|
||||
|
143
vendor/github.com/docker/cli/cli/trust/trust_push.go
generated
vendored
Normal file
143
vendor/github.com/docker/cli/cli/trust/trust_push.go
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
package trust
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/cli/cli/internal/jsonstream"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/docker/api/types"
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/theupdateframework/notary/client"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
)
|
||||
|
||||
// Streams is an interface which exposes the standard input and output streams.
|
||||
//
|
||||
// Same interface as [github.com/docker/cli/cli/command.Streams] but defined here to prevent a circular import.
|
||||
type Streams interface {
|
||||
In() *streams.In
|
||||
Out() *streams.Out
|
||||
Err() *streams.Out
|
||||
}
|
||||
|
||||
// PushTrustedReference pushes a canonical reference to the trust server.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func PushTrustedReference(ctx context.Context, ioStreams Streams, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig registrytypes.AuthConfig, in io.Reader, userAgent string) error {
|
||||
// If it is a trusted push we would like to find the target entry which match the
|
||||
// tag provided in the function and then do an AddTarget later.
|
||||
notaryTarget := &client.Target{}
|
||||
// Count the times of calling for handleTarget,
|
||||
// if it is called more that once, that should be considered an error in a trusted push.
|
||||
cnt := 0
|
||||
handleTarget := func(msg jsonstream.JSONMessage) {
|
||||
cnt++
|
||||
if cnt > 1 {
|
||||
// handleTarget should only be called once. This will be treated as an error.
|
||||
return
|
||||
}
|
||||
|
||||
var pushResult types.PushResult
|
||||
err := json.Unmarshal(*msg.Aux, &pushResult)
|
||||
if err == nil && pushResult.Tag != "" {
|
||||
if dgst, err := digest.Parse(pushResult.Digest); err == nil {
|
||||
h, err := hex.DecodeString(dgst.Hex())
|
||||
if err != nil {
|
||||
notaryTarget = nil
|
||||
return
|
||||
}
|
||||
notaryTarget.Name = pushResult.Tag
|
||||
notaryTarget.Hashes = data.Hashes{string(dgst.Algorithm()): h}
|
||||
notaryTarget.Length = int64(pushResult.Size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tag string
|
||||
switch x := ref.(type) {
|
||||
case reference.Canonical:
|
||||
return errors.New("cannot push a digest reference")
|
||||
case reference.NamedTagged:
|
||||
tag = x.Tag()
|
||||
default:
|
||||
// We want trust signatures to always take an explicit tag,
|
||||
// otherwise it will act as an untrusted push.
|
||||
if err := jsonstream.Display(ctx, in, ioStreams.Out()); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = fmt.Fprintln(ioStreams.Err(), "No tag specified, skipping trust metadata push")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := jsonstream.Display(ctx, in, ioStreams.Out(), jsonstream.WithAuxCallback(handleTarget)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cnt > 1 {
|
||||
return errors.Errorf("internal error: only one call to handleTarget expected")
|
||||
}
|
||||
|
||||
if notaryTarget == nil {
|
||||
return errors.Errorf("no targets found, provide a specific tag in order to sign it")
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(ioStreams.Out(), "Signing and pushing trust metadata")
|
||||
|
||||
repo, err := GetNotaryRepository(ioStreams.In(), ioStreams.Out(), userAgent, repoInfo, &authConfig, "push", "pull")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error establishing connection to trust repository")
|
||||
}
|
||||
|
||||
// get the latest repository metadata so we can figure out which roles to sign
|
||||
_, err = repo.ListTargets()
|
||||
|
||||
switch err.(type) {
|
||||
case client.ErrRepoNotInitialized, client.ErrRepositoryNotExist:
|
||||
keys := repo.GetCryptoService().ListKeys(data.CanonicalRootRole)
|
||||
var rootKeyID string
|
||||
// always select the first root key
|
||||
if len(keys) > 0 {
|
||||
sort.Strings(keys)
|
||||
rootKeyID = keys[0]
|
||||
} else {
|
||||
rootPublicKey, err := repo.GetCryptoService().Create(data.CanonicalRootRole, "", data.ECDSAKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rootKeyID = rootPublicKey.ID()
|
||||
}
|
||||
|
||||
// Initialize the notary repository with a remotely managed snapshot key
|
||||
if err := repo.Initialize([]string{rootKeyID}, data.CanonicalSnapshotRole); err != nil {
|
||||
return NotaryError(repoInfo.Name.Name(), err)
|
||||
}
|
||||
_, _ = fmt.Fprintf(ioStreams.Out(), "Finished initializing %q\n", repoInfo.Name.Name())
|
||||
err = repo.AddTarget(notaryTarget, data.CanonicalTargetsRole)
|
||||
case nil:
|
||||
// already initialized and we have successfully downloaded the latest metadata
|
||||
err = AddToAllSignableRoles(repo, notaryTarget)
|
||||
default:
|
||||
return NotaryError(repoInfo.Name.Name(), err)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = repo.Publish()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "failed to sign %s:%s", repoInfo.Name.Name(), tag)
|
||||
return NotaryError(repoInfo.Name.Name(), err)
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(ioStreams.Out(), "Successfully signed %s:%s\n", repoInfo.Name.Name(), tag)
|
||||
return nil
|
||||
}
|
22
vendor/github.com/docker/cli/cli/trust/trust_tag.go
generated
vendored
Normal file
22
vendor/github.com/docker/cli/cli/trust/trust_tag.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
package trust
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/docker/client"
|
||||
)
|
||||
|
||||
// TagTrusted tags a trusted ref. It is a shallow wrapper around [client.Client.ImageTag]
|
||||
// that updates the given image references to their familiar format for tagging
|
||||
// and printing.
|
||||
func TagTrusted(ctx context.Context, apiClient client.ImageAPIClient, out io.Writer, trustedRef reference.Canonical, ref reference.NamedTagged) error {
|
||||
// Use familiar references when interacting with client and output
|
||||
familiarRef := reference.FamiliarString(ref)
|
||||
trustedFamiliarRef := reference.FamiliarString(trustedRef)
|
||||
|
||||
_, _ = fmt.Fprintf(out, "Tagging %s as %s\n", trustedFamiliarRef, familiarRef)
|
||||
return apiClient.ImageTag(ctx, trustedFamiliarRef, familiarRef)
|
||||
}
|
5
vendor/github.com/docker/cli/opts/duration.go
generated
vendored
5
vendor/github.com/docker/cli/opts/duration.go
generated
vendored
@ -1,9 +1,8 @@
|
||||
package opts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// PositiveDurationOpt is an option type for time.Duration that uses a pointer.
|
||||
@ -20,7 +19,7 @@ func (d *PositiveDurationOpt) Set(s string) error {
|
||||
return err
|
||||
}
|
||||
if *d.DurationOpt.value < 0 {
|
||||
return errors.Errorf("duration cannot be negative")
|
||||
return errors.New("duration cannot be negative")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
3
vendor/github.com/docker/cli/opts/env.go
generated
vendored
3
vendor/github.com/docker/cli/opts/env.go
generated
vendored
@ -1,10 +1,9 @@
|
||||
package opts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ValidateEnv validates an environment variable and returns it.
|
||||
|
13
vendor/github.com/docker/cli/opts/gpus.go
generated
vendored
13
vendor/github.com/docker/cli/opts/gpus.go
generated
vendored
@ -2,12 +2,12 @@ package opts
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// GpuOpts is a Value type for parsing mounts
|
||||
@ -20,7 +20,14 @@ func parseCount(s string) (int, error) {
|
||||
return -1, nil
|
||||
}
|
||||
i, err := strconv.Atoi(s)
|
||||
return i, errors.Wrap(err, "count must be an integer")
|
||||
if err != nil {
|
||||
var numErr *strconv.NumError
|
||||
if errors.As(err, &numErr) {
|
||||
err = numErr.Err
|
||||
}
|
||||
return 0, fmt.Errorf(`invalid count (%s): value must be either "all" or an integer: %w`, s, err)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// Set a new mount value
|
||||
@ -69,7 +76,7 @@ func (o *GpuOpts) Set(value string) error {
|
||||
r := csv.NewReader(strings.NewReader(val))
|
||||
optFields, err := r.Read()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to read gpu options")
|
||||
return fmt.Errorf("failed to read gpu options: %w", err)
|
||||
}
|
||||
req.Options = ConvertKVStringsToMap(optFields)
|
||||
default:
|
||||
|
3
vendor/github.com/docker/cli/opts/mount.go
generated
vendored
3
vendor/github.com/docker/cli/opts/mount.go
generated
vendored
@ -135,8 +135,7 @@ func (m *MountOpt) Set(value string) error {
|
||||
// TODO: implicitly set propagation and error if the user specifies a propagation in a future refactor/UX polish pass
|
||||
// https://github.com/docker/cli/pull/4316#discussion_r1341974730
|
||||
default:
|
||||
return fmt.Errorf("invalid value for %s: %s (must be \"enabled\", \"disabled\", \"writable\", or \"readonly\")",
|
||||
key, val)
|
||||
return fmt.Errorf(`invalid value for %s: %s (must be "enabled", "disabled", "writable", or "readonly")`, key, val)
|
||||
}
|
||||
case "volume-subpath":
|
||||
volumeOptions().Subpath = val
|
||||
|
6
vendor/github.com/docker/cli/opts/network.go
generated
vendored
6
vendor/github.com/docker/cli/opts/network.go
generated
vendored
@ -89,7 +89,11 @@ func (n *NetworkOpt) Set(value string) error { //nolint:gocyclo
|
||||
case gwPriorityOpt:
|
||||
netOpt.GwPriority, err = strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid gw-priority: %w", err)
|
||||
var numErr *strconv.NumError
|
||||
if errors.As(err, &numErr) {
|
||||
err = numErr.Err
|
||||
}
|
||||
return fmt.Errorf("invalid gw-priority (%s): %w", val, err)
|
||||
}
|
||||
default:
|
||||
return errors.New("invalid field key " + key)
|
||||
|
4
vendor/github.com/docker/cli/opts/opts.go
generated
vendored
4
vendor/github.com/docker/cli/opts/opts.go
generated
vendored
@ -1,6 +1,7 @@
|
||||
package opts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
@ -9,8 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/docker/go-units"
|
||||
)
|
||||
|
||||
var (
|
||||
|
18
vendor/github.com/docker/cli/opts/opts_deprecated.go
generated
vendored
Normal file
18
vendor/github.com/docker/cli/opts/opts_deprecated.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
package opts
|
||||
|
||||
import "github.com/docker/cli/opts/swarmopts"
|
||||
|
||||
// PortOpt represents a port config in swarm mode.
|
||||
//
|
||||
// Deprecated: use [swarmopts.PortOpt]
|
||||
type PortOpt = swarmopts.PortOpt
|
||||
|
||||
// ConfigOpt is a Value type for parsing configs.
|
||||
//
|
||||
// Deprecated: use [swarmopts.ConfigOpt]
|
||||
type ConfigOpt = swarmopts.ConfigOpt
|
||||
|
||||
// SecretOpt is a Value type for parsing secrets
|
||||
//
|
||||
// Deprecated: use [swarmopts.SecretOpt]
|
||||
type SecretOpt = swarmopts.SecretOpt
|
@ -1,4 +1,4 @@
|
||||
package opts
|
||||
package swarmopts
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
@ -8,12 +8,12 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
swarmtypes "github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
)
|
||||
|
||||
// ConfigOpt is a Value type for parsing configs
|
||||
type ConfigOpt struct {
|
||||
values []*swarmtypes.ConfigReference
|
||||
values []*swarm.ConfigReference
|
||||
}
|
||||
|
||||
// Set a new config value
|
||||
@ -24,8 +24,8 @@ func (o *ConfigOpt) Set(value string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
options := &swarmtypes.ConfigReference{
|
||||
File: &swarmtypes.ConfigReferenceFileTarget{
|
||||
options := &swarm.ConfigReference{
|
||||
File: &swarm.ConfigReferenceFileTarget{
|
||||
UID: "0",
|
||||
GID: "0",
|
||||
Mode: 0o444,
|
||||
@ -95,6 +95,6 @@ func (o *ConfigOpt) String() string {
|
||||
}
|
||||
|
||||
// Value returns the config requests
|
||||
func (o *ConfigOpt) Value() []*swarmtypes.ConfigReference {
|
||||
func (o *ConfigOpt) Value() []*swarm.ConfigReference {
|
||||
return o.values
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package opts
|
||||
package swarmopts
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
@ -46,42 +46,50 @@ func (p *PortOpt) Set(value string) error {
|
||||
// TODO(thaJeztah): these options should not be case-insensitive.
|
||||
key, val, ok := strings.Cut(strings.ToLower(field), "=")
|
||||
if !ok || key == "" {
|
||||
return fmt.Errorf("invalid field %s", field)
|
||||
return fmt.Errorf("invalid field: %s", field)
|
||||
}
|
||||
switch key {
|
||||
case portOptProtocol:
|
||||
if val != string(swarm.PortConfigProtocolTCP) && val != string(swarm.PortConfigProtocolUDP) && val != string(swarm.PortConfigProtocolSCTP) {
|
||||
return fmt.Errorf("invalid protocol value %s", val)
|
||||
return fmt.Errorf("invalid protocol value '%s'", val)
|
||||
}
|
||||
|
||||
pConfig.Protocol = swarm.PortConfigProtocol(val)
|
||||
case portOptMode:
|
||||
if val != string(swarm.PortConfigPublishModeIngress) && val != string(swarm.PortConfigPublishModeHost) {
|
||||
return fmt.Errorf("invalid publish mode value %s", val)
|
||||
return fmt.Errorf("invalid publish mode value (%s): must be either '%s' or '%s'", val, swarm.PortConfigPublishModeIngress, swarm.PortConfigPublishModeHost)
|
||||
}
|
||||
|
||||
pConfig.PublishMode = swarm.PortConfigPublishMode(val)
|
||||
case portOptTargetPort:
|
||||
tPort, err := strconv.ParseUint(val, 10, 16)
|
||||
if err != nil {
|
||||
return err
|
||||
var numErr *strconv.NumError
|
||||
if errors.As(err, &numErr) {
|
||||
err = numErr.Err
|
||||
}
|
||||
return fmt.Errorf("invalid target port (%s): value must be an integer: %w", val, err)
|
||||
}
|
||||
|
||||
pConfig.TargetPort = uint32(tPort)
|
||||
case portOptPublishedPort:
|
||||
pPort, err := strconv.ParseUint(val, 10, 16)
|
||||
if err != nil {
|
||||
return err
|
||||
var numErr *strconv.NumError
|
||||
if errors.As(err, &numErr) {
|
||||
err = numErr.Err
|
||||
}
|
||||
return fmt.Errorf("invalid published port (%s): value must be an integer: %w", val, err)
|
||||
}
|
||||
|
||||
pConfig.PublishedPort = uint32(pPort)
|
||||
default:
|
||||
return fmt.Errorf("invalid field key %s", key)
|
||||
return fmt.Errorf("invalid field key: %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
if pConfig.TargetPort == 0 {
|
||||
return fmt.Errorf("missing mandatory field %q", portOptTargetPort)
|
||||
return fmt.Errorf("missing mandatory field '%s'", portOptTargetPort)
|
||||
}
|
||||
|
||||
if pConfig.PublishMode == "" {
|
@ -1,4 +1,4 @@
|
||||
package opts
|
||||
package swarmopts
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
@ -8,12 +8,12 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
swarmtypes "github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
)
|
||||
|
||||
// SecretOpt is a Value type for parsing secrets
|
||||
type SecretOpt struct {
|
||||
values []*swarmtypes.SecretReference
|
||||
values []*swarm.SecretReference
|
||||
}
|
||||
|
||||
// Set a new secret value
|
||||
@ -24,8 +24,8 @@ func (o *SecretOpt) Set(value string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
options := &swarmtypes.SecretReference{
|
||||
File: &swarmtypes.SecretReferenceFileTarget{
|
||||
options := &swarm.SecretReference{
|
||||
File: &swarm.SecretReferenceFileTarget{
|
||||
UID: "0",
|
||||
GID: "0",
|
||||
Mode: 0o444,
|
||||
@ -94,6 +94,6 @@ func (o *SecretOpt) String() string {
|
||||
}
|
||||
|
||||
// Value returns the secret requests
|
||||
func (o *SecretOpt) Value() []*swarmtypes.SecretReference {
|
||||
func (o *SecretOpt) Value() []*swarm.SecretReference {
|
||||
return o.values
|
||||
}
|
12
vendor/github.com/docker/docker/api/swagger.yaml
generated
vendored
12
vendor/github.com/docker/docker/api/swagger.yaml
generated
vendored
@ -5508,8 +5508,11 @@ definitions:
|
||||
com.example.some-other-label: "some-other-value"
|
||||
Data:
|
||||
description: |
|
||||
Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5))
|
||||
data to store as secret.
|
||||
Data is the data to store as a secret, formatted as a Base64-url-safe-encoded
|
||||
([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) string.
|
||||
It must be empty if the Driver field is set, in which case the data is
|
||||
loaded from an external secret store. The maximum allowed size is 500KB,
|
||||
as defined in [MaxSecretSize](https://pkg.go.dev/github.com/moby/swarmkit/v2@v2.0.0-20250103191802-8c1959736554/api/validation#MaxSecretSize).
|
||||
|
||||
This field is only used to _create_ a secret, and is not returned by
|
||||
other endpoints.
|
||||
@ -5560,8 +5563,9 @@ definitions:
|
||||
type: "string"
|
||||
Data:
|
||||
description: |
|
||||
Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5))
|
||||
config data.
|
||||
Data is the data to store as a config, formatted as a Base64-url-safe-encoded
|
||||
([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) string.
|
||||
The maximum allowed size is 1000KB, as defined in [MaxConfigSize](https://pkg.go.dev/github.com/moby/swarmkit/v2@v2.0.0-20250103191802-8c1959736554/manager/controlapi#MaxConfigSize).
|
||||
type: "string"
|
||||
Templating:
|
||||
description: |
|
||||
|
16
vendor/github.com/docker/docker/api/types/registry/registry.go
generated
vendored
16
vendor/github.com/docker/docker/api/types/registry/registry.go
generated
vendored
@ -49,15 +49,17 @@ func (ipnet *NetIPNet) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets the IPNet from a byte array of JSON
|
||||
func (ipnet *NetIPNet) UnmarshalJSON(b []byte) (err error) {
|
||||
func (ipnet *NetIPNet) UnmarshalJSON(b []byte) error {
|
||||
var ipnetStr string
|
||||
if err = json.Unmarshal(b, &ipnetStr); err == nil {
|
||||
var cidr *net.IPNet
|
||||
if _, cidr, err = net.ParseCIDR(ipnetStr); err == nil {
|
||||
*ipnet = NetIPNet(*cidr)
|
||||
}
|
||||
if err := json.Unmarshal(b, &ipnetStr); err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
_, cidr, err := net.ParseCIDR(ipnetStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*ipnet = NetIPNet(*cidr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// IndexInfo contains information about a registry
|
||||
|
6
vendor/github.com/docker/docker/api/types/swarm/config.go
generated
vendored
6
vendor/github.com/docker/docker/api/types/swarm/config.go
generated
vendored
@ -12,6 +12,12 @@ type Config struct {
|
||||
// ConfigSpec represents a config specification from a config in swarm
|
||||
type ConfigSpec struct {
|
||||
Annotations
|
||||
|
||||
// Data is the data to store as a config.
|
||||
//
|
||||
// The maximum allowed size is 1000KB, as defined in [MaxConfigSize].
|
||||
//
|
||||
// [MaxConfigSize]: https://pkg.go.dev/github.com/moby/swarmkit/v2@v2.0.0-20250103191802-8c1959736554/manager/controlapi#MaxConfigSize
|
||||
Data []byte `json:",omitempty"`
|
||||
|
||||
// Templating controls whether and how to evaluate the config payload as
|
||||
|
18
vendor/github.com/docker/docker/api/types/swarm/secret.go
generated
vendored
18
vendor/github.com/docker/docker/api/types/swarm/secret.go
generated
vendored
@ -12,8 +12,22 @@ type Secret struct {
|
||||
// SecretSpec represents a secret specification from a secret in swarm
|
||||
type SecretSpec struct {
|
||||
Annotations
|
||||
Data []byte `json:",omitempty"`
|
||||
Driver *Driver `json:",omitempty"` // name of the secrets driver used to fetch the secret's value from an external secret store
|
||||
|
||||
// Data is the data to store as a secret. It must be empty if a
|
||||
// [Driver] is used, in which case the data is loaded from an external
|
||||
// secret store. The maximum allowed size is 500KB, as defined in
|
||||
// [MaxSecretSize].
|
||||
//
|
||||
// This field is only used to create the secret, and is not returned
|
||||
// by other endpoints.
|
||||
//
|
||||
// [MaxSecretSize]: https://pkg.go.dev/github.com/moby/swarmkit/v2@v2.0.0-20250103191802-8c1959736554/api/validation#MaxSecretSize
|
||||
Data []byte `json:",omitempty"`
|
||||
|
||||
// Driver is the name of the secrets driver used to fetch the secret's
|
||||
// value from an external secret store. If not set, the default built-in
|
||||
// store is used.
|
||||
Driver *Driver `json:",omitempty"`
|
||||
|
||||
// Templating controls whether and how to evaluate the secret payload as
|
||||
// a template. If it is not set, no templating is used.
|
||||
|
14
vendor/github.com/docker/docker/client/container_create.go
generated
vendored
14
vendor/github.com/docker/docker/client/container_create.go
generated
vendored
@ -3,6 +3,7 @@ package client // import "github.com/docker/docker/client"
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/url"
|
||||
"path"
|
||||
"sort"
|
||||
@ -54,6 +55,19 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config
|
||||
// When using API under 1.42, the Linux daemon doesn't respect the ConsoleSize
|
||||
hostConfig.ConsoleSize = [2]uint{0, 0}
|
||||
}
|
||||
if versions.LessThan(cli.ClientVersion(), "1.44") {
|
||||
for _, m := range hostConfig.Mounts {
|
||||
if m.BindOptions != nil {
|
||||
// ReadOnlyNonRecursive can be safely ignored when API < 1.44
|
||||
if m.BindOptions.ReadOnlyForceRecursive {
|
||||
return response, errors.New("bind-recursive=readonly requires API v1.44 or later")
|
||||
}
|
||||
if m.BindOptions.NonRecursive && versions.LessThan(cli.ClientVersion(), "1.40") {
|
||||
return response, errors.New("bind-recursive=disabled requires API v1.40 or later")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hostConfig.CapAdd = normalizeCapabilities(hostConfig.CapAdd)
|
||||
hostConfig.CapDrop = normalizeCapabilities(hostConfig.CapDrop)
|
||||
|
20
vendor/github.com/docker/docker/client/service_create.go
generated
vendored
20
vendor/github.com/docker/docker/client/service_create.go
generated
vendored
@ -37,6 +37,11 @@ func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec,
|
||||
if err := validateServiceSpec(service); err != nil {
|
||||
return response, err
|
||||
}
|
||||
if versions.LessThan(cli.version, "1.30") {
|
||||
if err := validateAPIVersion(service, cli.version); err != nil {
|
||||
return response, err
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that the image is tagged
|
||||
var resolveWarning string
|
||||
@ -191,3 +196,18 @@ func validateServiceSpec(s swarm.ServiceSpec) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAPIVersion(c swarm.ServiceSpec, apiVersion string) error {
|
||||
for _, m := range c.TaskTemplate.ContainerSpec.Mounts {
|
||||
if m.BindOptions != nil {
|
||||
if m.BindOptions.NonRecursive && versions.LessThan(apiVersion, "1.40") {
|
||||
return errors.Errorf("bind-recursive=disabled requires API v1.40 or later")
|
||||
}
|
||||
// ReadOnlyNonRecursive can be safely ignored when API < 1.44
|
||||
if m.BindOptions.ReadOnlyForceRecursive && versions.LessThan(apiVersion, "1.44") {
|
||||
return errors.Errorf("bind-recursive=readonly requires API v1.44 or later")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
26
vendor/github.com/docker/docker/pkg/archive/archive.go
generated
vendored
26
vendor/github.com/docker/docker/pkg/archive/archive.go
generated
vendored
@ -236,11 +236,9 @@ func (r *readCloserWrapper) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
bufioReader32KPool = &sync.Pool{
|
||||
New: func() interface{} { return bufio.NewReaderSize(nil, 32*1024) },
|
||||
}
|
||||
)
|
||||
var bufioReader32KPool = &sync.Pool{
|
||||
New: func() interface{} { return bufio.NewReaderSize(nil, 32*1024) },
|
||||
}
|
||||
|
||||
type bufferedReader struct {
|
||||
buf *bufio.Reader
|
||||
@ -252,17 +250,17 @@ func newBufferedReader(r io.Reader) *bufferedReader {
|
||||
return &bufferedReader{buf}
|
||||
}
|
||||
|
||||
func (r *bufferedReader) Read(p []byte) (n int, err error) {
|
||||
func (r *bufferedReader) Read(p []byte) (int, error) {
|
||||
if r.buf == nil {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n, err = r.buf.Read(p)
|
||||
n, err := r.buf.Read(p)
|
||||
if err == io.EOF {
|
||||
r.buf.Reset(nil)
|
||||
bufioReader32KPool.Put(r.buf)
|
||||
r.buf = nil
|
||||
}
|
||||
return
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *bufferedReader) Peek(n int) ([]byte, error) {
|
||||
@ -428,7 +426,7 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
|
||||
pipeWriter.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
if _, err := copyWithBuffer(tarWriter, tarReader); err != nil {
|
||||
if err := copyWithBuffer(tarWriter, tarReader); err != nil {
|
||||
pipeWriter.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
@ -731,7 +729,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = copyWithBuffer(ta.TarWriter, file)
|
||||
err = copyWithBuffer(ta.TarWriter, file)
|
||||
file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -778,11 +776,11 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := copyWithBuffer(file, reader); err != nil {
|
||||
file.Close()
|
||||
if err := copyWithBuffer(file, reader); err != nil {
|
||||
_ = file.Close()
|
||||
return err
|
||||
}
|
||||
file.Close()
|
||||
_ = file.Close()
|
||||
|
||||
case tar.TypeBlock, tar.TypeChar:
|
||||
if inUserns { // cannot create devices in a userns
|
||||
@ -1438,7 +1436,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := copyWithBuffer(tw, srcF); err != nil {
|
||||
if err := copyWithBuffer(tw, srcF); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
65
vendor/github.com/docker/docker/pkg/archive/archive_linux.go
generated
vendored
65
vendor/github.com/docker/docker/pkg/archive/archive_linux.go
generated
vendored
@ -20,7 +20,7 @@ func getWhiteoutConverter(format WhiteoutFormat) tarWhiteoutConverter {
|
||||
|
||||
type overlayWhiteoutConverter struct{}
|
||||
|
||||
func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, err error) {
|
||||
func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, _ error) {
|
||||
// convert whiteouts to AUFS format
|
||||
if fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0 {
|
||||
// we just rename the file and make it normal
|
||||
@ -31,38 +31,41 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os
|
||||
hdr.Size = 0
|
||||
}
|
||||
|
||||
if fi.Mode()&os.ModeDir != 0 {
|
||||
opaqueXattrName := "trusted.overlay.opaque"
|
||||
if userns.RunningInUserNS() {
|
||||
opaqueXattrName = "user.overlay.opaque"
|
||||
}
|
||||
|
||||
// convert opaque dirs to AUFS format by writing an empty file with the prefix
|
||||
opaque, err := lgetxattr(path, opaqueXattrName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(opaque) == 1 && opaque[0] == 'y' {
|
||||
delete(hdr.PAXRecords, paxSchilyXattr+opaqueXattrName)
|
||||
|
||||
// create a header for the whiteout file
|
||||
// it should inherit some properties from the parent, but be a regular file
|
||||
wo = &tar.Header{
|
||||
Typeflag: tar.TypeReg,
|
||||
Mode: hdr.Mode & int64(os.ModePerm),
|
||||
Name: filepath.Join(hdr.Name, WhiteoutOpaqueDir), // #nosec G305 -- An archive is being created, not extracted.
|
||||
Size: 0,
|
||||
Uid: hdr.Uid,
|
||||
Uname: hdr.Uname,
|
||||
Gid: hdr.Gid,
|
||||
Gname: hdr.Gname,
|
||||
AccessTime: hdr.AccessTime,
|
||||
ChangeTime: hdr.ChangeTime,
|
||||
}
|
||||
}
|
||||
if fi.Mode()&os.ModeDir == 0 {
|
||||
// FIXME(thaJeztah): return a sentinel error instead of nil, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return
|
||||
opaqueXattrName := "trusted.overlay.opaque"
|
||||
if userns.RunningInUserNS() {
|
||||
opaqueXattrName = "user.overlay.opaque"
|
||||
}
|
||||
|
||||
// convert opaque dirs to AUFS format by writing an empty file with the prefix
|
||||
opaque, err := lgetxattr(path, opaqueXattrName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(opaque) != 1 || opaque[0] != 'y' {
|
||||
// FIXME(thaJeztah): return a sentinel error instead of nil, nil
|
||||
return nil, nil
|
||||
}
|
||||
delete(hdr.PAXRecords, paxSchilyXattr+opaqueXattrName)
|
||||
|
||||
// create a header for the whiteout file
|
||||
// it should inherit some properties from the parent, but be a regular file
|
||||
return &tar.Header{
|
||||
Typeflag: tar.TypeReg,
|
||||
Mode: hdr.Mode & int64(os.ModePerm),
|
||||
Name: filepath.Join(hdr.Name, WhiteoutOpaqueDir), // #nosec G305 -- An archive is being created, not extracted.
|
||||
Size: 0,
|
||||
Uid: hdr.Uid,
|
||||
Uname: hdr.Uname,
|
||||
Gid: hdr.Gid,
|
||||
Gname: hdr.Gname,
|
||||
AccessTime: hdr.AccessTime,
|
||||
ChangeTime: hdr.ChangeTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, error) {
|
||||
|
11
vendor/github.com/docker/docker/pkg/archive/archive_unix.go
generated
vendored
11
vendor/github.com/docker/docker/pkg/archive/archive_unix.go
generated
vendored
@ -73,14 +73,13 @@ func statUnix(fi os.FileInfo, hdr *tar.Header) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getInodeFromStat(stat interface{}) (inode uint64, err error) {
|
||||
func getInodeFromStat(stat interface{}) (uint64, error) {
|
||||
s, ok := stat.(*syscall.Stat_t)
|
||||
|
||||
if ok {
|
||||
inode = s.Ino
|
||||
if !ok {
|
||||
// FIXME(thaJeztah): this should likely return an error; see https://github.com/moby/moby/pull/49493#discussion_r1979152897
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return
|
||||
return s.Ino, nil
|
||||
}
|
||||
|
||||
func getFileUIDGID(stat interface{}) (idtools.Identity, error) {
|
||||
|
4
vendor/github.com/docker/docker/pkg/archive/archive_windows.go
generated
vendored
4
vendor/github.com/docker/docker/pkg/archive/archive_windows.go
generated
vendored
@ -48,9 +48,9 @@ func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (
|
||||
return
|
||||
}
|
||||
|
||||
func getInodeFromStat(stat interface{}) (inode uint64, err error) {
|
||||
func getInodeFromStat(stat interface{}) (uint64, error) {
|
||||
// do nothing. no notion of Inode in stat on Windows
|
||||
return
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// handleTarTypeBlockCharFifo is an OS-specific helper function used by
|
||||
|
2
vendor/github.com/docker/docker/pkg/archive/changes.go
generated
vendored
2
vendor/github.com/docker/docker/pkg/archive/changes.go
generated
vendored
@ -83,7 +83,7 @@ func aufsMetadataSkip(path string) (skip bool, err error) {
|
||||
if err != nil {
|
||||
skip = true
|
||||
}
|
||||
return
|
||||
return skip, err
|
||||
}
|
||||
|
||||
func aufsDeletedFile(root, path string, fi os.FileInfo) (string, error) {
|
||||
|
22
vendor/github.com/docker/docker/pkg/archive/copy.go
generated
vendored
22
vendor/github.com/docker/docker/pkg/archive/copy.go
generated
vendored
@ -25,11 +25,11 @@ var copyPool = sync.Pool{
|
||||
New: func() interface{} { s := make([]byte, 32*1024); return &s },
|
||||
}
|
||||
|
||||
func copyWithBuffer(dst io.Writer, src io.Reader) (written int64, err error) {
|
||||
func copyWithBuffer(dst io.Writer, src io.Reader) error {
|
||||
buf := copyPool.Get().(*[]byte)
|
||||
written, err = io.CopyBuffer(dst, src, *buf)
|
||||
_, err := io.CopyBuffer(dst, src, *buf)
|
||||
copyPool.Put(buf)
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// PreserveTrailingDotOrSeparator returns the given cleaned path (after
|
||||
@ -105,13 +105,13 @@ func TarResource(sourceInfo CopyInfo) (content io.ReadCloser, err error) {
|
||||
|
||||
// TarResourceRebase is like TarResource but renames the first path element of
|
||||
// items in the resulting tar archive to match the given rebaseName if not "".
|
||||
func TarResourceRebase(sourcePath, rebaseName string) (content io.ReadCloser, err error) {
|
||||
func TarResourceRebase(sourcePath, rebaseName string) (content io.ReadCloser, _ error) {
|
||||
sourcePath = normalizePath(sourcePath)
|
||||
if _, err = os.Lstat(sourcePath); err != nil {
|
||||
if _, err := os.Lstat(sourcePath); err != nil {
|
||||
// Catches the case where the source does not exist or is not a
|
||||
// directory if asserted to be a directory, as this also causes an
|
||||
// error.
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Separate the source path between its directory and
|
||||
@ -442,11 +442,12 @@ func CopyTo(content io.Reader, srcInfo CopyInfo, dstPath string) error {
|
||||
// whether to follow symbol link or not, if followLink is true, resolvedPath will return
|
||||
// link target of any symbol link file, else it will only resolve symlink of directory
|
||||
// but return symbol link file itself without resolving.
|
||||
func ResolveHostSourcePath(path string, followLink bool) (resolvedPath, rebaseName string, err error) {
|
||||
func ResolveHostSourcePath(path string, followLink bool) (resolvedPath, rebaseName string, _ error) {
|
||||
if followLink {
|
||||
var err error
|
||||
resolvedPath, err = filepath.EvalSymlinks(path)
|
||||
if err != nil {
|
||||
return
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
resolvedPath, rebaseName = GetRebaseName(path, resolvedPath)
|
||||
@ -454,10 +455,9 @@ func ResolveHostSourcePath(path string, followLink bool) (resolvedPath, rebaseNa
|
||||
dirPath, basePath := filepath.Split(path)
|
||||
|
||||
// if not follow symbol link, then resolve symbol link of parent dir
|
||||
var resolvedDirPath string
|
||||
resolvedDirPath, err = filepath.EvalSymlinks(dirPath)
|
||||
resolvedDirPath, err := filepath.EvalSymlinks(dirPath)
|
||||
if err != nil {
|
||||
return
|
||||
return "", "", err
|
||||
}
|
||||
// resolvedDirPath will have been cleaned (no trailing path separators) so
|
||||
// we can manually join it with the base path element.
|
||||
|
9
vendor/github.com/docker/docker/pkg/archive/time_nonwindows.go
generated
vendored
9
vendor/github.com/docker/docker/pkg/archive/time_nonwindows.go
generated
vendored
@ -17,12 +17,13 @@ func chtimes(name string, atime time.Time, mtime time.Time) error {
|
||||
return os.Chtimes(name, atime, mtime)
|
||||
}
|
||||
|
||||
func timeToTimespec(time time.Time) (ts unix.Timespec) {
|
||||
func timeToTimespec(time time.Time) unix.Timespec {
|
||||
if time.IsZero() {
|
||||
// Return UTIME_OMIT special value
|
||||
ts.Sec = 0
|
||||
ts.Nsec = (1 << 30) - 2
|
||||
return
|
||||
return unix.Timespec{
|
||||
Sec: 0,
|
||||
Nsec: (1 << 30) - 2,
|
||||
}
|
||||
}
|
||||
return unix.NsecToTimespec(time.UnixNano())
|
||||
}
|
||||
|
6
vendor/github.com/docker/docker/pkg/archive/wrap.go
generated
vendored
6
vendor/github.com/docker/docker/pkg/archive/wrap.go
generated
vendored
@ -45,8 +45,8 @@ func Generate(input ...string) (io.Reader, error) {
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func parseStringPairs(input ...string) (output [][2]string) {
|
||||
output = make([][2]string, 0, len(input)/2+1)
|
||||
func parseStringPairs(input ...string) [][2]string {
|
||||
output := make([][2]string, 0, len(input)/2+1)
|
||||
for i := 0; i < len(input); i += 2 {
|
||||
var pair [2]string
|
||||
pair[0] = input[i]
|
||||
@ -55,5 +55,5 @@ func parseStringPairs(input ...string) (output [][2]string) {
|
||||
}
|
||||
output = append(output, pair)
|
||||
}
|
||||
return
|
||||
return output
|
||||
}
|
||||
|
4
vendor/github.com/docker/docker/pkg/atomicwriter/atomicwriter.go
generated
vendored
4
vendor/github.com/docker/docker/pkg/atomicwriter/atomicwriter.go
generated
vendored
@ -11,12 +11,12 @@ import (
|
||||
// destination path. Writing and closing concurrently is not allowed.
|
||||
// NOTE: umask is not considered for the file's permissions.
|
||||
func New(filename string, perm os.FileMode) (io.WriteCloser, error) {
|
||||
f, err := os.CreateTemp(filepath.Dir(filename), ".tmp-"+filepath.Base(filename))
|
||||
abspath, err := filepath.Abs(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
abspath, err := filepath.Abs(filename)
|
||||
f, err := os.CreateTemp(filepath.Dir(abspath), ".tmp-"+filepath.Base(filename))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
8
vendor/github.com/docker/docker/pkg/stdcopy/stdcopy.go
generated
vendored
8
vendor/github.com/docker/docker/pkg/stdcopy/stdcopy.go
generated
vendored
@ -43,9 +43,9 @@ type stdWriter struct {
|
||||
// It inserts the prefix header before the buffer,
|
||||
// so stdcopy.StdCopy knows where to multiplex the output.
|
||||
// It makes stdWriter to implement io.Writer.
|
||||
func (w *stdWriter) Write(p []byte) (n int, err error) {
|
||||
func (w *stdWriter) Write(p []byte) (int, error) {
|
||||
if w == nil || w.Writer == nil {
|
||||
return 0, errors.New("Writer not instantiated")
|
||||
return 0, errors.New("writer not instantiated")
|
||||
}
|
||||
if p == nil {
|
||||
return 0, nil
|
||||
@ -57,7 +57,7 @@ func (w *stdWriter) Write(p []byte) (n int, err error) {
|
||||
buf.Write(header[:])
|
||||
buf.Write(p)
|
||||
|
||||
n, err = w.Writer.Write(buf.Bytes())
|
||||
n, err := w.Writer.Write(buf.Bytes())
|
||||
n -= stdWriterPrefixLen
|
||||
if n < 0 {
|
||||
n = 0
|
||||
@ -65,7 +65,7 @@ func (w *stdWriter) Write(p []byte) (n int, err error) {
|
||||
|
||||
buf.Reset()
|
||||
bufPool.Put(buf)
|
||||
return
|
||||
return n, err
|
||||
}
|
||||
|
||||
// NewStdWriter instantiates a new Writer.
|
||||
|
16
vendor/github.com/docker/docker/registry/auth.go
generated
vendored
16
vendor/github.com/docker/docker/registry/auth.go
generated
vendored
@ -66,13 +66,13 @@ func (scs staticCredentialStore) SetRefreshToken(*url.URL, string, string) {
|
||||
// loginV2 tries to login to the v2 registry server. The given registry
|
||||
// endpoint will be pinged to get authorization challenges. These challenges
|
||||
// will be used to authenticate against the registry to validate credentials.
|
||||
func loginV2(authConfig *registry.AuthConfig, endpoint APIEndpoint, userAgent string) (status string, token string, _ error) {
|
||||
func loginV2(authConfig *registry.AuthConfig, endpoint APIEndpoint, userAgent string) (token string, _ error) {
|
||||
endpointStr := strings.TrimRight(endpoint.URL.String(), "/") + "/v2/"
|
||||
log.G(context.TODO()).Debugf("attempting v2 login to registry endpoint %s", endpointStr)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, endpointStr, nil)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
|
||||
var (
|
||||
@ -84,22 +84,22 @@ func loginV2(authConfig *registry.AuthConfig, endpoint APIEndpoint, userAgent st
|
||||
|
||||
loginClient, err := v2AuthHTTPClient(endpoint.URL, authTrans, modifiers, creds, nil)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
|
||||
resp, err := loginClient.Do(req)
|
||||
if err != nil {
|
||||
err = translateV2AuthError(err)
|
||||
return "", "", err
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
return "Login Succeeded", credentialAuthConfig.IdentityToken, nil
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
// TODO(dmcgowan): Attempt to further interpret result, status code and error code string
|
||||
return "", errors.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode))
|
||||
}
|
||||
|
||||
// TODO(dmcgowan): Attempt to further interpret result, status code and error code string
|
||||
return "", "", errors.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode))
|
||||
return credentialAuthConfig.IdentityToken, nil
|
||||
}
|
||||
|
||||
func v2AuthHTTPClient(endpoint *url.URL, authTransport http.RoundTripper, modifiers []transport.RequestModifier, creds auth.CredentialStore, scopes []auth.Scope) (*http.Client, error) {
|
||||
|
168
vendor/github.com/docker/docker/registry/config.go
generated
vendored
168
vendor/github.com/docker/docker/registry/config.go
generated
vendored
@ -4,13 +4,17 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/internal/lazyregexp"
|
||||
"github.com/docker/docker/pkg/homedir"
|
||||
)
|
||||
|
||||
// ServiceOptions holds command line options.
|
||||
@ -56,26 +60,52 @@ var (
|
||||
Host: DefaultRegistryHost,
|
||||
}
|
||||
|
||||
emptyServiceConfig, _ = newServiceConfig(ServiceOptions{})
|
||||
validHostPortRegex = lazyregexp.New(`^` + reference.DomainRegexp.String() + `$`)
|
||||
validHostPortRegex = lazyregexp.New(`^` + reference.DomainRegexp.String() + `$`)
|
||||
|
||||
// certsDir is used to override defaultCertsDir.
|
||||
certsDir string
|
||||
// certsDir is used to override defaultCertsDir when running with rootlessKit.
|
||||
//
|
||||
// TODO(thaJeztah): change to a sync.OnceValue once we remove [SetCertsDir]
|
||||
// TODO(thaJeztah): certsDir should not be a package variable, but stored in our config, and passed when needed.
|
||||
setCertsDirOnce sync.Once
|
||||
certsDir string
|
||||
)
|
||||
|
||||
func setCertsDir(dir string) string {
|
||||
setCertsDirOnce.Do(func() {
|
||||
if dir != "" {
|
||||
certsDir = dir
|
||||
return
|
||||
}
|
||||
if os.Getenv("ROOTLESSKIT_STATE_DIR") != "" {
|
||||
// Configure registry.CertsDir() when running in rootless-mode
|
||||
// This is the equivalent of [rootless.RunningWithRootlessKit],
|
||||
// but inlining it to prevent adding that as a dependency
|
||||
// for docker/cli.
|
||||
//
|
||||
// [rootless.RunningWithRootlessKit]: https://github.com/moby/moby/blob/b4bdf12daec84caaf809a639f923f7370d4926ad/pkg/rootless/rootless.go#L5-L8
|
||||
if configHome, _ := homedir.GetConfigHome(); configHome != "" {
|
||||
certsDir = filepath.Join(configHome, "docker/certs.d")
|
||||
return
|
||||
}
|
||||
}
|
||||
certsDir = defaultCertsDir
|
||||
})
|
||||
return certsDir
|
||||
}
|
||||
|
||||
// SetCertsDir allows the default certs directory to be changed. This function
|
||||
// is used at daemon startup to set the correct location when running in
|
||||
// rootless mode.
|
||||
//
|
||||
// Deprecated: the cert-directory is now automatically selected when running with rootlessKit, and should no longer be set manually.
|
||||
func SetCertsDir(path string) {
|
||||
certsDir = path
|
||||
setCertsDir(path)
|
||||
}
|
||||
|
||||
// CertsDir is the directory where certificates are stored.
|
||||
func CertsDir() string {
|
||||
if certsDir != "" {
|
||||
return certsDir
|
||||
}
|
||||
return defaultCertsDir
|
||||
// call setCertsDir with an empty path to synchronise with [SetCertsDir]
|
||||
return setCertsDir("")
|
||||
}
|
||||
|
||||
// newServiceConfig returns a new instance of ServiceConfig
|
||||
@ -181,7 +211,7 @@ skip:
|
||||
// Assume `host:port` if not CIDR.
|
||||
indexConfigs[r] = ®istry.IndexInfo{
|
||||
Name: r,
|
||||
Mirrors: make([]string, 0),
|
||||
Mirrors: []string{},
|
||||
Secure: false,
|
||||
Official: false,
|
||||
}
|
||||
@ -288,16 +318,22 @@ func ValidateMirror(val string) (string, error) {
|
||||
// ValidateIndexName validates an index name. It is used by the daemon to
|
||||
// validate the daemon configuration.
|
||||
func ValidateIndexName(val string) (string, error) {
|
||||
// TODO: upstream this to check to reference package
|
||||
if val == "index.docker.io" {
|
||||
val = "docker.io"
|
||||
}
|
||||
val = normalizeIndexName(val)
|
||||
if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
|
||||
return "", invalidParamf("invalid index name (%s). Cannot begin or end with a hyphen", val)
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func normalizeIndexName(val string) string {
|
||||
// TODO(thaJeztah): consider normalizing other known options, such as "(https://)registry-1.docker.io", "https://index.docker.io/v1/".
|
||||
// TODO: upstream this to check to reference package
|
||||
if val == "index.docker.io" {
|
||||
return "docker.io"
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func hasScheme(reposName string) bool {
|
||||
return strings.Contains(reposName, "://")
|
||||
}
|
||||
@ -327,25 +363,20 @@ func validateHostPort(s string) error {
|
||||
}
|
||||
|
||||
// newIndexInfo returns IndexInfo configuration from indexName
|
||||
func newIndexInfo(config *serviceConfig, indexName string) (*registry.IndexInfo, error) {
|
||||
var err error
|
||||
indexName, err = ValidateIndexName(indexName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func newIndexInfo(config *serviceConfig, indexName string) *registry.IndexInfo {
|
||||
indexName = normalizeIndexName(indexName)
|
||||
|
||||
// Return any configured index info, first.
|
||||
if index, ok := config.IndexConfigs[indexName]; ok {
|
||||
return index, nil
|
||||
return index
|
||||
}
|
||||
|
||||
// Construct a non-configured index info.
|
||||
return ®istry.IndexInfo{
|
||||
Name: indexName,
|
||||
Mirrors: make([]string, 0),
|
||||
Secure: config.isSecureIndex(indexName),
|
||||
Official: false,
|
||||
}, nil
|
||||
Name: indexName,
|
||||
Mirrors: []string{},
|
||||
Secure: config.isSecureIndex(indexName),
|
||||
}
|
||||
}
|
||||
|
||||
// GetAuthConfigKey special-cases using the full index address of the official
|
||||
@ -358,18 +389,22 @@ func GetAuthConfigKey(index *registry.IndexInfo) string {
|
||||
}
|
||||
|
||||
// newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
|
||||
func newRepositoryInfo(config *serviceConfig, name reference.Named) (*RepositoryInfo, error) {
|
||||
index, err := newIndexInfo(config, reference.Domain(name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func newRepositoryInfo(config *serviceConfig, name reference.Named) *RepositoryInfo {
|
||||
index := newIndexInfo(config, reference.Domain(name))
|
||||
var officialRepo bool
|
||||
if index.Official {
|
||||
// RepositoryInfo.Official indicates whether the image repository
|
||||
// is an official (docker library official images) repository.
|
||||
//
|
||||
// We only need to check this if the image-repository is on Docker Hub.
|
||||
officialRepo = !strings.ContainsRune(reference.FamiliarName(name), '/')
|
||||
}
|
||||
official := !strings.ContainsRune(reference.FamiliarName(name), '/')
|
||||
|
||||
return &RepositoryInfo{
|
||||
Name: reference.TrimNamed(name),
|
||||
Index: index,
|
||||
Official: official,
|
||||
}, nil
|
||||
Official: officialRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// ParseRepositoryInfo performs the breakdown of a repository name into a
|
||||
@ -377,5 +412,70 @@ func newRepositoryInfo(config *serviceConfig, name reference.Named) (*Repository
|
||||
//
|
||||
// It is used by the Docker cli to interact with registry-related endpoints.
|
||||
func ParseRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
|
||||
return newRepositoryInfo(emptyServiceConfig, reposName)
|
||||
indexName := normalizeIndexName(reference.Domain(reposName))
|
||||
if indexName == IndexName {
|
||||
officialRepo := !strings.ContainsRune(reference.FamiliarName(reposName), '/')
|
||||
return &RepositoryInfo{
|
||||
Name: reference.TrimNamed(reposName),
|
||||
Index: ®istry.IndexInfo{
|
||||
Name: IndexName,
|
||||
Mirrors: []string{},
|
||||
Secure: true,
|
||||
Official: true,
|
||||
},
|
||||
Official: officialRepo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
insecure := false
|
||||
if isInsecure(indexName) {
|
||||
insecure = true
|
||||
}
|
||||
|
||||
return &RepositoryInfo{
|
||||
Name: reference.TrimNamed(reposName),
|
||||
Index: ®istry.IndexInfo{
|
||||
Name: indexName,
|
||||
Mirrors: []string{},
|
||||
Secure: !insecure,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// isInsecure is used to detect whether a registry domain or IP-address is allowed
|
||||
// to use an insecure (non-TLS, or self-signed cert) connection according to the
|
||||
// defaults, which allows for insecure connections with registries running on a
|
||||
// loopback address ("localhost", "::1/128", "127.0.0.0/8").
|
||||
//
|
||||
// It is used in situations where we don't have access to the daemon's configuration,
|
||||
// for example, when used from the client / CLI.
|
||||
func isInsecure(hostNameOrIP string) bool {
|
||||
// Attempt to strip port if present; this also strips brackets for
|
||||
// IPv6 addresses with a port (e.g. "[::1]:5000").
|
||||
//
|
||||
// This is best-effort; we'll continue using the address as-is if it fails.
|
||||
if host, _, err := net.SplitHostPort(hostNameOrIP); err == nil {
|
||||
hostNameOrIP = host
|
||||
}
|
||||
if hostNameOrIP == "127.0.0.1" || hostNameOrIP == "::1" || strings.EqualFold(hostNameOrIP, "localhost") {
|
||||
// Fast path; no need to resolve these, assuming nobody overrides
|
||||
// "localhost" for anything else than a loopback address (sorry, not sorry).
|
||||
return true
|
||||
}
|
||||
|
||||
var addresses []net.IP
|
||||
if ip := net.ParseIP(hostNameOrIP); ip != nil {
|
||||
addresses = append(addresses, ip)
|
||||
} else {
|
||||
// Try to resolve the host's IP-addresses.
|
||||
addrs, _ := lookupIP(hostNameOrIP)
|
||||
addresses = append(addresses, addrs...)
|
||||
}
|
||||
|
||||
for _, addr := range addresses {
|
||||
if addr.IsLoopback() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
31
vendor/github.com/docker/docker/registry/registry.go
generated
vendored
31
vendor/github.com/docker/docker/registry/registry.go
generated
vendored
@ -8,7 +8,6 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/log"
|
||||
@ -18,7 +17,14 @@ import (
|
||||
)
|
||||
|
||||
// HostCertsDir returns the config directory for a specific host.
|
||||
//
|
||||
// Deprecated: this function was only used internally, and will be removed in a future release.
|
||||
func HostCertsDir(hostname string) string {
|
||||
return hostCertsDir(hostname)
|
||||
}
|
||||
|
||||
// hostCertsDir returns the config directory for a specific host.
|
||||
func hostCertsDir(hostname string) string {
|
||||
return filepath.Join(CertsDir(), cleanPath(hostname))
|
||||
}
|
||||
|
||||
@ -26,11 +32,10 @@ func HostCertsDir(hostname string) string {
|
||||
func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) {
|
||||
// PreferredServerCipherSuites should have no effect
|
||||
tlsConfig := tlsconfig.ServerDefault()
|
||||
|
||||
tlsConfig.InsecureSkipVerify = !isSecure
|
||||
|
||||
if isSecure && CertsDir() != "" {
|
||||
hostDir := HostCertsDir(hostname)
|
||||
if isSecure {
|
||||
hostDir := hostCertsDir(hostname)
|
||||
log.G(context.TODO()).Debugf("hostDir: %s", hostDir)
|
||||
if err := ReadCertsDirectory(tlsConfig, hostDir); err != nil {
|
||||
return nil, err
|
||||
@ -59,7 +64,8 @@ func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
|
||||
}
|
||||
|
||||
for _, f := range fs {
|
||||
if strings.HasSuffix(f.Name(), ".crt") {
|
||||
switch filepath.Ext(f.Name()) {
|
||||
case ".crt":
|
||||
if tlsConfig.RootCAs == nil {
|
||||
systemPool, err := tlsconfig.SystemCertPool()
|
||||
if err != nil {
|
||||
@ -67,17 +73,17 @@ func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
|
||||
}
|
||||
tlsConfig.RootCAs = systemPool
|
||||
}
|
||||
log.G(context.TODO()).Debugf("crt: %s", filepath.Join(directory, f.Name()))
|
||||
data, err := os.ReadFile(filepath.Join(directory, f.Name()))
|
||||
fileName := filepath.Join(directory, f.Name())
|
||||
log.G(context.TODO()).Debugf("crt: %s", fileName)
|
||||
data, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tlsConfig.RootCAs.AppendCertsFromPEM(data)
|
||||
}
|
||||
if strings.HasSuffix(f.Name(), ".cert") {
|
||||
case ".cert":
|
||||
certName := f.Name()
|
||||
keyName := certName[:len(certName)-5] + ".key"
|
||||
log.G(context.TODO()).Debugf("cert: %s", filepath.Join(directory, f.Name()))
|
||||
log.G(context.TODO()).Debugf("cert: %s", filepath.Join(directory, certName))
|
||||
if !hasFile(fs, keyName) {
|
||||
return invalidParamf("missing key %s for client certificate %s. CA certificates must use the extension .crt", keyName, certName)
|
||||
}
|
||||
@ -86,11 +92,10 @@ func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
|
||||
return err
|
||||
}
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
|
||||
}
|
||||
if strings.HasSuffix(f.Name(), ".key") {
|
||||
case ".key":
|
||||
keyName := f.Name()
|
||||
certName := keyName[:len(keyName)-4] + ".cert"
|
||||
log.G(context.TODO()).Debugf("key: %s", filepath.Join(directory, f.Name()))
|
||||
log.G(context.TODO()).Debugf("key: %s", filepath.Join(directory, keyName))
|
||||
if !hasFile(fs, certName) {
|
||||
return invalidParamf("missing client certificate %s for key %s", certName, keyName)
|
||||
}
|
||||
|
27
vendor/github.com/docker/docker/registry/search.go
generated
vendored
27
vendor/github.com/docker/docker/registry/search.go
generated
vendored
@ -93,12 +93,8 @@ func (s *Service) searchUnfiltered(ctx context.Context, term string, limit int,
|
||||
|
||||
// Search is a long-running operation, just lock s.config to avoid block others.
|
||||
s.mu.RLock()
|
||||
index, err := newIndexInfo(s.config, indexName)
|
||||
index := newIndexInfo(s.config, indexName)
|
||||
s.mu.RUnlock()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if index.Official {
|
||||
// If pull "library/foo", it's stored locally under "foo"
|
||||
remoteName = strings.TrimPrefix(remoteName, "library/")
|
||||
@ -158,5 +154,24 @@ func splitReposSearchTerm(reposName string) (string, string) {
|
||||
// for that.
|
||||
func ParseSearchIndexInfo(reposName string) (*registry.IndexInfo, error) {
|
||||
indexName, _ := splitReposSearchTerm(reposName)
|
||||
return newIndexInfo(emptyServiceConfig, indexName)
|
||||
indexName = normalizeIndexName(indexName)
|
||||
if indexName == IndexName {
|
||||
return ®istry.IndexInfo{
|
||||
Name: IndexName,
|
||||
Mirrors: []string{},
|
||||
Secure: true,
|
||||
Official: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
insecure := false
|
||||
if isInsecure(indexName) {
|
||||
insecure = true
|
||||
}
|
||||
|
||||
return ®istry.IndexInfo{
|
||||
Name: indexName,
|
||||
Mirrors: []string{},
|
||||
Secure: !insecure,
|
||||
}, nil
|
||||
}
|
||||
|
6
vendor/github.com/docker/docker/registry/search_session.go
generated
vendored
6
vendor/github.com/docker/docker/registry/search_session.go
generated
vendored
@ -83,12 +83,12 @@ type onEOFReader struct {
|
||||
Fn func()
|
||||
}
|
||||
|
||||
func (r *onEOFReader) Read(p []byte) (n int, err error) {
|
||||
n, err = r.Rc.Read(p)
|
||||
func (r *onEOFReader) Read(p []byte) (int, error) {
|
||||
n, err := r.Rc.Read(p)
|
||||
if err == io.EOF {
|
||||
r.runFunc()
|
||||
}
|
||||
return
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Close closes the file and run the function.
|
||||
|
32
vendor/github.com/docker/docker/registry/service.go
generated
vendored
32
vendor/github.com/docker/docker/registry/service.go
generated
vendored
@ -52,7 +52,7 @@ func (s *Service) ReplaceConfig(options ServiceOptions) (commit func(), err erro
|
||||
// Auth contacts the public registry with the provided credentials,
|
||||
// and returns OK if authentication was successful.
|
||||
// It can be used to verify the validity of a client's credentials.
|
||||
func (s *Service) Auth(ctx context.Context, authConfig *registry.AuthConfig, userAgent string) (status, token string, err error) {
|
||||
func (s *Service) Auth(ctx context.Context, authConfig *registry.AuthConfig, userAgent string) (statusMessage, token string, _ error) {
|
||||
// TODO Use ctx when searching for repositories
|
||||
registryHostName := IndexHostname
|
||||
|
||||
@ -77,19 +77,28 @@ func (s *Service) Auth(ctx context.Context, authConfig *registry.AuthConfig, use
|
||||
return "", "", invalidParam(err)
|
||||
}
|
||||
|
||||
var lastErr error
|
||||
for _, endpoint := range endpoints {
|
||||
status, token, err = loginV2(authConfig, endpoint, userAgent)
|
||||
if err == nil {
|
||||
return
|
||||
authToken, err := loginV2(authConfig, endpoint, userAgent)
|
||||
if err != nil {
|
||||
if errdefs.IsUnauthorized(err) {
|
||||
// Failed to authenticate; don't continue with (non-TLS) endpoints.
|
||||
return "", "", err
|
||||
}
|
||||
// Try next endpoint
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
"error": err,
|
||||
"endpoint": endpoint,
|
||||
}).Infof("Error logging in to endpoint, trying next endpoint")
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
if errdefs.IsUnauthorized(err) {
|
||||
// Failed to authenticate; don't continue with (non-TLS) endpoints.
|
||||
return status, token, err
|
||||
}
|
||||
log.G(ctx).WithError(err).Infof("Error logging in to endpoint, trying next endpoint")
|
||||
|
||||
// TODO(thaJeztah): move the statusMessage to the API endpoint; we don't need to produce that here?
|
||||
return "Login Succeeded", authToken, nil
|
||||
}
|
||||
|
||||
return "", "", err
|
||||
return "", "", lastErr
|
||||
}
|
||||
|
||||
// ResolveRepository splits a repository name into its components
|
||||
@ -97,7 +106,8 @@ func (s *Service) Auth(ctx context.Context, authConfig *registry.AuthConfig, use
|
||||
func (s *Service) ResolveRepository(name reference.Named) (*RepositoryInfo, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return newRepositoryInfo(s.config, name)
|
||||
// TODO(thaJeztah): remove error return as it's no longer used.
|
||||
return newRepositoryInfo(s.config, name), nil
|
||||
}
|
||||
|
||||
// APIEndpoint represents a remote API endpoint
|
||||
|
2
vendor/github.com/docker/docker/registry/types.go
generated
vendored
2
vendor/github.com/docker/docker/registry/types.go
generated
vendored
@ -13,6 +13,8 @@ type RepositoryInfo struct {
|
||||
// Official indicates whether the repository is considered official.
|
||||
// If the registry is official, and the normalized name does not
|
||||
// contain a '/' (e.g. "foo"), then it is considered an official repo.
|
||||
//
|
||||
// Deprecated: this field is no longer used and will be removed in the next release. The information captured in this field can be obtained from the [Name] field instead.
|
||||
Official bool
|
||||
// Class represents the class of the repository, such as "plugin"
|
||||
// or "image".
|
||||
|
11
vendor/modules.txt
vendored
11
vendor/modules.txt
vendored
@ -155,7 +155,7 @@ github.com/containerd/console
|
||||
# github.com/containerd/containerd/api v1.8.0
|
||||
## explicit; go 1.21
|
||||
github.com/containerd/containerd/api/services/content/v1
|
||||
# github.com/containerd/containerd/v2 v2.0.3
|
||||
# github.com/containerd/containerd/v2 v2.0.4
|
||||
## explicit; go 1.22.0
|
||||
github.com/containerd/containerd/v2/core/content
|
||||
github.com/containerd/containerd/v2/core/content/proxy
|
||||
@ -229,11 +229,10 @@ 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.0.1+incompatible
|
||||
# github.com/docker/cli v28.0.2+incompatible
|
||||
## explicit
|
||||
github.com/docker/cli/cli
|
||||
github.com/docker/cli/cli-plugins/hooks
|
||||
github.com/docker/cli/cli-plugins/manager
|
||||
github.com/docker/cli/cli-plugins/metadata
|
||||
github.com/docker/cli/cli-plugins/plugin
|
||||
github.com/docker/cli/cli-plugins/socket
|
||||
github.com/docker/cli/cli/command
|
||||
@ -252,6 +251,7 @@ github.com/docker/cli/cli/context/store
|
||||
github.com/docker/cli/cli/debug
|
||||
github.com/docker/cli/cli/flags
|
||||
github.com/docker/cli/cli/hints
|
||||
github.com/docker/cli/cli/internal/jsonstream
|
||||
github.com/docker/cli/cli/manifest/store
|
||||
github.com/docker/cli/cli/manifest/types
|
||||
github.com/docker/cli/cli/registry/client
|
||||
@ -260,6 +260,7 @@ github.com/docker/cli/cli/trust
|
||||
github.com/docker/cli/cli/version
|
||||
github.com/docker/cli/internal/tui
|
||||
github.com/docker/cli/opts
|
||||
github.com/docker/cli/opts/swarmopts
|
||||
github.com/docker/cli/pkg/kvfile
|
||||
github.com/docker/cli/templates
|
||||
# github.com/docker/cli-docs-tool v0.9.0
|
||||
@ -283,7 +284,7 @@ github.com/docker/distribution/registry/client/transport
|
||||
github.com/docker/distribution/registry/storage/cache
|
||||
github.com/docker/distribution/registry/storage/cache/memory
|
||||
github.com/docker/distribution/uuid
|
||||
# github.com/docker/docker v28.0.1+incompatible
|
||||
# github.com/docker/docker v28.0.2+incompatible
|
||||
## explicit
|
||||
github.com/docker/docker/api
|
||||
github.com/docker/docker/api/types
|
||||
|
Loading…
x
Reference in New Issue
Block a user