build: go 1.24

We were running behind and there were quite some deprecations to update.
This was mostly in the upstream copy/pasta package but seems quite
minimal.
This commit is contained in:
2025-03-16 12:04:32 +01:00
parent a2b678caf6
commit 1723025fbf
822 changed files with 25433 additions and 197407 deletions

View File

@ -11,8 +11,8 @@ func PrintNextSteps(out io.Writer, messages []string) {
if len(messages) == 0 {
return
}
fmt.Fprintln(out, aec.Bold.Apply("\nWhat's next:"))
_, _ = fmt.Fprintln(out, aec.Bold.Apply("\nWhat's next:"))
for _, n := range messages {
_, _ = fmt.Fprintf(out, " %s\n", n)
_, _ = fmt.Fprintln(out, " ", n)
}
}

View File

@ -52,7 +52,6 @@ func AddPluginCommandStubs(dockerCli command.Cli, rootCmd *cobra.Command) (err e
return
}
for _, p := range plugins {
p := p
vendor := p.Vendor
if vendor == "" {
vendor = "unknown"
@ -82,7 +81,7 @@ func AddPluginCommandStubs(dockerCli command.Cli, rootCmd *cobra.Command) (err e
cmd.HelpFunc()(rootCmd, args)
return nil
}
return fmt.Errorf("docker: '%s' is not a docker command.\nSee 'docker --help'", cmd.Name())
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

View File

@ -32,7 +32,7 @@ const (
// errPluginNotFound is the error returned when a plugin could not be found.
type errPluginNotFound string
func (e errPluginNotFound) NotFound() {}
func (errPluginNotFound) NotFound() {}
func (e errPluginNotFound) Error() string {
return "Error: No such CLI plugin: " + string(e)

View File

@ -92,12 +92,8 @@ func FlagErrorFunc(cmd *cobra.Command, err error) error {
return nil
}
usage := ""
if cmd.HasSubCommands() {
usage = "\n\n" + cmd.UsageString()
}
return StatusError{
Status: fmt.Sprintf("%s\nSee '%s --help'.%s", err, cmd.CommandPath(), usage),
Status: fmt.Sprintf("%s\n\nUsage: %s\n\nRun '%s --help' for more information", err, cmd.UseLine(), cmd.CommandPath()),
StatusCode: 125,
}
}
@ -341,8 +337,10 @@ func operationSubCommands(cmd *cobra.Command) []*cobra.Command {
return cmds
}
const defaultTermWidth = 80
func wrappedFlagUsages(cmd *cobra.Command) string {
width := 80
width := defaultTermWidth
if ws, err := term.GetWinsize(0); err == nil {
width = int(ws.Width)
}
@ -522,4 +520,4 @@ Run '{{.CommandPath}} COMMAND --help' for more information on a command.
`
const helpTemplate = `
{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
{{- if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`

View File

@ -97,7 +97,7 @@ type DockerCli struct {
}
// DefaultVersion returns api.defaultVersion.
func (cli *DockerCli) DefaultVersion() string {
func (*DockerCli) DefaultVersion() string {
return api.DefaultVersion
}
@ -114,7 +114,7 @@ func (cli *DockerCli) CurrentVersion() string {
// Client returns the APIClient
func (cli *DockerCli) Client() client.APIClient {
if err := cli.initialize(); err != nil {
_, _ = fmt.Fprintf(cli.Err(), "Failed to initialize: %s\n", err)
_, _ = fmt.Fprintln(cli.Err(), "Failed to initialize:", err)
os.Exit(1)
}
return cli.client
@ -231,7 +231,7 @@ func (cli *DockerCli) HooksEnabled() bool {
}
// ManifestStore returns a store for local manifests
func (cli *DockerCli) ManifestStore() manifeststore.Store {
func (*DockerCli) ManifestStore() manifeststore.Store {
// TODO: support override default location from config file
return manifeststore.NewStore(filepath.Join(config.Dir(), "manifests"))
}
@ -272,7 +272,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...CLIOption)
debug.Enable()
}
if opts.Context != "" && len(opts.Hosts) > 0 {
return errors.New("conflicting options: either specify --host or --context, not both")
return errors.New("conflicting options: cannot specify both --host and --context")
}
cli.options = opts
@ -299,7 +299,7 @@ func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions, ops ...CLIOption)
// NewAPIClientFromFlags creates a new APIClient from command line flags
func NewAPIClientFromFlags(opts *cliflags.ClientOptions, configFile *configfile.ConfigFile) (client.APIClient, error) {
if opts.Context != "" && len(opts.Hosts) > 0 {
return nil, errors.New("conflicting options: either specify --host or --context, not both")
return nil, errors.New("conflicting options: cannot specify both --host and --context")
}
storeConfig := DefaultContextStoreConfig()
@ -475,7 +475,7 @@ func (cli *DockerCli) DockerEndpoint() docker.Endpoint {
if err := cli.initialize(); err != nil {
// Note that we're not terminating here, as this function may be used
// in cases where we're able to continue.
_, _ = fmt.Fprintf(cli.Err(), "%v\n", cli.initErr)
_, _ = fmt.Fprintln(cli.Err(), cli.initErr)
}
return cli.dockerEndpoint
}

View File

@ -177,7 +177,10 @@ func withCustomHeadersFromEnv() client.Opt {
csvReader := csv.NewReader(strings.NewReader(value))
fields, err := csvReader.Read()
if err != nil {
return errdefs.InvalidParameter(errors.Errorf("failed to parse custom headers from %s environment variable: value must be formatted as comma-separated key=value pairs", envOverrideHTTPHeaders))
return errdefs.InvalidParameter(errors.Errorf(
"failed to parse custom headers from %s environment variable: value must be formatted as comma-separated key=value pairs",
envOverrideHTTPHeaders,
))
}
if len(fields) == 0 {
return nil
@ -191,7 +194,10 @@ func withCustomHeadersFromEnv() client.Opt {
k = strings.TrimSpace(k)
if k == "" {
return errdefs.InvalidParameter(errors.Errorf(`failed to set custom headers from %s environment variable: value contains a key=value pair with an empty key: '%s'`, envOverrideHTTPHeaders, kv))
return errdefs.InvalidParameter(errors.Errorf(
`failed to set custom headers from %s environment variable: value contains a key=value pair with an empty key: '%s'`,
envOverrideHTTPHeaders, kv,
))
}
// We don't currently allow empty key=value pairs, and produce an error.
@ -199,7 +205,10 @@ func withCustomHeadersFromEnv() client.Opt {
// from an environment variable with the same name). In the meantime,
// produce an error to prevent users from depending on this.
if !hasValue {
return errdefs.InvalidParameter(errors.Errorf(`failed to set custom headers from %s environment variable: missing "=" in key=value pair: '%s'`, envOverrideHTTPHeaders, kv))
return errdefs.InvalidParameter(errors.Errorf(
`failed to set custom headers from %s environment variable: missing "=" in key=value pair: '%s'`,
envOverrideHTTPHeaders, kv,
))
}
env[http.CanonicalHeaderKey(k)] = v

View File

@ -12,7 +12,7 @@ import (
"time"
"github.com/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/go-units"
)
@ -67,10 +67,10 @@ ports: {{- pad .Ports 1 0}}
}
// ContainerWrite renders the context for a list of containers
func ContainerWrite(ctx Context, containers []types.Container) error {
func ContainerWrite(ctx Context, containers []container.Summary) error {
render := func(format func(subContext SubContext) error) error {
for _, container := range containers {
err := format(&ContainerContext{trunc: ctx.Trunc, c: container})
for _, ctr := range containers {
err := format(&ContainerContext{trunc: ctx.Trunc, c: ctr})
if err != nil {
return err
}
@ -84,7 +84,7 @@ func ContainerWrite(ctx Context, containers []types.Container) error {
type ContainerContext struct {
HeaderContext
trunc bool
c types.Container
c container.Summary
// FieldsUsed is used in the pre-processing step to detect which fields are
// used in the template. It's currently only used to detect use of the .Size
@ -193,7 +193,9 @@ func (c *ContainerContext) Command() string {
return strconv.Quote(command)
}
// CreatedAt returns the "Created" date/time of the container as a unix timestamp.
// CreatedAt returns the formatted string representing the container's creation date/time.
// The format may include nanoseconds if present.
// e.g. "2006-01-02 15:04:05.999999999 -0700 MST" or "2006-01-02 15:04:05 -0700 MST"
func (c *ContainerContext) CreatedAt() string {
return time.Unix(c.c.Created, 0).String()
}
@ -314,7 +316,7 @@ func (c *ContainerContext) Networks() string {
// DisplayablePorts returns formatted string representing open ports of container
// e.g. "0.0.0.0:80->9090/tcp, 9988/tcp"
// it's used by command 'docker ps'
func DisplayablePorts(ports []types.Port) string {
func DisplayablePorts(ports []container.Port) string {
type portGroup struct {
first uint16
last uint16
@ -375,12 +377,12 @@ func formGroup(key string, start, last uint16) string {
group = fmt.Sprintf("%s-%d", group, last)
}
if ip != "" {
group = fmt.Sprintf("%s:%s->%s", ip, group, group)
group = fmt.Sprintf("%s->%s", net.JoinHostPort(ip, group), group)
}
return group + "/" + groupType
}
func comparePorts(i, j types.Port) bool {
func comparePorts(i, j container.Port) bool {
if i.PrivatePort != j.PrivatePort {
return i.PrivatePort < j.PrivatePort
}

View File

@ -32,7 +32,7 @@ type SubContext interface {
type SubHeaderContext map[string]string
// Label returns the header label for the specified string
func (c SubHeaderContext) Label(name string) string {
func (SubHeaderContext) Label(name string) string {
n := strings.Split(name, ".")
r := strings.NewReplacer("-", " ", "_", " ")
h := r.Replace(n[len(n)-1])

View File

@ -9,6 +9,7 @@ import (
"github.com/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/volume"
units "github.com/docker/go-units"
@ -36,7 +37,7 @@ type DiskUsageContext struct {
Verbose bool
LayersSize int64
Images []*image.Summary
Containers []*types.Container
Containers []*container.Summary
Volumes []*volume.Volume
BuildCache []*types.BuildCache
BuilderSize int64
@ -124,7 +125,7 @@ func (ctx *DiskUsageContext) Write() (err error) {
return err
}
diskUsageContainersCtx := diskUsageContainersContext{containers: []*types.Container{}}
diskUsageContainersCtx := diskUsageContainersContext{containers: []*container.Summary{}}
diskUsageContainersCtx.Header = SubHeaderContext{
"Type": typeHeader,
"TotalCount": totalHeader,
@ -236,7 +237,7 @@ func (ctx *DiskUsageContext) verboseWriteTable(duc *diskUsageContext) error {
if err != nil {
return err
}
ctx.Output.Write([]byte("\nLocal Volumes space usage:\n\n"))
_, _ = ctx.Output.Write([]byte("\nLocal Volumes space usage:\n\n"))
for _, v := range duc.Volumes {
if err := ctx.contextFormat(tmpl, v); err != nil {
return err
@ -248,7 +249,7 @@ func (ctx *DiskUsageContext) verboseWriteTable(duc *diskUsageContext) error {
if err != nil {
return err
}
fmt.Fprintf(ctx.Output, "\nBuild cache usage: %s\n\n", units.HumanSize(float64(ctx.BuilderSize)))
_, _ = fmt.Fprintf(ctx.Output, "\nBuild cache usage: %s\n\n", units.HumanSize(float64(ctx.BuilderSize)))
for _, v := range duc.BuildCache {
if err := ctx.contextFormat(tmpl, v); err != nil {
return err
@ -269,7 +270,7 @@ func (c *diskUsageImagesContext) MarshalJSON() ([]byte, error) {
return MarshalJSON(c)
}
func (c *diskUsageImagesContext) Type() string {
func (*diskUsageImagesContext) Type() string {
return "Images"
}
@ -313,14 +314,14 @@ func (c *diskUsageImagesContext) Reclaimable() string {
type diskUsageContainersContext struct {
HeaderContext
containers []*types.Container
containers []*container.Summary
}
func (c *diskUsageContainersContext) MarshalJSON() ([]byte, error) {
return MarshalJSON(c)
}
func (c *diskUsageContainersContext) Type() string {
func (*diskUsageContainersContext) Type() string {
return "Containers"
}
@ -328,16 +329,16 @@ func (c *diskUsageContainersContext) TotalCount() string {
return strconv.Itoa(len(c.containers))
}
func (c *diskUsageContainersContext) isActive(container types.Container) bool {
return strings.Contains(container.State, "running") ||
strings.Contains(container.State, "paused") ||
strings.Contains(container.State, "restarting")
func (*diskUsageContainersContext) isActive(ctr container.Summary) bool {
return strings.Contains(ctr.State, "running") ||
strings.Contains(ctr.State, "paused") ||
strings.Contains(ctr.State, "restarting")
}
func (c *diskUsageContainersContext) Active() string {
used := 0
for _, container := range c.containers {
if c.isActive(*container) {
for _, ctr := range c.containers {
if c.isActive(*ctr) {
used++
}
}
@ -348,22 +349,21 @@ func (c *diskUsageContainersContext) Active() string {
func (c *diskUsageContainersContext) Size() string {
var size int64
for _, container := range c.containers {
size += container.SizeRw
for _, ctr := range c.containers {
size += ctr.SizeRw
}
return units.HumanSize(float64(size))
}
func (c *diskUsageContainersContext) Reclaimable() string {
var reclaimable int64
var totalSize int64
var reclaimable, totalSize int64
for _, container := range c.containers {
if !c.isActive(*container) {
reclaimable += container.SizeRw
for _, ctr := range c.containers {
if !c.isActive(*ctr) {
reclaimable += ctr.SizeRw
}
totalSize += container.SizeRw
totalSize += ctr.SizeRw
}
if totalSize > 0 {
@ -382,7 +382,7 @@ func (c *diskUsageVolumesContext) MarshalJSON() ([]byte, error) {
return MarshalJSON(c)
}
func (c *diskUsageVolumesContext) Type() string {
func (*diskUsageVolumesContext) Type() string {
return "Local Volumes"
}
@ -443,7 +443,7 @@ func (c *diskUsageBuilderContext) MarshalJSON() ([]byte, error) {
return MarshalJSON(c)
}
func (c *diskUsageBuilderContext) Type() string {
func (*diskUsageBuilderContext) Type() string {
return "Build Cache"
}

View File

@ -13,9 +13,10 @@ import (
configtypes "github.com/docker/cli/cli/config/types"
"github.com/docker/cli/cli/hints"
"github.com/docker/cli/cli/streams"
"github.com/docker/docker/api/types"
"github.com/docker/cli/internal/tui"
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/registry"
"github.com/morikuni/aec"
"github.com/pkg/errors"
)
@ -29,7 +30,7 @@ const (
// RegistryAuthenticationPrivilegedFunc returns a RequestPrivilegeFunc from the specified registry index info
// for the given command.
func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInfo, cmdName string) types.RequestPrivilegeFunc {
func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInfo, cmdName string) registrytypes.RequestAuthConfig {
return func(ctx context.Context) (string, error) {
_, _ = fmt.Fprintf(cli.Out(), "\nLogin prior to %s:\n", cmdName)
indexServer := registry.GetAuthConfigKey(index)
@ -179,6 +180,9 @@ func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword
}
}()
out := tui.NewOutput(cli.Err())
out.PrintNote("A Personal Access Token (PAT) can be used instead.\n" +
"To create a PAT, visit " + aec.Underline.Apply("https://app.docker.com/settings") + "\n\n")
argPassword, err = PromptForInput(ctx, cli.In(), cli.Out(), "Password: ")
if err != nil {
return registrytypes.AuthConfig{}, err

View File

@ -79,14 +79,6 @@ func ServiceProgress(ctx context.Context, apiClient client.APIClient, serviceID
signal.Notify(sigint, os.Interrupt)
defer signal.Stop(sigint)
taskFilter := filters.NewArgs()
taskFilter.Add("service", serviceID)
taskFilter.Add("_up-to-date", "true")
getUpToDateTasks := func() ([]swarm.Task, error) {
return apiClient.TaskList(ctx, types.TaskListOptions{Filters: taskFilter})
}
var (
updater progressUpdater
converged bool
@ -151,7 +143,10 @@ func ServiceProgress(ctx context.Context, apiClient client.APIClient, serviceID
return nil
}
tasks, err := getUpToDateTasks()
tasks, err := apiClient.TaskList(ctx, types.TaskListOptions{Filters: filters.NewArgs(
filters.KeyValuePair{Key: "service", Value: service.ID},
filters.KeyValuePair{Key: "_up-to-date", Value: "true"},
)})
if err != nil {
return err
}
@ -218,7 +213,10 @@ func ServiceProgress(ctx context.Context, apiClient client.APIClient, serviceID
}
}
func getActiveNodes(ctx context.Context, apiClient client.APIClient) (map[string]struct{}, error) {
// getActiveNodes returns all nodes that are currently not in status [swarm.NodeStateDown].
//
// TODO(thaJeztah): this should really be a filter on [apiClient.NodeList] instead of being filtered on the client side.
func getActiveNodes(ctx context.Context, apiClient client.NodeAPIClient) (map[string]struct{}, error) {
nodes, err := apiClient.NodeList(ctx, types.NodeListOptions{})
if err != nil {
return nil, err
@ -275,8 +273,9 @@ func truncError(errMsg string) string {
// Limit the length to 75 characters, so that even on narrow terminals
// this will not overflow to the next line.
if len(errMsg) > 75 {
errMsg = errMsg[:74] + "…"
const maxWidth = 75
if len(errMsg) > maxWidth {
errMsg = errMsg[:maxWidth-1] + "…"
}
return errMsg
}
@ -351,7 +350,7 @@ func (u *replicatedProgressUpdater) update(service swarm.Service, tasks []swarm.
return running == replicas, nil
}
func (u *replicatedProgressUpdater) tasksBySlot(tasks []swarm.Task, activeNodes map[string]struct{}) map[int]swarm.Task {
func (*replicatedProgressUpdater) tasksBySlot(tasks []swarm.Task, activeNodes map[string]struct{}) map[int]swarm.Task {
// If there are multiple tasks with the same slot number, favor the one
// with the *lowest* desired state. This can happen in restart
// scenarios.
@ -472,7 +471,7 @@ func (u *globalProgressUpdater) update(_ swarm.Service, tasks []swarm.Task, acti
return running == nodeCount, nil
}
func (u *globalProgressUpdater) tasksByNode(tasks []swarm.Task) map[string]swarm.Task {
func (*globalProgressUpdater) tasksByNode(tasks []swarm.Task) map[string]swarm.Task {
// If there are multiple tasks with the same node ID, favor the one
// with the *lowest* desired state. This can happen in restart
// scenarios.
@ -573,16 +572,17 @@ type replicatedJobProgressUpdater struct {
}
func newReplicatedJobProgressUpdater(service swarm.Service, progressOut progress.Output) *replicatedJobProgressUpdater {
u := &replicatedJobProgressUpdater{
progressOut: progressOut,
concurrent: int(*service.Spec.Mode.ReplicatedJob.MaxConcurrent),
total: int(*service.Spec.Mode.ReplicatedJob.TotalCompletions),
jobIteration: service.JobStatus.JobIteration.Index,
}
u.progressDigits = len(strconv.Itoa(u.total))
u.activeDigits = len(strconv.Itoa(u.concurrent))
concurrent := int(*service.Spec.Mode.ReplicatedJob.MaxConcurrent)
total := int(*service.Spec.Mode.ReplicatedJob.TotalCompletions)
return u
return &replicatedJobProgressUpdater{
progressOut: progressOut,
concurrent: concurrent,
total: total,
jobIteration: service.JobStatus.JobIteration.Index,
progressDigits: len(strconv.Itoa(total)),
activeDigits: len(strconv.Itoa(concurrent)),
}
}
// update writes out the progress of the replicated job.

View File

@ -14,7 +14,7 @@ import (
"go.opentelemetry.io/otel/sdk/metric/metricdata"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"go.opentelemetry.io/otel/trace"
)
@ -54,11 +54,11 @@ func (cli *DockerCli) Resource() *resource.Resource {
return cli.res.Get()
}
func (cli *DockerCli) TracerProvider() trace.TracerProvider {
func (*DockerCli) TracerProvider() trace.TracerProvider {
return otel.GetTracerProvider()
}
func (cli *DockerCli) MeterProvider() metric.MeterProvider {
func (*DockerCli) MeterProvider() metric.MeterProvider {
return otel.GetMeterProvider()
}

View File

@ -199,7 +199,7 @@ func PruneFilters(dockerCli Cli, pruneFilters filters.Args) filters.Args {
// AddPlatformFlag adds `platform` to a set of flags for API version 1.32 and later.
func AddPlatformFlag(flags *pflag.FlagSet, target *string) {
flags.StringVar(target, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
flags.SetAnnotation("platform", "version", []string{"1.32"})
_ = flags.SetAnnotation("platform", "version", []string{"1.32"})
}
// ValidateOutputPath validates the output paths of the `export` and `save` commands.

View File

@ -25,7 +25,7 @@ import (
"github.com/google/shlex"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
yaml "gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
)
// Options supported by Load
@ -53,11 +53,11 @@ func ParseYAML(source []byte) (map[string]any, error) {
if err := yaml.Unmarshal(source, &cfg); err != nil {
return nil, err
}
cfgMap, ok := cfg.(map[any]any)
_, ok := cfg.(map[string]any)
if !ok {
return nil, errors.Errorf("top-level object must be a mapping")
}
converted, err := convertToStringKeysRecursive(cfgMap, "")
converted, err := convertToStringKeysRecursive(cfg, "")
if err != nil {
return nil, err
}
@ -269,7 +269,7 @@ type ForbiddenPropertiesError struct {
Properties map[string]string
}
func (e *ForbiddenPropertiesError) Error() string {
func (*ForbiddenPropertiesError) Error() string {
return "Configuration contains forbidden properties"
}
@ -349,24 +349,20 @@ func createTransformHook(additionalTransformers ...Transformer) mapstructure.Dec
// keys needs to be converted to strings for jsonschema
func convertToStringKeysRecursive(value any, keyPrefix string) (any, error) {
if mapping, ok := value.(map[any]any); ok {
if mapping, ok := value.(map[string]any); ok {
dict := make(map[string]any)
for key, entry := range mapping {
str, ok := key.(string)
if !ok {
return nil, formatInvalidKeyError(keyPrefix, key)
}
var newKeyPrefix string
if keyPrefix == "" {
newKeyPrefix = str
newKeyPrefix = key
} else {
newKeyPrefix = fmt.Sprintf("%s.%s", keyPrefix, str)
newKeyPrefix = fmt.Sprintf("%s.%s", keyPrefix, key)
}
convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix)
if err != nil {
return nil, err
}
dict[str] = convertedEntry
dict[key] = convertedEntry
}
return dict, nil
}
@ -385,16 +381,6 @@ func convertToStringKeysRecursive(value any, keyPrefix string) (any, error) {
return value, nil
}
func formatInvalidKeyError(keyPrefix string, key any) error {
var location string
if keyPrefix == "" {
location = "at top level"
} else {
location = "in " + keyPrefix
}
return errors.Errorf("non-string key %s: %#v", location, key)
}
// LoadServices produces a ServiceConfig map from a compose file Dict
// the servicesDict is not validated if directly used. Use Load() to enable validation
func LoadServices(servicesDict map[string]any, workingDir string, lookupEnv template.Mapping) ([]types.ServiceConfig, error) {

View File

@ -68,7 +68,6 @@ func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig,
},
}
for name, overrideService := range overrideServices {
overrideService := overrideService
if baseService, ok := baseServices[name]; ok {
if err := mergo.Merge(&baseService, &overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(specials)); err != nil {
return base, errors.Wrapf(err, "cannot merge service %s", name)

View File

@ -6,9 +6,11 @@ package schema
import (
"embed"
"fmt"
"math/big"
"strings"
"time"
"github.com/docker/go-connections/nat"
"github.com/pkg/errors"
"github.com/xeipuuv/gojsonschema"
)
@ -20,14 +22,23 @@ const (
type portsFormatChecker struct{}
func (checker portsFormatChecker) IsFormat(_ any) bool {
// TODO: implement this
return true
func (portsFormatChecker) IsFormat(input any) bool {
var portSpec string
switch p := input.(type) {
case string:
portSpec = p
case *big.Rat:
portSpec = strings.Split(p.String(), "/")[0]
}
_, err := nat.ParsePortSpec(portSpec)
return err == nil
}
type durationFormatChecker struct{}
func (checker durationFormatChecker) IsFormat(input any) bool {
func (durationFormatChecker) IsFormat(input any) bool {
value, ok := input.(string)
if !ok {
return false
@ -37,7 +48,6 @@ func (checker durationFormatChecker) IsFormat(input any) bool {
}
func init() {
gojsonschema.FormatCheckers.Add("expose", portsFormatChecker{})
gojsonschema.FormatCheckers.Add("ports", portsFormatChecker{})
gojsonschema.FormatCheckers.Add("duration", durationFormatChecker{})
}

View File

@ -398,6 +398,7 @@ type ServiceVolumeConfig struct {
Consistency string `yaml:",omitempty" json:"consistency,omitempty"`
Bind *ServiceVolumeBind `yaml:",omitempty" json:"bind,omitempty"`
Volume *ServiceVolumeVolume `yaml:",omitempty" json:"volume,omitempty"`
Image *ServiceVolumeImage `yaml:",omitempty" json:"image,omitempty"`
Tmpfs *ServiceVolumeTmpfs `yaml:",omitempty" json:"tmpfs,omitempty"`
Cluster *ServiceVolumeCluster `yaml:",omitempty" json:"cluster,omitempty"`
}
@ -409,7 +410,13 @@ type ServiceVolumeBind struct {
// ServiceVolumeVolume are options for a service volume of type volume
type ServiceVolumeVolume struct {
NoCopy bool `mapstructure:"nocopy" yaml:"nocopy,omitempty" json:"nocopy,omitempty"`
NoCopy bool `mapstructure:"nocopy" yaml:"nocopy,omitempty" json:"nocopy,omitempty"`
Subpath string `mapstructure:"subpath" yaml:"subpath,omitempty" json:"subpath,omitempty"`
}
// ServiceVolumeImage are options for a service volume of type image
type ServiceVolumeImage struct {
Subpath string `mapstructure:"subpath" yaml:"subpath,omitempty" json:"subpath,omitempty"`
}
// ServiceVolumeTmpfs are options for a service volume of type tmpfs

View File

@ -143,7 +143,7 @@ func load(configDir string) (*configfile.ConfigFile, error) {
defer file.Close()
err = configFile.LoadFromReader(file)
if err != nil {
err = errors.Wrapf(err, "loading config file: %s: ", filename)
err = errors.Wrapf(err, "parsing config file (%s)", filename)
}
return configFile, err
}

View File

@ -1,9 +1,12 @@
package credentials
import (
"fmt"
"net"
"net/url"
"os"
"strings"
"sync/atomic"
"github.com/docker/cli/cli/config/types"
)
@ -57,6 +60,21 @@ func (c *fileStore) GetAll() (map[string]types.AuthConfig, error) {
return c.file.GetAuthConfigs(), nil
}
// unencryptedWarning warns the user when using an insecure credential storage.
// After a deprecation period, user will get prompted if stdin and stderr are a terminal.
// Otherwise, we'll assume they want it (sadly), because people may have been scripting
// insecure logins and we don't want to break them. Maybe they'll see the warning in their
// logs and fix things.
const unencryptedWarning = `
WARNING! Your credentials are stored unencrypted in '%s'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/
`
// alreadyPrinted ensures that we only print the unencryptedWarning once per
// CLI invocation (no need to warn the user multiple times per command).
var alreadyPrinted atomic.Bool
// Store saves the given credentials in the file store. This function is
// idempotent and does not update the file if credentials did not change.
func (c *fileStore) Store(authConfig types.AuthConfig) error {
@ -66,15 +84,19 @@ func (c *fileStore) Store(authConfig types.AuthConfig) error {
return nil
}
authConfigs[authConfig.ServerAddress] = authConfig
return c.file.Save()
}
if err := c.file.Save(); err != nil {
return err
}
func (c *fileStore) GetFilename() string {
return c.file.GetFilename()
}
if !alreadyPrinted.Load() && authConfig.Password != "" {
// Display a warning if we're storing the users password (not a token).
//
// FIXME(thaJeztah): make output configurable instead of hardcoding to os.Stderr
_, _ = fmt.Fprintln(os.Stderr, fmt.Sprintf(unencryptedWarning, c.file.GetFilename()))
alreadyPrinted.Store(true)
}
func (c *fileStore) IsFileStore() bool {
return true
return nil
}
// ConvertToHostname converts a registry url which has http|https prepended

View File

@ -33,18 +33,28 @@ import (
)
// New returns net.Conn
func New(_ context.Context, cmd string, args ...string) (net.Conn, error) {
var (
c commandConn
err error
)
c.cmd = exec.Command(cmd, args...)
func New(ctx context.Context, cmd string, args ...string) (net.Conn, error) {
// Don't kill the ssh process if the context is cancelled. Killing the
// ssh process causes an error when go's http.Client tries to reuse the
// net.Conn (commandConn).
//
// Not passing down the Context might seem counter-intuitive, but in this
// case, the lifetime of the process should be managed by the http.Client,
// not the caller's Context.
//
// Further details;;
//
// - https://github.com/docker/cli/pull/3900
// - https://github.com/docker/compose/issues/9448#issuecomment-1264263721
ctx = context.WithoutCancel(ctx)
c := commandConn{cmd: exec.CommandContext(ctx, cmd, args...)}
// we assume that args never contains sensitive information
logrus.Debugf("commandconn: starting %s with %v", cmd, args)
c.cmd.Env = os.Environ()
c.cmd.SysProcAttr = &syscall.SysProcAttr{}
setPdeathsig(c.cmd)
createSession(c.cmd)
var err error
c.stdin, err = c.cmd.StdinPipe()
if err != nil {
return nil, err
@ -243,17 +253,17 @@ func (c *commandConn) RemoteAddr() net.Addr {
return c.remoteAddr
}
func (c *commandConn) SetDeadline(t time.Time) error {
func (*commandConn) SetDeadline(t time.Time) error {
logrus.Debugf("unimplemented call: SetDeadline(%v)", t)
return nil
}
func (c *commandConn) SetReadDeadline(t time.Time) error {
func (*commandConn) SetReadDeadline(t time.Time) error {
logrus.Debugf("unimplemented call: SetReadDeadline(%v)", t)
return nil
}
func (c *commandConn) SetWriteDeadline(t time.Time) error {
func (*commandConn) SetWriteDeadline(t time.Time) error {
logrus.Debugf("unimplemented call: SetWriteDeadline(%v)", t)
return nil
}

View File

@ -12,7 +12,7 @@ import (
"sort"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/atomicwriter"
"github.com/fvbommel/sortorder"
"github.com/pkg/errors"
)
@ -40,7 +40,7 @@ func (s *metadataStore) createOrUpdate(meta Metadata) error {
if err != nil {
return err
}
return ioutils.AtomicWriteFile(filepath.Join(contextDir, metaFile), bytes, 0o644)
return atomicwriter.WriteFile(filepath.Join(contextDir, metaFile), bytes, 0o644)
}
func parseTypedOrMap(payload []byte, getter TypeGetter) (any, error) {

View File

@ -5,7 +5,7 @@ import (
"path/filepath"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/atomicwriter"
"github.com/pkg/errors"
)
@ -32,7 +32,7 @@ func (s *tlsStore) createOrUpdate(name, endpointName, filename string, data []by
if err := os.MkdirAll(endpointDir, 0o700); err != nil {
return err
}
return ioutils.AtomicWriteFile(filepath.Join(endpointDir, filename), data, 0o600)
return atomicwriter.WriteFile(filepath.Join(endpointDir, filename), data, 0o600)
}
func (s *tlsStore) getData(name, endpointName, filename string) ([]byte, error) {

View File

@ -10,14 +10,14 @@ import (
// Enable sets the DEBUG env var to true
// and makes the logger to log at debug level.
func Enable() {
os.Setenv("DEBUG", "1")
_ = os.Setenv("DEBUG", "1")
logrus.SetLevel(logrus.DebugLevel)
}
// Disable sets the DEBUG env var to false
// and makes the logger to log at info level.
func Disable() {
os.Setenv("DEBUG", "")
_ = os.Setenv("DEBUG", "")
logrus.SetLevel(logrus.InfoLevel)
}

View File

@ -1,35 +1,27 @@
package cli
import (
"fmt"
"strings"
)
// Errors is a list of errors.
// Useful in a loop if you don't want to return the error right away and you want to display after the loop,
// all the errors that happened during the loop.
//
// Deprecated: use [errors.Join] instead; will be removed in the next release.
type Errors []error
func (errList Errors) Error() string {
if len(errList) < 1 {
return ""
}
out := make([]string, len(errList))
for i := range errList {
out[i] = errList[i].Error()
}
return strings.Join(out, ", ")
}
// StatusError reports an unsuccessful exit by a command.
type StatusError struct {
Cause error
Status string
StatusCode int
}
// Error formats the error for printing. If a custom Status is provided,
// it is returned as-is, otherwise it generates a generic error-message
// based on the StatusCode.
func (e StatusError) Error() string {
return fmt.Sprintf("Status: %s, Code: %d", e.Status, e.StatusCode)
if e.Status != "" {
return e.Status
}
if e.Cause != nil {
return e.Cause.Error()
}
// we don't want to set a default message here,
// some commands might want to be explicit about the error message
return ""
}
func (e StatusError) Unwrap() error {
return e.Cause
}

View File

@ -138,7 +138,7 @@ func SetLogLevel(logLevel string) {
if logLevel != "" {
lvl, err := logrus.ParseLevel(logLevel)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to parse logging level: %s\n", logLevel)
_, _ = fmt.Fprintln(os.Stderr, "Unable to parse logging level:", logLevel)
os.Exit(1)
}
logrus.SetLevel(lvl)

View File

@ -5,7 +5,9 @@ import (
"strconv"
)
// Enabled returns whether cli hints are enabled or not
// Enabled returns whether cli hints are enabled or not. Hints are enabled by
// default, but can be disabled through the "DOCKER_CLI_HINTS" environment
// variable.
func Enabled() bool {
if v := os.Getenv("DOCKER_CLI_HINTS"); v != "" {
enabled, err := strconv.ParseBool(v)

View File

@ -44,7 +44,7 @@ func (s *fsStore) Get(listRef reference.Reference, manifest reference.Reference)
return s.getFromFilename(manifest, filename)
}
func (s *fsStore) getFromFilename(ref reference.Reference, filename string) (types.ImageManifest, error) {
func (*fsStore) getFromFilename(ref reference.Reference, filename string) (types.ImageManifest, error) {
bytes, err := os.ReadFile(filename)
switch {
case os.IsNotExist(err):
@ -165,7 +165,7 @@ func (n *notFoundError) Error() string {
}
// NotFound interface
func (n *notFoundError) NotFound() {}
func (*notFoundError) NotFound() {}
// IsNotFound returns true if the error is a not found error
func IsNotFound(err error) bool {

View File

@ -22,12 +22,7 @@ type repositoryEndpoint struct {
// Name returns the repository name
func (r repositoryEndpoint) Name() string {
repoName := r.info.Name.Name()
// If endpoint does not support CanonicalName, use the RemoteName instead
if r.endpoint.TrimHostname {
repoName = reference.Path(r.info.Name)
}
return repoName
return reference.Path(r.info.Name)
}
// BaseURL returns the endpoint url
@ -128,6 +123,6 @@ func (th *existingTokenHandler) AuthorizeRequest(req *http.Request, _ map[string
return nil
}
func (th *existingTokenHandler) Scheme() string {
func (*existingTokenHandler) Scheme() string {
return "bearer"
}

View File

@ -304,4 +304,4 @@ func (n *notFoundError) Error() string {
}
// NotFound satisfies interface github.com/docker/docker/errdefs.ErrNotFound
func (n *notFoundError) NotFound() {}
func (notFoundError) NotFound() {}

View File

@ -1,8 +1,6 @@
package cli
import (
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@ -14,15 +12,20 @@ func NoArgs(cmd *cobra.Command, args []string) error {
}
if cmd.HasSubCommands() {
return errors.New("\n" + strings.TrimRight(cmd.UsageString(), "\n"))
return errors.Errorf(
"%[1]s: unknown command: %[2]s %[3]s\n\nUsage: %[4]s\n\nRun '%[2]s --help' for more information",
binName(cmd),
cmd.CommandPath(),
args[0],
cmd.UseLine(),
)
}
return errors.Errorf(
"%q accepts no arguments.\nSee '%s --help'.\n\nUsage: %s\n\n%s",
cmd.CommandPath(),
"%[1]s: '%[2]s' accepts no arguments\n\nUsage: %[3]s\n\nRun '%[2]s --help' for more information",
binName(cmd),
cmd.CommandPath(),
cmd.UseLine(),
cmd.Short,
)
}
@ -33,13 +36,12 @@ func RequiresMinArgs(minArgs int) cobra.PositionalArgs {
return nil
}
return errors.Errorf(
"%q requires at least %d %s.\nSee '%s --help'.\n\nUsage: %s\n\n%s",
"%[1]s: '%[2]s' requires at least %[3]d %[4]s\n\nUsage: %[5]s\n\nSee '%[2]s --help' for more information",
binName(cmd),
cmd.CommandPath(),
minArgs,
pluralize("argument", minArgs),
cmd.CommandPath(),
cmd.UseLine(),
cmd.Short,
)
}
}
@ -51,13 +53,12 @@ func RequiresMaxArgs(maxArgs int) cobra.PositionalArgs {
return nil
}
return errors.Errorf(
"%q requires at most %d %s.\nSee '%s --help'.\n\nUsage: %s\n\n%s",
"%[1]s: '%[2]s' requires at most %[3]d %[4]s\n\nUsage: %[5]s\n\nSRun '%[2]s --help' for more information",
binName(cmd),
cmd.CommandPath(),
maxArgs,
pluralize("argument", maxArgs),
cmd.CommandPath(),
cmd.UseLine(),
cmd.Short,
)
}
}
@ -69,14 +70,13 @@ func RequiresRangeArgs(minArgs int, maxArgs int) cobra.PositionalArgs {
return nil
}
return errors.Errorf(
"%q requires at least %d and at most %d %s.\nSee '%s --help'.\n\nUsage: %s\n\n%s",
"%[1]s: '%[2]s' requires at least %[3]d and at most %[4]d %[5]s\n\nUsage: %[6]s\n\nRun '%[2]s --help' for more information",
binName(cmd),
cmd.CommandPath(),
minArgs,
maxArgs,
pluralize("argument", maxArgs),
cmd.CommandPath(),
cmd.UseLine(),
cmd.Short,
)
}
}
@ -88,17 +88,21 @@ func ExactArgs(number int) cobra.PositionalArgs {
return nil
}
return errors.Errorf(
"%q requires exactly %d %s.\nSee '%s --help'.\n\nUsage: %s\n\n%s",
"%[1]s: '%[2]s' requires %[3]d %[4]s\n\nUsage: %[5]s\n\nRun '%[2]s --help' for more information",
binName(cmd),
cmd.CommandPath(),
number,
pluralize("argument", number),
cmd.CommandPath(),
cmd.UseLine(),
cmd.Short,
)
}
}
// binName returns the name of the binary / root command (usually 'docker').
func binName(cmd *cobra.Command) string {
return cmd.Root().Name()
}
//nolint:unparam
func pluralize(word string, number int) string {
if number == 1 {

View File

@ -89,7 +89,7 @@ func (scs simpleCredentialStore) RefreshToken(*url.URL, string) string {
return scs.auth.IdentityToken
}
func (scs simpleCredentialStore) SetRefreshToken(*url.URL, string, string) {}
func (simpleCredentialStore) SetRefreshToken(*url.URL, string, string) {}
// GetNotaryRepository returns a NotaryRepository which stores all the
// information needed to operate on a notary repository.

12
vendor/github.com/docker/cli/internal/tui/chip.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// 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 tui
import "strconv"
func Chip(fg, bg int, content string) string {
fgAnsi := "\x1b[38;5;" + strconv.Itoa(fg) + "m"
bgAnsi := "\x1b[48;5;" + strconv.Itoa(bg) + "m"
return fgAnsi + bgAnsi + content + "\x1b[0m"
}

33
vendor/github.com/docker/cli/internal/tui/colors.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
// 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 tui
import (
"github.com/morikuni/aec"
)
var (
ColorTitle = aec.NewBuilder(aec.DefaultF, aec.Bold).ANSI
ColorPrimary = aec.NewBuilder(aec.DefaultF, aec.Bold).ANSI
ColorSecondary = aec.DefaultF
ColorTertiary = aec.NewBuilder(aec.DefaultF, aec.Faint).ANSI
ColorLink = aec.NewBuilder(aec.LightCyanF, aec.Underline).ANSI
ColorWarning = aec.LightYellowF
ColorFlag = aec.NewBuilder(aec.Bold).ANSI
ColorNone = aec.ANSI(noColor{})
)
type noColor struct{}
func (a noColor) With(_ ...aec.ANSI) aec.ANSI {
return a
}
func (noColor) Apply(s string) string {
return s
}
func (noColor) String() string {
return ""
}

70
vendor/github.com/docker/cli/internal/tui/count.go generated vendored Normal file
View File

@ -0,0 +1,70 @@
// 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 tui
import (
"strings"
"github.com/mattn/go-runewidth"
)
func cleanANSI(s string) string {
for {
start := strings.Index(s, "\x1b")
if start == -1 {
return s
}
end := strings.Index(s[start:], "m")
if end == -1 {
return s
}
s = s[:start] + s[start+end+1:]
}
}
// Width returns the width of the string, ignoring ANSI escape codes.
// Not all ANSI escape codes are supported yet.
func Width(s string) int {
return runewidth.StringWidth(cleanANSI(s))
}
// Ellipsis truncates a string to a given number of runes with an ellipsis at the end.
// It tries to persist the ANSI escape sequences.
func Ellipsis(s string, length int) string {
out := make([]rune, 0, length)
ln := 0
inEscape := false
tooLong := false
for _, r := range s {
if r == '\x1b' {
out = append(out, r)
inEscape = true
continue
}
if inEscape {
out = append(out, r)
if r == 'm' {
inEscape = false
if tooLong {
break
}
}
continue
}
ln += 1
if ln == length {
tooLong = true
}
if !tooLong {
out = append(out, r)
}
}
if tooLong {
return string(out) + "…"
}
return string(out)
}

39
vendor/github.com/docker/cli/internal/tui/note.go generated vendored Normal file
View File

@ -0,0 +1,39 @@
// 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 tui
import (
"fmt"
"strings"
"github.com/morikuni/aec"
)
var InfoHeader = Str{
Plain: " Info -> ",
Fancy: aec.Bold.Apply(aec.LightCyanB.Apply(aec.BlackF.Apply("i")) + " " + aec.LightCyanF.Apply("Info → ")),
}
func (o Output) PrintNote(format string, args ...any) {
if o.isTerminal {
// TODO: Handle all flags
format = strings.ReplaceAll(format, "--platform", ColorFlag.Apply("--platform"))
}
header := o.Sprint(InfoHeader)
_, _ = fmt.Fprint(o, "\n", header)
s := fmt.Sprintf(format, args...)
for idx, line := range strings.Split(s, "\n") {
if idx > 0 {
_, _ = fmt.Fprint(o, strings.Repeat(" ", Width(header)))
}
l := line
if o.isTerminal {
l = aec.Italic.Apply(l)
}
_, _ = fmt.Fprintln(o, l)
}
}

62
vendor/github.com/docker/cli/internal/tui/output.go generated vendored Normal file
View File

@ -0,0 +1,62 @@
// 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 tui
import (
"fmt"
"github.com/docker/cli/cli/streams"
"github.com/morikuni/aec"
)
type Output struct {
*streams.Out
isTerminal bool
}
type terminalPrintable interface {
String(isTerminal bool) string
}
func NewOutput(out *streams.Out) Output {
return Output{
Out: out,
isTerminal: out.IsTerminal(),
}
}
func (o Output) Color(clr aec.ANSI) aec.ANSI {
if o.isTerminal {
return clr
}
return ColorNone
}
func (o Output) Sprint(all ...any) string {
var out []any
for _, p := range all {
if s, ok := p.(terminalPrintable); ok {
out = append(out, s.String(o.isTerminal))
} else {
out = append(out, p)
}
}
return fmt.Sprint(out...)
}
func (o Output) PrintlnWithColor(clr aec.ANSI, args ...any) {
msg := o.Sprint(args...)
if o.isTerminal {
msg = clr.Apply(msg)
}
_, _ = fmt.Fprintln(o.Out, msg)
}
func (o Output) Println(p ...any) {
_, _ = fmt.Fprintln(o.Out, o.Sprint(p...))
}
func (o Output) Print(p ...any) {
_, _ = fmt.Print(o.Out, o.Sprint(p...))
}

19
vendor/github.com/docker/cli/internal/tui/str.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
// 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 tui
type Str struct {
// Fancy is the fancy string representation of the string.
Fancy string
// Plain is the plain string representation of the string.
Plain string
}
func (p Str) String(isTerminal bool) string {
if isTerminal {
return p.Fancy
}
return p.Plain
}

View File

@ -80,7 +80,7 @@ func (o *ConfigOpt) Set(value string) error {
}
// Type returns the type of this option
func (o *ConfigOpt) Type() string {
func (*ConfigOpt) Type() string {
return "config"
}

View File

@ -46,7 +46,7 @@ func (d *DurationOpt) Set(s string) error {
}
// Type returns the type of this option, which will be displayed in `--help` output
func (d *DurationOpt) Type() string {
func (*DurationOpt) Type() string {
return "duration"
}

View File

@ -92,7 +92,7 @@ func (o *GpuOpts) Set(value string) error {
}
// Type returns the type of this option
func (o *GpuOpts) Type() string {
func (*GpuOpts) Type() string {
return "gpu-request"
}

View File

@ -43,6 +43,13 @@ func (m *MountOpt) Set(value string) error {
return mount.VolumeOptions
}
imageOptions := func() *mounttypes.ImageOptions {
if mount.ImageOptions == nil {
mount.ImageOptions = new(mounttypes.ImageOptions)
}
return mount.ImageOptions
}
bindOptions := func() *mounttypes.BindOptions {
if mount.BindOptions == nil {
mount.BindOptions = new(mounttypes.BindOptions)
@ -147,6 +154,8 @@ func (m *MountOpt) Set(value string) error {
volumeOptions().DriverConfig.Options = make(map[string]string)
}
setValueOnMap(volumeOptions().DriverConfig.Options, val)
case "image-subpath":
imageOptions().Subpath = val
case "tmpfs-size":
sizeBytes, err := units.RAMInBytes(val)
if err != nil {
@ -175,6 +184,9 @@ func (m *MountOpt) Set(value string) error {
if mount.VolumeOptions != nil && mount.Type != mounttypes.TypeVolume {
return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", mount.Type)
}
if mount.ImageOptions != nil && mount.Type != mounttypes.TypeImage {
return fmt.Errorf("cannot mix 'image-*' options with mount type '%s'", mount.Type)
}
if mount.BindOptions != nil && mount.Type != mounttypes.TypeBind {
return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", mount.Type)
}
@ -203,7 +215,7 @@ func (m *MountOpt) Set(value string) error {
}
// Type returns the type of this option
func (m *MountOpt) Type() string {
func (*MountOpt) Type() string {
return "mount"
}

View File

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
)
@ -16,6 +17,7 @@ const (
networkOptMacAddress = "mac-address"
networkOptLinkLocalIP = "link-local-ip"
driverOpt = "driver-opt"
gwPriorityOpt = "gw-priority"
)
// NetworkAttachmentOpts represents the network options for endpoint creation
@ -28,6 +30,7 @@ type NetworkAttachmentOpts struct {
IPv6Address string
LinkLocalIPs []string
MacAddress string
GwPriority int
}
// NetworkOpt represents a network config in swarm mode.
@ -83,6 +86,11 @@ func (n *NetworkOpt) Set(value string) error { //nolint:gocyclo
netOpt.DriverOpts = make(map[string]string)
}
netOpt.DriverOpts[key] = val
case gwPriorityOpt:
netOpt.GwPriority, err = strconv.Atoi(val)
if err != nil {
return fmt.Errorf("invalid gw-priority: %w", err)
}
default:
return errors.New("invalid field key " + key)
}
@ -98,7 +106,7 @@ func (n *NetworkOpt) Set(value string) error { //nolint:gocyclo
}
// Type returns the type of this option
func (n *NetworkOpt) Type() string {
func (*NetworkOpt) Type() string {
return "network"
}
@ -108,7 +116,7 @@ func (n *NetworkOpt) Value() []NetworkAttachmentOpts {
}
// String returns the network opts as a string
func (n *NetworkOpt) String() string {
func (*NetworkOpt) String() string {
return ""
}

View File

@ -110,7 +110,7 @@ func (opts *ListOpts) Len() int {
}
// Type returns a string name for this Option type
func (opts *ListOpts) Type() string {
func (*ListOpts) Type() string {
return "list"
}
@ -180,7 +180,7 @@ func (opts *MapOpts) String() string {
}
// Type returns a string name for this Option type
func (opts *MapOpts) Type() string {
func (*MapOpts) Type() string {
return "map"
}
@ -358,7 +358,7 @@ func (o *FilterOpt) Set(value string) error {
}
// Type returns the option type
func (o *FilterOpt) Type() string {
func (*FilterOpt) Type() string {
return "filter"
}
@ -386,7 +386,7 @@ func (c *NanoCPUs) Set(value string) error {
}
// Type returns the type
func (c *NanoCPUs) Type() string {
func (*NanoCPUs) Type() string {
return "decimal"
}
@ -463,7 +463,7 @@ func (m *MemBytes) Set(value string) error {
}
// Type returns the type
func (m *MemBytes) Type() string {
func (*MemBytes) Type() string {
return "bytes"
}
@ -498,7 +498,7 @@ func (m *MemSwapBytes) Set(value string) error {
}
// Type returns the type
func (m *MemSwapBytes) Type() string {
func (*MemSwapBytes) Type() string {
return "bytes"
}

View File

@ -121,7 +121,7 @@ func (p *PortOpt) Set(value string) error {
}
// Type returns the type of this option
func (p *PortOpt) Type() string {
func (*PortOpt) Type() string {
return "port"
}

View File

@ -13,7 +13,7 @@ func (s *QuotedString) Set(val string) error {
}
// Type returns the type of the value
func (s *QuotedString) Type() string {
func (*QuotedString) Type() string {
return "string"
}

View File

@ -79,7 +79,7 @@ func (o *SecretOpt) Set(value string) error {
}
// Type returns the type of this option
func (o *SecretOpt) Type() string {
func (*SecretOpt) Type() string {
return "secret"
}

View File

@ -100,6 +100,6 @@ func (opt *ThrottledeviceOpt) GetList() []*blkiodev.ThrottleDevice {
}
// Type returns the option type
func (opt *ThrottledeviceOpt) Type() string {
func (*ThrottledeviceOpt) Type() string {
return "list"
}

View File

@ -58,6 +58,6 @@ func (o *UlimitOpt) GetList() []*container.Ulimit {
}
// Type returns the option type
func (o *UlimitOpt) Type() string {
func (*UlimitOpt) Type() string {
return "ulimit"
}

View File

@ -79,6 +79,6 @@ func (opt *WeightdeviceOpt) GetList() []*blkiodev.WeightDevice {
}
// Type returns the option type
func (opt *WeightdeviceOpt) Type() string {
func (*WeightdeviceOpt) Type() string {
return "list"
}