Compare commits
58
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fbb22f658 | ||
|
|
890dcca877 | ||
|
|
d544885316 | ||
|
|
c197aa70ee | ||
|
|
ba683d8df3 | ||
|
|
0aedba58c2 | ||
|
|
dd2be022c0 | ||
|
|
360952c8d3 | ||
|
|
8fc15eaf2c | ||
|
|
1abfbf298c | ||
|
|
e0d30db115 | ||
|
|
5691ade75a | ||
|
|
848dcad809 | ||
|
|
6a0099bc8a | ||
|
|
c90166ffa6 | ||
|
|
ac5e886124 | ||
|
|
3ec414638c | ||
|
|
616e93a0c2 | ||
|
|
1202f8a642 | ||
|
|
b67055c963 | ||
|
|
eee3e3d015 | ||
|
|
3247a5aae3 | ||
|
|
4759615835 | ||
|
|
3099d4716c | ||
|
|
511dad69d0 | ||
|
|
11f24b8458 | ||
|
|
d84396d4eb | ||
|
|
6751cd1690 | ||
|
|
8108357bcb | ||
|
|
3a842587f9 | ||
|
|
eedd9698e9 | ||
|
|
dd2c493825 | ||
|
|
67cef775fe | ||
|
|
207bf52c27 | ||
|
|
2cfd9df568 | ||
|
|
be9e6308f5 | ||
|
|
88e324150b | ||
|
|
2ae51e2d69 | ||
|
|
ed281ddf52 | ||
|
|
aa5d00a3a4 | ||
|
|
b66b93130c | ||
|
|
c44e8a0727 | ||
|
|
bff56f0493 | ||
|
|
3d4129b9ea | ||
|
|
d787e70a14 | ||
|
|
e730f6f0f3 | ||
|
|
6ac3f93755 | ||
|
|
ebc1995f9f | ||
|
|
31d1a59d07 | ||
|
|
ad96811f12 | ||
|
|
6ba06b5fb4 | ||
|
|
e0716b571f | ||
|
|
179efae8b0 | ||
|
|
4b450f113b | ||
|
|
41088ed7d0 | ||
|
|
712f569f17 | ||
|
|
7736f5e606 | ||
|
|
d45551dac9 |
@@ -35,7 +35,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
-
|
||||
name: Create matrix
|
||||
id: platforms
|
||||
@@ -143,7 +143,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
-
|
||||
name: Create matrix
|
||||
id: platforms
|
||||
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 2
|
||||
# CodeQL 2.16.4's auto-build added support for multi-module repositories,
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
name: Update Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.25.4"
|
||||
go-version: "1.25.5"
|
||||
-
|
||||
name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
|
||||
@@ -37,14 +37,14 @@ jobs:
|
||||
- alpine
|
||||
- debian
|
||||
engine-version:
|
||||
- 29-rc # latest rc
|
||||
- 28 # latest
|
||||
- 27 # latest - 1
|
||||
- rc # latest rc
|
||||
- 29 # latest
|
||||
- 28 # latest - 1
|
||||
- 25 # mirantis lts
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
-
|
||||
name: Update daemon.json
|
||||
run: |
|
||||
|
||||
@@ -60,14 +60,14 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
path: ${{ env.GOPATH }}/src/github.com/docker/cli
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.25.4"
|
||||
go-version: "1.25.5"
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
-
|
||||
name: Generate
|
||||
shell: 'script --return --quiet --command "bash {0}"'
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
-
|
||||
name: Run
|
||||
shell: 'script --return --quiet --command "bash {0}"'
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ run:
|
||||
# which causes it to fallback to go1.17 semantics.
|
||||
#
|
||||
# TODO(thaJeztah): update "usetesting" settings to enable go1.24 features once our minimum version is go1.24
|
||||
go: "1.25.4"
|
||||
go: "1.25.5"
|
||||
|
||||
timeout: 5m
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ ARG BASE_VARIANT=alpine
|
||||
ARG ALPINE_VERSION=3.22
|
||||
ARG BASE_DEBIAN_DISTRO=bookworm
|
||||
|
||||
ARG GO_VERSION=1.25.4
|
||||
ARG GO_VERSION=1.25.5
|
||||
|
||||
# XX_VERSION specifies the version of the xx utility to use.
|
||||
# It must be a valid tag in the docker.io/tonistiigi/xx image repository.
|
||||
|
||||
@@ -8,23 +8,23 @@ import (
|
||||
|
||||
type fakeClient struct {
|
||||
client.Client
|
||||
checkpointCreateFunc func(container string, options client.CheckpointCreateOptions) error
|
||||
checkpointDeleteFunc func(container string, options client.CheckpointDeleteOptions) error
|
||||
checkpointCreateFunc func(container string, options client.CheckpointCreateOptions) (client.CheckpointCreateResult, error)
|
||||
checkpointDeleteFunc func(container string, options client.CheckpointRemoveOptions) (client.CheckpointRemoveResult, error)
|
||||
checkpointListFunc func(container string, options client.CheckpointListOptions) (client.CheckpointListResult, error)
|
||||
}
|
||||
|
||||
func (cli *fakeClient) CheckpointCreate(_ context.Context, container string, options client.CheckpointCreateOptions) error {
|
||||
func (cli *fakeClient) CheckpointCreate(_ context.Context, container string, options client.CheckpointCreateOptions) (client.CheckpointCreateResult, error) {
|
||||
if cli.checkpointCreateFunc != nil {
|
||||
return cli.checkpointCreateFunc(container, options)
|
||||
}
|
||||
return nil
|
||||
return client.CheckpointCreateResult{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) CheckpointDelete(_ context.Context, container string, options client.CheckpointDeleteOptions) error {
|
||||
func (cli *fakeClient) CheckpointRemove(_ context.Context, container string, options client.CheckpointRemoveOptions) (client.CheckpointRemoveResult, error) {
|
||||
if cli.checkpointDeleteFunc != nil {
|
||||
return cli.checkpointDeleteFunc(container, options)
|
||||
}
|
||||
return nil
|
||||
return client.CheckpointRemoveResult{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) CheckpointList(_ context.Context, container string, options client.CheckpointListOptions) (client.CheckpointListResult, error) {
|
||||
|
||||
@@ -41,7 +41,7 @@ func newCreateCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
func runCreate(ctx context.Context, dockerCLI command.Cli, opts createOptions) error {
|
||||
err := dockerCLI.Client().CheckpointCreate(ctx, opts.container, client.CheckpointCreateOptions{
|
||||
_, err := dockerCLI.Client().CheckpointCreate(ctx, opts.container, client.CheckpointCreateOptions{
|
||||
CheckpointID: opts.checkpoint,
|
||||
CheckpointDir: opts.checkpointDir,
|
||||
Exit: !opts.leaveRunning,
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
func TestCheckpointCreateErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
checkpointCreateFunc func(container string, options client.CheckpointCreateOptions) error
|
||||
checkpointCreateFunc func(container string, options client.CheckpointCreateOptions) (client.CheckpointCreateResult, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
@@ -29,8 +29,8 @@ func TestCheckpointCreateErrors(t *testing.T) {
|
||||
},
|
||||
{
|
||||
args: []string{"foo", "bar"},
|
||||
checkpointCreateFunc: func(container string, options client.CheckpointCreateOptions) error {
|
||||
return errors.New("error creating checkpoint for container foo")
|
||||
checkpointCreateFunc: func(container string, options client.CheckpointCreateOptions) (client.CheckpointCreateResult, error) {
|
||||
return client.CheckpointCreateResult{}, errors.New("error creating checkpoint for container foo")
|
||||
},
|
||||
expectedError: "error creating checkpoint for container foo",
|
||||
},
|
||||
@@ -61,10 +61,10 @@ func TestCheckpointCreateWithOptions(t *testing.T) {
|
||||
var actualContainerName string
|
||||
var actualOptions client.CheckpointCreateOptions
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
checkpointCreateFunc: func(container string, options client.CheckpointCreateOptions) error {
|
||||
checkpointCreateFunc: func(container string, options client.CheckpointCreateOptions) (client.CheckpointCreateResult, error) {
|
||||
actualContainerName = container
|
||||
actualOptions = options
|
||||
return nil
|
||||
return client.CheckpointCreateResult{}, nil
|
||||
},
|
||||
})
|
||||
cmd := newCreateCommand(cli)
|
||||
|
||||
@@ -48,5 +48,5 @@ func runList(ctx context.Context, dockerCLI command.Cli, container string, opts
|
||||
Output: dockerCLI.Out(),
|
||||
Format: newFormat(formatter.TableFormatKey),
|
||||
}
|
||||
return formatWrite(cpCtx, checkpoints.Checkpoints)
|
||||
return formatWrite(cpCtx, checkpoints.Items)
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ func TestCheckpointListWithOptions(t *testing.T) {
|
||||
containerID = container
|
||||
checkpointDir = options.CheckpointDir
|
||||
return client.CheckpointListResult{
|
||||
Checkpoints: []checkpoint.Summary{
|
||||
Items: []checkpoint.Summary{
|
||||
{Name: "checkpoint-foo"},
|
||||
},
|
||||
}, nil
|
||||
@@ -63,7 +63,7 @@ func TestCheckpointListWithOptions(t *testing.T) {
|
||||
})
|
||||
cmd := newListCommand(cli)
|
||||
cmd.SetArgs([]string{"container-foo"})
|
||||
cmd.Flags().Set("checkpoint-dir", "/dir/foo")
|
||||
assert.Check(t, cmd.Flags().Set("checkpoint-dir", "/dir/foo"))
|
||||
assert.NilError(t, cmd.Execute())
|
||||
assert.Check(t, is.Equal("container-foo", containerID))
|
||||
assert.Check(t, is.Equal("/dir/foo", checkpointDir))
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package checkpoint
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/moby/moby/client"
|
||||
@@ -22,7 +20,12 @@ func newRemoveCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
Short: "Remove a checkpoint",
|
||||
Args: cli.ExactArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runRemove(cmd.Context(), dockerCLI, args[0], args[1], opts)
|
||||
containerID, checkpointID := args[0], args[1]
|
||||
_, err := dockerCLI.Client().CheckpointRemove(cmd.Context(), containerID, client.CheckpointRemoveOptions{
|
||||
CheckpointID: checkpointID,
|
||||
CheckpointDir: opts.checkpointDir,
|
||||
})
|
||||
return err
|
||||
},
|
||||
DisableFlagsInUseLine: true,
|
||||
}
|
||||
@@ -32,10 +35,3 @@ func newRemoveCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRemove(ctx context.Context, dockerCli command.Cli, container string, checkpointID string, opts removeOptions) error {
|
||||
return dockerCli.Client().CheckpointDelete(ctx, container, client.CheckpointDeleteOptions{
|
||||
CheckpointID: checkpointID,
|
||||
CheckpointDir: opts.checkpointDir,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
func TestCheckpointRemoveErrors(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
checkpointDeleteFunc func(container string, options client.CheckpointDeleteOptions) error
|
||||
checkpointDeleteFunc func(container string, options client.CheckpointRemoveOptions) (client.CheckpointRemoveResult, error)
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
@@ -27,8 +27,8 @@ func TestCheckpointRemoveErrors(t *testing.T) {
|
||||
},
|
||||
{
|
||||
args: []string{"foo", "bar"},
|
||||
checkpointDeleteFunc: func(container string, options client.CheckpointDeleteOptions) error {
|
||||
return errors.New("error deleting checkpoint")
|
||||
checkpointDeleteFunc: func(container string, options client.CheckpointRemoveOptions) (client.CheckpointRemoveResult, error) {
|
||||
return client.CheckpointRemoveResult{}, errors.New("error deleting checkpoint")
|
||||
},
|
||||
expectedError: "error deleting checkpoint",
|
||||
},
|
||||
@@ -49,16 +49,16 @@ func TestCheckpointRemoveErrors(t *testing.T) {
|
||||
func TestCheckpointRemoveWithOptions(t *testing.T) {
|
||||
var containerID, checkpointID, checkpointDir string
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
checkpointDeleteFunc: func(container string, options client.CheckpointDeleteOptions) error {
|
||||
checkpointDeleteFunc: func(container string, options client.CheckpointRemoveOptions) (client.CheckpointRemoveResult, error) {
|
||||
containerID = container
|
||||
checkpointID = options.CheckpointID
|
||||
checkpointDir = options.CheckpointDir
|
||||
return nil
|
||||
return client.CheckpointRemoveResult{}, nil
|
||||
},
|
||||
})
|
||||
cmd := newRemoveCommand(cli)
|
||||
cmd.SetArgs([]string{"container-foo", "checkpoint-bar"})
|
||||
cmd.Flags().Set("checkpoint-dir", "/dir/foo")
|
||||
assert.Check(t, cmd.Flags().Set("checkpoint-dir", "/dir/foo"))
|
||||
assert.NilError(t, cmd.Execute())
|
||||
assert.Check(t, is.Equal("container-foo", containerID))
|
||||
assert.Check(t, is.Equal("checkpoint-bar", checkpointID))
|
||||
|
||||
@@ -120,8 +120,8 @@ func TestNewAPIClientFromFlagsWithCustomHeadersFromEnv(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewAPIClientFromFlagsWithAPIVersionFromEnv(t *testing.T) {
|
||||
const customVersion = "v3.3.3"
|
||||
const expectedVersion = "3.3.3"
|
||||
const customVersion = "v3.3"
|
||||
const expectedVersion = "3.3"
|
||||
t.Setenv("DOCKER_API_VERSION", customVersion)
|
||||
t.Setenv("DOCKER_HOST", ":2375")
|
||||
|
||||
|
||||
@@ -247,7 +247,7 @@ func (c *ContainerContext) Ports() string {
|
||||
// State returns the container's current state (e.g. "running" or "paused").
|
||||
// Refer to [container.ContainerState] for possible states.
|
||||
func (c *ContainerContext) State() string {
|
||||
return c.c.State
|
||||
return string(c.c.State)
|
||||
}
|
||||
|
||||
// Status returns the container's status in a human readable form (for example,
|
||||
|
||||
@@ -152,7 +152,7 @@ func TestContainerPsContext(t *testing.T) {
|
||||
{
|
||||
container: container.Summary{State: container.StateRunning},
|
||||
trunc: true,
|
||||
expValue: container.StateRunning,
|
||||
expValue: string(container.StateRunning),
|
||||
call: ctx.State,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -114,6 +114,16 @@ func runImages(ctx context.Context, dockerCLI command.Cli, options imagesOptions
|
||||
}
|
||||
}
|
||||
|
||||
format := options.format
|
||||
if len(format) == 0 {
|
||||
if len(dockerCLI.ConfigFile().ImagesFormat) > 0 && !options.quiet && !options.tree {
|
||||
format = dockerCLI.ConfigFile().ImagesFormat
|
||||
useTree = false
|
||||
} else {
|
||||
format = formatter.TableFormatKey
|
||||
}
|
||||
}
|
||||
|
||||
if useTree {
|
||||
return runTree(ctx, dockerCLI, treeOptions{
|
||||
images: images,
|
||||
@@ -123,15 +133,6 @@ func runImages(ctx context.Context, dockerCLI command.Cli, options imagesOptions
|
||||
})
|
||||
}
|
||||
|
||||
format := options.format
|
||||
if len(format) == 0 {
|
||||
if len(dockerCLI.ConfigFile().ImagesFormat) > 0 && !options.quiet {
|
||||
format = dockerCLI.ConfigFile().ImagesFormat
|
||||
} else {
|
||||
format = formatter.TableFormatKey
|
||||
}
|
||||
}
|
||||
|
||||
imageCtx := formatter.ImageContext{
|
||||
Context: formatter.Context{
|
||||
Output: dockerCLI.Out(),
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
Info -> U In Use
|
||||
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Info -> U In Use
|
||||
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
Info -> U In Use
|
||||
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
Info -> U In Use
|
||||
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
|
||||
|
||||
+87
-44
@@ -111,6 +111,13 @@ func runTree(ctx context.Context, dockerCLI command.Cli, opts treeOptions) (int,
|
||||
continue
|
||||
}
|
||||
|
||||
if opts.all && len(sortedTags) == 0 {
|
||||
view.images = append(view.images, topImage{
|
||||
Details: topDetails,
|
||||
Children: children,
|
||||
created: img.Created,
|
||||
})
|
||||
}
|
||||
for _, tag := range sortedTags {
|
||||
view.images = append(view.images, topImage{
|
||||
Names: []string{tag},
|
||||
@@ -230,11 +237,11 @@ func printImageTree(outs command.Streams, view treeView) {
|
||||
}
|
||||
|
||||
out := tui.NewOutput(outs.Out())
|
||||
isTerm := out.IsTerminal()
|
||||
|
||||
_, width := out.GetTtySize()
|
||||
if width == 0 {
|
||||
width = 80
|
||||
}
|
||||
if width < 20 {
|
||||
limitWidth := width == 0
|
||||
if isTerm && width < 20 {
|
||||
width = 20
|
||||
}
|
||||
|
||||
@@ -243,16 +250,18 @@ func printImageTree(outs command.Streams, view treeView) {
|
||||
untaggedColor := out.Color(tui.ColorTertiary)
|
||||
titleColor := out.Color(tui.ColorTitle)
|
||||
|
||||
isTerm := out.IsTerminal()
|
||||
|
||||
out.Println(generateLegend(out, width))
|
||||
// Legend is right-aligned, so don't print it if the width is unlimited
|
||||
if !limitWidth {
|
||||
out.Println(generateLegend(out, width))
|
||||
}
|
||||
|
||||
possibleChips := getPossibleChips(view)
|
||||
columns := []imgColumn{
|
||||
{
|
||||
Title: "Image",
|
||||
Align: alignLeft,
|
||||
Width: 0,
|
||||
Title: "Image",
|
||||
Align: alignLeft,
|
||||
Width: 0,
|
||||
NoEllipsis: true,
|
||||
},
|
||||
{
|
||||
Title: "ID",
|
||||
@@ -284,7 +293,7 @@ func printImageTree(outs command.Streams, view treeView) {
|
||||
Width: func() int {
|
||||
maxChipsWidth := 0
|
||||
for _, chip := range possibleChips {
|
||||
s := chip.String(isTerm)
|
||||
s := out.Sprint(chip)
|
||||
l := tui.Width(s)
|
||||
maxChipsWidth += l
|
||||
}
|
||||
@@ -300,9 +309,9 @@ func printImageTree(outs command.Streams, view treeView) {
|
||||
var b strings.Builder
|
||||
for _, chip := range possibleChips {
|
||||
if chip.check(d) {
|
||||
b.WriteString(chip.String(isTerm))
|
||||
b.WriteString(out.Sprint(chip))
|
||||
} else {
|
||||
b.WriteString(chipPlaceholder.String(isTerm))
|
||||
b.WriteString(out.Sprint(chipPlaceholder))
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
@@ -340,25 +349,27 @@ func printImageTree(outs command.Streams, view treeView) {
|
||||
// to display their content.
|
||||
func adjustColumns(width uint, columns []imgColumn, images []topImage) []imgColumn {
|
||||
nameWidth := int(width)
|
||||
for idx, h := range columns {
|
||||
if h.Width == 0 {
|
||||
continue
|
||||
if nameWidth > 0 {
|
||||
for idx, h := range columns {
|
||||
if h.Width == 0 {
|
||||
continue
|
||||
}
|
||||
d := h.Width
|
||||
if idx > 0 {
|
||||
d += columnSpacing
|
||||
}
|
||||
// If the first column gets too short, remove remaining columns
|
||||
if nameWidth-d < 12 {
|
||||
columns = columns[:idx]
|
||||
break
|
||||
}
|
||||
nameWidth -= d
|
||||
}
|
||||
d := h.Width
|
||||
if idx > 0 {
|
||||
d += columnSpacing
|
||||
}
|
||||
// If the first column gets too short, remove remaining columns
|
||||
if nameWidth-d < 12 {
|
||||
columns = columns[:idx]
|
||||
break
|
||||
}
|
||||
nameWidth -= d
|
||||
}
|
||||
|
||||
// Try to make the first column as narrow as possible
|
||||
widest := widestFirstColumnValue(columns, images)
|
||||
if nameWidth > widest {
|
||||
if width == 0 || nameWidth > widest {
|
||||
nameWidth = widest
|
||||
}
|
||||
columns[0].Width = nameWidth
|
||||
@@ -426,13 +437,27 @@ func printNames(out tui.Output, headers []imgColumn, img topImage, color, untagg
|
||||
}
|
||||
|
||||
for nameIdx, name := range img.Names {
|
||||
// Don't limit first names to the column width because only the last
|
||||
// name will be printed alongside other columns.
|
||||
if nameIdx < len(img.Names)-1 {
|
||||
_, fullWidth := out.GetTtySize()
|
||||
_, _ = fmt.Fprintln(out, color.Apply(tui.Ellipsis(name, int(fullWidth))))
|
||||
} else {
|
||||
_, _ = fmt.Fprint(out, headers[0].Print(color, name))
|
||||
nameWidth := tui.Width(name)
|
||||
lastName := nameIdx == len(img.Names)-1
|
||||
multiLine := nameWidth > headers[0].Width
|
||||
|
||||
_, _ = fmt.Fprint(out, headers[0].Print(color, name))
|
||||
|
||||
// Print each name on its own line, including the last,
|
||||
// unless the last name fits into the column.
|
||||
//
|
||||
// IMAGE ID ...
|
||||
// anImage 171e65262c80 ...
|
||||
// firstName
|
||||
// lastNameIsALongOne
|
||||
// eade5be814e8 ...
|
||||
// anotherLongName
|
||||
// bb747ca923a5 ...
|
||||
if !lastName || multiLine {
|
||||
_, _ = fmt.Fprintln(out)
|
||||
}
|
||||
if multiLine && lastName {
|
||||
_, _ = fmt.Fprint(out, strings.Repeat(" ", headers[0].Width))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -452,6 +477,7 @@ type imgColumn struct {
|
||||
|
||||
DetailsValue func(*imageDetails) string
|
||||
Color *aec.ANSI
|
||||
NoEllipsis bool
|
||||
}
|
||||
|
||||
func (h imgColumn) Print(clr aec.ANSI, s string) string {
|
||||
@@ -468,12 +494,16 @@ func (h imgColumn) Print(clr aec.ANSI, s string) string {
|
||||
func (h imgColumn) PrintC(clr aec.ANSI, s string) string {
|
||||
ln := tui.Width(s)
|
||||
|
||||
if ln > h.Width {
|
||||
return clr.Apply(tui.Ellipsis(s, h.Width))
|
||||
}
|
||||
|
||||
fill := h.Width - ln
|
||||
|
||||
if fill < 0 {
|
||||
if h.NoEllipsis {
|
||||
fill = 0
|
||||
} else {
|
||||
return clr.Apply(tui.Ellipsis(s, h.Width))
|
||||
}
|
||||
}
|
||||
|
||||
l := fill / 2
|
||||
r := fill - l
|
||||
|
||||
@@ -482,20 +512,33 @@ func (h imgColumn) PrintC(clr aec.ANSI, s string) string {
|
||||
|
||||
func (h imgColumn) PrintL(clr aec.ANSI, s string) string {
|
||||
ln := tui.Width(s)
|
||||
if ln > h.Width {
|
||||
return clr.Apply(tui.Ellipsis(s, h.Width))
|
||||
|
||||
fill := h.Width - ln
|
||||
|
||||
if fill < 0 {
|
||||
if h.NoEllipsis {
|
||||
fill = 0
|
||||
} else {
|
||||
return clr.Apply(tui.Ellipsis(s, h.Width))
|
||||
}
|
||||
}
|
||||
|
||||
return clr.Apply(s) + strings.Repeat(" ", h.Width-ln)
|
||||
return clr.Apply(s) + strings.Repeat(" ", fill)
|
||||
}
|
||||
|
||||
func (h imgColumn) PrintR(clr aec.ANSI, s string) string {
|
||||
ln := tui.Width(s)
|
||||
if ln > h.Width {
|
||||
return clr.Apply(tui.Ellipsis(s, h.Width))
|
||||
fill := h.Width - ln
|
||||
|
||||
if fill < 0 {
|
||||
if h.NoEllipsis {
|
||||
fill = 0
|
||||
} else {
|
||||
return clr.Apply(tui.Ellipsis(s, h.Width))
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Repeat(" ", h.Width-ln) + clr.Apply(s)
|
||||
return strings.Repeat(" ", fill) + clr.Apply(s)
|
||||
}
|
||||
|
||||
// widestFirstColumnValue calculates the width needed to fully display the image names and platforms.
|
||||
|
||||
@@ -15,6 +15,7 @@ func TestPrintImageTreeAnsiTty(t *testing.T) {
|
||||
stdoutTty bool
|
||||
stderrTty bool
|
||||
expectedAnsi bool
|
||||
noColorEnv bool
|
||||
}{
|
||||
{
|
||||
name: "non-terminal",
|
||||
@@ -80,6 +81,24 @@ func TestPrintImageTreeAnsiTty(t *testing.T) {
|
||||
|
||||
expectedAnsi: false,
|
||||
},
|
||||
{
|
||||
name: "no-color-env",
|
||||
stdinTty: false,
|
||||
stdoutTty: false,
|
||||
stderrTty: false,
|
||||
|
||||
noColorEnv: true,
|
||||
expectedAnsi: false,
|
||||
},
|
||||
{
|
||||
name: "no-color-env-terminal",
|
||||
stdinTty: true,
|
||||
stdoutTty: true,
|
||||
stderrTty: true,
|
||||
|
||||
noColorEnv: true,
|
||||
expectedAnsi: false,
|
||||
},
|
||||
}
|
||||
|
||||
mockView := treeView{
|
||||
@@ -115,6 +134,11 @@ func TestPrintImageTreeAnsiTty(t *testing.T) {
|
||||
cli.In().SetIsTerminal(tc.stdinTty)
|
||||
cli.Out().SetIsTerminal(tc.stdoutTty)
|
||||
cli.Err().SetIsTerminal(tc.stderrTty)
|
||||
if tc.noColorEnv {
|
||||
t.Setenv("NO_COLOR", "1")
|
||||
} else {
|
||||
t.Setenv("NO_COLOR", "")
|
||||
}
|
||||
|
||||
printImageTree(cli, mockView)
|
||||
|
||||
@@ -123,9 +147,9 @@ func TestPrintImageTreeAnsiTty(t *testing.T) {
|
||||
|
||||
hasAnsi := strings.Contains(out, "\x1b[")
|
||||
if tc.expectedAnsi {
|
||||
assert.Check(t, hasAnsi, "Output should contain ANSI escape codes")
|
||||
assert.Check(t, hasAnsi, "Output should contain ANSI escape codes, output: %s", out)
|
||||
} else {
|
||||
assert.Check(t, !hasAnsi, "Output should not contain ANSI escape codes")
|
||||
assert.Check(t, !hasAnsi, "Output should not contain ANSI escape codes, output: %s", out)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ func formatWrite(fmtCtx formatter.Context, plugins client.PluginListResult) erro
|
||||
for _, p := range plugins.Items {
|
||||
if err := format(&pluginContext{
|
||||
trunc: fmtCtx.Trunc,
|
||||
p: *p,
|
||||
p: p,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ foobar_bar
|
||||
}
|
||||
|
||||
plugins := client.PluginListResult{
|
||||
Items: []*plugin.Plugin{
|
||||
Items: []plugin.Plugin{
|
||||
{ID: "pluginID1", Name: "foobar_baz", Config: plugin.Config{Description: "description 1"}, Enabled: true},
|
||||
{ID: "pluginID2", Name: "foobar_bar", Config: plugin.Config{Description: "description 2"}, Enabled: false},
|
||||
},
|
||||
@@ -171,7 +171,7 @@ foobar_bar
|
||||
|
||||
func TestPluginContextWriteJSON(t *testing.T) {
|
||||
plugins := client.PluginListResult{
|
||||
Items: []*plugin.Plugin{
|
||||
Items: []plugin.Plugin{
|
||||
{ID: "pluginID1", Name: "foobar_baz"},
|
||||
{ID: "pluginID2", Name: "foobar_bar"},
|
||||
},
|
||||
@@ -197,7 +197,7 @@ func TestPluginContextWriteJSON(t *testing.T) {
|
||||
|
||||
func TestPluginContextWriteJSONField(t *testing.T) {
|
||||
plugins := client.PluginListResult{
|
||||
Items: []*plugin.Plugin{
|
||||
Items: []plugin.Plugin{
|
||||
{ID: "pluginID1", Name: "foobar_baz"},
|
||||
{ID: "pluginID2", Name: "foobar_bar"},
|
||||
},
|
||||
|
||||
@@ -119,7 +119,7 @@ func TestList(t *testing.T) {
|
||||
golden: "plugin-list-with-no-trunc-option.golden",
|
||||
listFunc: func(opts client.PluginListOptions) (client.PluginListResult, error) {
|
||||
return client.PluginListResult{
|
||||
Items: []*plugin.Plugin{{
|
||||
Items: []plugin.Plugin{{
|
||||
ID: "xyg4z2hiSLO5yTnBJfg4OYia9gKA6Qjd",
|
||||
Name: "name-foo",
|
||||
Enabled: true,
|
||||
@@ -148,7 +148,7 @@ func TestList(t *testing.T) {
|
||||
golden: "plugin-list-sort.golden",
|
||||
listFunc: func(client.PluginListOptions) (client.PluginListResult, error) {
|
||||
return client.PluginListResult{
|
||||
Items: []*plugin.Plugin{
|
||||
Items: []plugin.Plugin{
|
||||
{
|
||||
ID: "id-1",
|
||||
Name: "plugin-1-foo",
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"Client":{"Version":"18.99.5-ce","ApiVersion":"1.38","DefaultAPIVersion":"1.38","GitCommit":"deadbeef","GoVersion":"go1.10.2","Os":"linux","Arch":"amd64","BuildTime":"Wed May 30 22:21:05 2018","Context":"my-context"},"Server":{"Platform":{"Name":"Docker Enterprise Edition (EE) 2.0"},"Version":"","APIVersion":"","MinAPIVersion":"","Os":"","Arch":"","Experimental":false,"Components":[{"Name":"Engine","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 9 23:38:38 2018","Experimental":"false","GitCommit":"64ddfa6","GoVersion":"go1.8.7","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"Universal Control Plane","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 2 21:24:07 UTC 2018","GitCommit":"4513922","GoVersion":"go1.9.4","MinApiVersion":"1.20","Os":"linux","Version":"3.0.3-tp2"}},{"Name":"Kubernetes","Version":"1.8+","Details":{"buildDate":"2018-04-26T16:51:21Z","compiler":"gc","gitCommit":"8d637aedf46b9c21dde723e29c645b9f27106fa5","gitTreeState":"clean","gitVersion":"v1.8.11-docker-8d637ae","goVersion":"go1.8.3","major":"1","minor":"8+","platform":"linux/amd64"}},{"Name":"Calico","Version":"v3.0.8","Details":{"cni":"v2.0.6","kube-controllers":"v2.0.5","node":"v3.0.8"}}]}}
|
||||
{"Client":{"Version":"18.99.5-ce","ApiVersion":"1.38","DefaultAPIVersion":"1.38","GitCommit":"deadbeef","GoVersion":"go1.10.2","Os":"linux","Arch":"amd64","BuildTime":"Wed May 30 22:21:05 2018","Context":"my-context"},"Server":{"Platform":{"Name":"Docker Enterprise Edition (EE) 2.0"},"Version":"18.99.5-ce","ApiVersion":"1.30","MinAPIVersion":"1.12","Os":"linux","Arch":"amd64","Components":[{"Name":"Engine","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 9 23:38:38 2018","Experimental":"false","GitCommit":"64ddfa6","GoVersion":"go1.8.7","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"Universal Control Plane","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 2 21:24:07 UTC 2018","GitCommit":"4513922","GoVersion":"go1.9.4","MinApiVersion":"1.20","Os":"linux","Version":"3.0.3-tp2"}},{"Name":"Kubernetes","Version":"1.8+","Details":{"buildDate":"2018-04-26T16:51:21Z","compiler":"gc","gitCommit":"8d637aedf46b9c21dde723e29c645b9f27106fa5","gitTreeState":"clean","gitVersion":"v1.8.11-docker-8d637ae","goVersion":"go1.8.3","major":"1","minor":"8+","platform":"linux/amd64"}},{"Name":"Calico","Version":"v3.0.8","Details":{"cni":"v2.0.6","kube-controllers":"v2.0.5","node":"v3.0.8"}}],"GitCommit":"64ddfa6","GoVersion":"go1.8.7","KernelVersion":"v1.0.0","BuildTime":"2018-07-09T22:38:38.000000000+00:00"}}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"Client":{"Version":"18.99.5-ce","ApiVersion":"1.38","DefaultAPIVersion":"1.38","GitCommit":"deadbeef","GoVersion":"go1.10.2","Os":"linux","Arch":"amd64","BuildTime":"Wed May 30 22:21:05 2018","Context":"my-context"},"Server":{"Platform":{"Name":"Docker Enterprise Edition (EE) 2.0"},"Version":"","APIVersion":"","MinAPIVersion":"","Os":"","Arch":"","Experimental":false,"Components":[{"Name":"Engine","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 9 23:38:38 2018","Experimental":"false","GitCommit":"64ddfa6","GoVersion":"go1.8.7","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"Universal Control Plane","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 2 21:24:07 UTC 2018","GitCommit":"4513922","GoVersion":"go1.9.4","MinApiVersion":"1.20","Os":"linux","Version":"3.0.3-tp2"}},{"Name":"Kubernetes","Version":"1.8+","Details":{"buildDate":"2018-04-26T16:51:21Z","compiler":"gc","gitCommit":"8d637aedf46b9c21dde723e29c645b9f27106fa5","gitTreeState":"clean","gitVersion":"v1.8.11-docker-8d637ae","goVersion":"go1.8.3","major":"1","minor":"8+","platform":"linux/amd64"}},{"Name":"Calico","Version":"v3.0.8","Details":{"cni":"v2.0.6","kube-controllers":"v2.0.5","node":"v3.0.8"}}]}}
|
||||
{"Client":{"Version":"18.99.5-ce","ApiVersion":"1.38","DefaultAPIVersion":"1.38","GitCommit":"deadbeef","GoVersion":"go1.10.2","Os":"linux","Arch":"amd64","BuildTime":"Wed May 30 22:21:05 2018","Context":"my-context"},"Server":{"Platform":{"Name":"Docker Enterprise Edition (EE) 2.0"},"Version":"18.99.5-ce","ApiVersion":"1.30","MinAPIVersion":"1.12","Os":"linux","Arch":"amd64","Components":[{"Name":"Engine","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 9 23:38:38 2018","Experimental":"false","GitCommit":"64ddfa6","GoVersion":"go1.8.7","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"Universal Control Plane","Version":"17.06.2-ee-15","Details":{"ApiVersion":"1.30","Arch":"amd64","BuildTime":"Mon Jul 2 21:24:07 UTC 2018","GitCommit":"4513922","GoVersion":"go1.9.4","MinApiVersion":"1.20","Os":"linux","Version":"3.0.3-tp2"}},{"Name":"Kubernetes","Version":"1.8+","Details":{"buildDate":"2018-04-26T16:51:21Z","compiler":"gc","gitCommit":"8d637aedf46b9c21dde723e29c645b9f27106fa5","gitTreeState":"clean","gitVersion":"v1.8.11-docker-8d637ae","goVersion":"go1.8.3","major":"1","minor":"8+","platform":"linux/amd64"}},{"Name":"Calico","Version":"v3.0.8","Details":{"cni":"v2.0.6","kube-controllers":"v2.0.5","node":"v3.0.8"}}],"GitCommit":"64ddfa6","GoVersion":"go1.8.7","KernelVersion":"v1.0.0","BuildTime":"2018-07-09T22:38:38.000000000+00:00"}}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
@@ -63,7 +64,7 @@ type versionOptions struct {
|
||||
// versionInfo contains version information of both the Client, and Server
|
||||
type versionInfo struct {
|
||||
Client clientVersion
|
||||
Server *client.ServerVersionResult
|
||||
Server *serverVersion
|
||||
}
|
||||
|
||||
type platformInfo struct {
|
||||
@@ -83,6 +84,26 @@ type clientVersion struct {
|
||||
Context string `json:"Context"`
|
||||
}
|
||||
|
||||
// serverVersion contains information about the Docker server host.
|
||||
// it's the client-side presentation of [client.ServerVersionResult].
|
||||
type serverVersion struct {
|
||||
Platform client.PlatformInfo `json:",omitempty"` // Platform is the platform (product name) the server is running on.
|
||||
Version string `json:"Version"` // Version is the version of the daemon.
|
||||
APIVersion string `json:"ApiVersion"` // APIVersion is the highest API version supported by the server.
|
||||
MinAPIVersion string `json:"MinAPIVersion,omitempty"` // MinAPIVersion is the minimum API version the server supports.
|
||||
Os string `json:"Os"` // Os is the operating system the server runs on.
|
||||
Arch string `json:"Arch"` // Arch is the hardware architecture the server runs on.
|
||||
Components []system.ComponentVersion `json:"Components,omitempty"` // Components contains version information for the components making up the server.
|
||||
|
||||
// The following fields are deprecated, they relate to the Engine component and are kept for backwards compatibility
|
||||
|
||||
GitCommit string `json:"GitCommit,omitempty"`
|
||||
GoVersion string `json:"GoVersion,omitempty"`
|
||||
KernelVersion string `json:"KernelVersion,omitempty"`
|
||||
Experimental bool `json:"Experimental,omitempty"`
|
||||
BuildTime string `json:"BuildTime,omitempty"`
|
||||
}
|
||||
|
||||
// newClientVersion constructs a new clientVersion. If a dockerCLI is
|
||||
// passed as argument, additional information is included (API version),
|
||||
// which may invoke an API connection. Pass nil to omit the additional
|
||||
@@ -107,6 +128,49 @@ func newClientVersion(contextName string, dockerCli command.Cli) clientVersion {
|
||||
return v
|
||||
}
|
||||
|
||||
func newServerVersion(sv client.ServerVersionResult) *serverVersion {
|
||||
out := &serverVersion{
|
||||
Platform: sv.Platform,
|
||||
Version: sv.Version,
|
||||
APIVersion: sv.APIVersion,
|
||||
MinAPIVersion: sv.MinAPIVersion,
|
||||
Os: sv.Os,
|
||||
Arch: sv.Arch,
|
||||
Experimental: sv.Experimental, //nolint:staticcheck // ignore deprecated field.
|
||||
Components: make([]system.ComponentVersion, 0, len(sv.Components)),
|
||||
}
|
||||
foundEngine := false
|
||||
for _, component := range sv.Components {
|
||||
if component.Name == "Engine" {
|
||||
foundEngine = true
|
||||
buildTime, ok := component.Details["BuildTime"]
|
||||
if ok {
|
||||
component.Details["BuildTime"] = reformatDate(buildTime)
|
||||
}
|
||||
out.GitCommit = component.Details["GitCommit"]
|
||||
out.GoVersion = component.Details["GoVersion"]
|
||||
out.KernelVersion = component.Details["KernelVersion"]
|
||||
out.Experimental = func() bool { b, _ := strconv.ParseBool(component.Details["Experimental"]); return b }()
|
||||
out.BuildTime = buildTime
|
||||
}
|
||||
out.Components = append(out.Components, component)
|
||||
}
|
||||
|
||||
if !foundEngine {
|
||||
out.Components = append(out.Components, system.ComponentVersion{
|
||||
Name: "Engine",
|
||||
Version: sv.Version,
|
||||
Details: map[string]string{
|
||||
"ApiVersion": sv.APIVersion,
|
||||
"MinAPIVersion": sv.MinAPIVersion,
|
||||
"Os": sv.Os,
|
||||
"Arch": sv.Arch,
|
||||
},
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// newVersionCommand creates a new cobra.Command for `docker version`
|
||||
func newVersionCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
var opts versionOptions
|
||||
@@ -138,14 +202,14 @@ func reformatDate(buildTime string) string {
|
||||
}
|
||||
|
||||
func arch() string {
|
||||
arch := runtime.GOARCH
|
||||
out := runtime.GOARCH
|
||||
if rosetta.Enabled() {
|
||||
arch += " (rosetta)"
|
||||
out += " (rosetta)"
|
||||
}
|
||||
return arch
|
||||
return out
|
||||
}
|
||||
|
||||
func runVersion(ctx context.Context, dockerCli command.Cli, opts *versionOptions) error {
|
||||
func runVersion(ctx context.Context, dockerCLI command.Cli, opts *versionOptions) error {
|
||||
var err error
|
||||
tmpl, err := newVersionTemplate(opts.format)
|
||||
if err != nil {
|
||||
@@ -153,36 +217,13 @@ func runVersion(ctx context.Context, dockerCli command.Cli, opts *versionOptions
|
||||
}
|
||||
|
||||
vd := versionInfo{
|
||||
Client: newClientVersion(dockerCli.CurrentContext(), dockerCli),
|
||||
Client: newClientVersion(dockerCLI.CurrentContext(), dockerCLI),
|
||||
}
|
||||
sv, err := dockerCli.Client().ServerVersion(ctx, client.ServerVersionOptions{})
|
||||
sv, err := dockerCLI.Client().ServerVersion(ctx, client.ServerVersionOptions{})
|
||||
if err == nil {
|
||||
vd.Server = &sv
|
||||
foundEngine := false
|
||||
for _, component := range sv.Components {
|
||||
if component.Name == "Engine" {
|
||||
foundEngine = true
|
||||
buildTime, ok := component.Details["BuildTime"]
|
||||
if ok {
|
||||
component.Details["BuildTime"] = reformatDate(buildTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !foundEngine {
|
||||
vd.Server.Components = append(vd.Server.Components, system.ComponentVersion{
|
||||
Name: "Engine",
|
||||
Version: sv.Version,
|
||||
Details: map[string]string{
|
||||
"ApiVersion": sv.APIVersion,
|
||||
"MinAPIVersion": sv.MinAPIVersion,
|
||||
"Os": sv.Os,
|
||||
"Arch": sv.Arch,
|
||||
},
|
||||
})
|
||||
}
|
||||
vd.Server = newServerVersion(sv)
|
||||
}
|
||||
if err2 := prettyPrintVersion(dockerCli.Out(), vd, tmpl); err2 != nil && err == nil {
|
||||
if err2 := prettyPrintVersion(dockerCLI.Out(), vd, tmpl); err2 != nil && err == nil {
|
||||
err = err2
|
||||
}
|
||||
return err
|
||||
|
||||
@@ -47,8 +47,18 @@ func TestVersionFormat(t *testing.T) {
|
||||
BuildTime: "Wed May 30 22:21:05 2018",
|
||||
Context: "my-context",
|
||||
},
|
||||
Server: &client.ServerVersionResult{
|
||||
Platform: client.PlatformInfo{Name: "Docker Enterprise Edition (EE) 2.0"},
|
||||
Server: &serverVersion{
|
||||
Platform: client.PlatformInfo{Name: "Docker Enterprise Edition (EE) 2.0"},
|
||||
Version: "18.99.5-ce",
|
||||
APIVersion: "1.30",
|
||||
MinAPIVersion: "1.12",
|
||||
Os: "linux",
|
||||
Arch: "amd64",
|
||||
GitCommit: "64ddfa6",
|
||||
GoVersion: "go1.8.7",
|
||||
KernelVersion: "v1.0.0",
|
||||
Experimental: false,
|
||||
BuildTime: "2018-07-09T22:38:38.000000000+00:00",
|
||||
Components: []system.ComponentVersion{
|
||||
{
|
||||
Name: "Engine",
|
||||
|
||||
@@ -16,7 +16,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.26.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
|
||||
@@ -560,10 +560,6 @@ func convertResources(source composetypes.Resources) (*swarm.ResourceRequirement
|
||||
GenericResources: generic,
|
||||
}
|
||||
}
|
||||
// These fields are themselves pointers -- we can simply assign, no need to
|
||||
// nil-check them. Nil is nil.
|
||||
resources.SwapBytes = source.MemswapLimit
|
||||
resources.MemorySwappiness = source.MemSwappiness
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -81,9 +81,6 @@ func TestConvertExtraHosts(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConvertResourcesFull(t *testing.T) {
|
||||
// create some variables so we can get pointers
|
||||
memswap := int64(72090)
|
||||
swappiness := int64(27)
|
||||
source := composetypes.Resources{
|
||||
Limits: &composetypes.ResourceLimit{
|
||||
NanoCPUs: "0.003",
|
||||
@@ -93,8 +90,6 @@ func TestConvertResourcesFull(t *testing.T) {
|
||||
NanoCPUs: "0.002",
|
||||
MemoryBytes: composetypes.UnitBytes(200000000),
|
||||
},
|
||||
MemswapLimit: &memswap,
|
||||
MemSwappiness: &swappiness,
|
||||
}
|
||||
resources, err := convertResources(source)
|
||||
assert.NilError(t, err)
|
||||
@@ -108,8 +103,6 @@ func TestConvertResourcesFull(t *testing.T) {
|
||||
NanoCPUs: 2000000,
|
||||
MemoryBytes: 200000000,
|
||||
},
|
||||
SwapBytes: &memswap,
|
||||
MemorySwappiness: &swappiness,
|
||||
}
|
||||
assert.Check(t, is.DeepEqual(expected, resources))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: "3.14"
|
||||
version: "3.13"
|
||||
|
||||
services:
|
||||
foo:
|
||||
@@ -79,8 +79,6 @@ services:
|
||||
- discrete_resource_spec:
|
||||
kind: 'ssd'
|
||||
value: 1
|
||||
memswap_limit: 86000
|
||||
mem_swappiness: 27
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 5s
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
func fullExampleConfig(workingDir, homeDir string) *types.Config {
|
||||
return &types.Config{
|
||||
Version: "3.14",
|
||||
Version: "3.13",
|
||||
Services: services(workingDir, homeDir),
|
||||
Networks: networks(),
|
||||
Volumes: volumes(),
|
||||
@@ -108,8 +108,6 @@ func services(workingDir, homeDir string) []types.ServiceConfig {
|
||||
},
|
||||
},
|
||||
},
|
||||
MemswapLimit: int64Ptr(86000),
|
||||
MemSwappiness: int64Ptr(27),
|
||||
},
|
||||
RestartPolicy: &types.RestartPolicy{
|
||||
Condition: "on-failure",
|
||||
|
||||
@@ -184,7 +184,7 @@ func strPtr(val string) *string {
|
||||
}
|
||||
|
||||
var sampleConfig = types.Config{
|
||||
Version: "3.14",
|
||||
Version: "3.13",
|
||||
Services: []types.ServiceConfig{
|
||||
{
|
||||
Name: "foo",
|
||||
@@ -970,10 +970,6 @@ func uint32Ptr(value uint32) *uint32 {
|
||||
return &value
|
||||
}
|
||||
|
||||
func int64Ptr(value int64) *int64 {
|
||||
return &value
|
||||
}
|
||||
|
||||
func TestFullExample(t *testing.T) {
|
||||
skip.If(t, runtime.GOOS == "windows", "FIXME: substitutes platform-specific HOME-dirs and requires platform-specific golden files; see https://github.com/docker/cli/pull/4610")
|
||||
|
||||
|
||||
@@ -116,7 +116,11 @@ func toServicePortConfigsMap(s any) (map[any]any, error) {
|
||||
}
|
||||
m := map[any]any{}
|
||||
for _, p := range ports {
|
||||
m[p.Published] = p
|
||||
protocol := "tcp"
|
||||
if p.Protocol != "" {
|
||||
protocol = p.Protocol
|
||||
}
|
||||
m[fmt.Sprintf("%d%s", p.Published, protocol)] = p
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
@@ -848,6 +848,8 @@ func TestLoadMultipleConfigs(t *testing.T) {
|
||||
"ports": []any{
|
||||
"8080:80",
|
||||
"9090:90",
|
||||
"53:53/tcp",
|
||||
"53:53/udp",
|
||||
},
|
||||
"labels": []any{
|
||||
"foo=bar",
|
||||
@@ -925,6 +927,18 @@ func TestLoadMultipleConfigs(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Ports: []types.ServicePortConfig{
|
||||
{
|
||||
Mode: "ingress",
|
||||
Target: 53,
|
||||
Published: 53,
|
||||
Protocol: "tcp",
|
||||
},
|
||||
{
|
||||
Mode: "ingress",
|
||||
Target: 53,
|
||||
Published: 53,
|
||||
Protocol: "udp",
|
||||
},
|
||||
{
|
||||
Target: 81,
|
||||
Published: 8080,
|
||||
|
||||
+2
-4
@@ -181,9 +181,7 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"memswap_limit": 86000,
|
||||
"mem_swappiness": 27
|
||||
}
|
||||
},
|
||||
"restart_policy": {
|
||||
"condition": "on-failure",
|
||||
@@ -515,7 +513,7 @@
|
||||
"working_dir": "/code"
|
||||
}
|
||||
},
|
||||
"version": "3.14",
|
||||
"version": "3.13",
|
||||
"volumes": {
|
||||
"another-volume": {
|
||||
"name": "user_specified_name",
|
||||
|
||||
+1
-3
@@ -1,4 +1,4 @@
|
||||
version: "3.14"
|
||||
version: "3.13"
|
||||
services:
|
||||
foo:
|
||||
build:
|
||||
@@ -73,8 +73,6 @@ services:
|
||||
- discrete_resource_spec:
|
||||
kind: ssd
|
||||
value: 1
|
||||
memswap_limit: 86000
|
||||
mem_swappiness: 27
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 5s
|
||||
|
||||
@@ -1,686 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"id": "config_schema_v3.14.json",
|
||||
"type": "object",
|
||||
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"default": "3.14"
|
||||
},
|
||||
|
||||
"services": {
|
||||
"id": "#/properties/services",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9._-]+$": {
|
||||
"$ref": "#/definitions/service"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"networks": {
|
||||
"id": "#/properties/networks",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9._-]+$": {
|
||||
"$ref": "#/definitions/network"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"volumes": {
|
||||
"id": "#/properties/volumes",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9._-]+$": {
|
||||
"$ref": "#/definitions/volume"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"secrets": {
|
||||
"id": "#/properties/secrets",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9._-]+$": {
|
||||
"$ref": "#/definitions/secret"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"configs": {
|
||||
"id": "#/properties/configs",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9._-]+$": {
|
||||
"$ref": "#/definitions/config"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
|
||||
"patternProperties": {"^x-": {}},
|
||||
"additionalProperties": false,
|
||||
|
||||
"definitions": {
|
||||
|
||||
"service": {
|
||||
"id": "#/definitions/service",
|
||||
"type": "object",
|
||||
|
||||
"properties": {
|
||||
"deploy": {"$ref": "#/definitions/deployment"},
|
||||
"build": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"context": {"type": "string"},
|
||||
"dockerfile": {"type": "string"},
|
||||
"args": {"$ref": "#/definitions/list_or_dict"},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"cache_from": {"$ref": "#/definitions/list_of_strings"},
|
||||
"network": {"type": "string"},
|
||||
"target": {"type": "string"},
|
||||
"shm_size": {"type": ["integer", "string"]},
|
||||
"extra_hosts": {"$ref": "#/definitions/list_or_dict"}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"cgroupns_mode": {"type": "string"},
|
||||
"cgroup_parent": {"type": "string"},
|
||||
"command": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "array", "items": {"type": "string"}}
|
||||
]
|
||||
},
|
||||
"configs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {"type": "string"},
|
||||
"target": {"type": "string"},
|
||||
"uid": {"type": "string"},
|
||||
"gid": {"type": "string"},
|
||||
"mode": {"type": "number"}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"container_name": {"type": "string"},
|
||||
"credential_spec": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"config": {"type": "string"},
|
||||
"file": {"type": "string"},
|
||||
"registry": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"depends_on": {"$ref": "#/definitions/list_of_strings"},
|
||||
"devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"dns": {"$ref": "#/definitions/string_or_list"},
|
||||
"dns_search": {"$ref": "#/definitions/string_or_list"},
|
||||
"domainname": {"type": "string"},
|
||||
"entrypoint": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "array", "items": {"type": "string"}}
|
||||
]
|
||||
},
|
||||
"env_file": {"$ref": "#/definitions/string_or_list"},
|
||||
"environment": {"$ref": "#/definitions/list_or_dict"},
|
||||
|
||||
"expose": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": ["string", "number"],
|
||||
"format": "expose"
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
|
||||
"external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"extra_hosts": {"$ref": "#/definitions/list_or_dict"},
|
||||
"healthcheck": {"$ref": "#/definitions/healthcheck"},
|
||||
"hostname": {"type": "string"},
|
||||
"image": {"type": "string"},
|
||||
"init": {"type": "boolean"},
|
||||
"ipc": {"type": "string"},
|
||||
"isolation": {"type": "string"},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
|
||||
"logging": {
|
||||
"type": "object",
|
||||
|
||||
"properties": {
|
||||
"driver": {"type": "string"},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^.+$": {"type": ["string", "number", "null"]}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"mac_address": {"type": "string"},
|
||||
"network_mode": {"type": "string"},
|
||||
|
||||
"networks": {
|
||||
"oneOf": [
|
||||
{"$ref": "#/definitions/list_of_strings"},
|
||||
{
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z0-9._-]+$": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"aliases": {"$ref": "#/definitions/list_of_strings"},
|
||||
"driver_opts": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^.+$": { "type": ["string", "number"] }
|
||||
}
|
||||
},
|
||||
"ipv4_address": {"type": "string"},
|
||||
"ipv6_address": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{"type": "null"}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"pid": {"type": ["string", "null"]},
|
||||
|
||||
"ports": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{"type": "number", "format": "ports"},
|
||||
{"type": "string", "format": "ports"},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mode": {"type": "string"},
|
||||
"target": {"type": "integer"},
|
||||
"published": {"type": "integer"},
|
||||
"protocol": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
|
||||
"privileged": {"type": "boolean"},
|
||||
"read_only": {"type": "boolean"},
|
||||
"restart": {"type": "string"},
|
||||
"security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
|
||||
"shm_size": {"type": ["number", "string"]},
|
||||
"secrets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {"type": "string"},
|
||||
"target": {"type": "string"},
|
||||
"uid": {"type": "string"},
|
||||
"gid": {"type": "string"},
|
||||
"mode": {"type": "number"}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sysctls": {"$ref": "#/definitions/list_or_dict"},
|
||||
"stdin_open": {"type": "boolean"},
|
||||
"stop_grace_period": {"type": "string", "format": "duration"},
|
||||
"stop_signal": {"type": "string"},
|
||||
"tmpfs": {"$ref": "#/definitions/string_or_list"},
|
||||
"tty": {"type": "boolean"},
|
||||
"ulimits": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^[a-z]+$": {
|
||||
"oneOf": [
|
||||
{"type": "integer"},
|
||||
{
|
||||
"type":"object",
|
||||
"properties": {
|
||||
"hard": {"type": "integer"},
|
||||
"soft": {"type": "integer"}
|
||||
},
|
||||
"required": ["soft", "hard"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"oom_score_adj": {"type": "integer"},
|
||||
"user": {"type": "string"},
|
||||
"userns_mode": {"type": "string"},
|
||||
"volumes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["type"],
|
||||
"properties": {
|
||||
"type": {"type": "string"},
|
||||
"source": {"type": "string"},
|
||||
"target": {"type": "string"},
|
||||
"read_only": {"type": "boolean"},
|
||||
"consistency": {"type": "string"},
|
||||
"bind": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"propagation": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"volume": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nocopy": {"type": "boolean"}
|
||||
}
|
||||
},
|
||||
"tmpfs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
],
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"working_dir": {"type": "string"}
|
||||
},
|
||||
"patternProperties": {"^x-": {}},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"healthcheck": {
|
||||
"id": "#/definitions/healthcheck",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"disable": {"type": "boolean"},
|
||||
"interval": {"type": "string", "format": "duration"},
|
||||
"retries": {"type": "number"},
|
||||
"test": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"type": "array", "items": {"type": "string"}}
|
||||
]
|
||||
},
|
||||
"timeout": {"type": "string", "format": "duration"},
|
||||
"start_period": {"type": "string", "format": "duration"},
|
||||
"start_interval": {"type": "string", "format": "duration"}
|
||||
}
|
||||
},
|
||||
"deployment": {
|
||||
"id": "#/definitions/deployment",
|
||||
"type": ["object", "null"],
|
||||
"properties": {
|
||||
"mode": {"type": "string"},
|
||||
"endpoint_mode": {"type": "string"},
|
||||
"replicas": {"type": "integer"},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"rollback_config": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"parallelism": {"type": "integer"},
|
||||
"delay": {"type": "string", "format": "duration"},
|
||||
"failure_action": {"type": "string"},
|
||||
"monitor": {"type": "string", "format": "duration"},
|
||||
"max_failure_ratio": {"type": "number"},
|
||||
"order": {"type": "string", "enum": [
|
||||
"start-first", "stop-first"
|
||||
]}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"update_config": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"parallelism": {"type": "integer"},
|
||||
"delay": {"type": "string", "format": "duration"},
|
||||
"failure_action": {"type": "string"},
|
||||
"monitor": {"type": "string", "format": "duration"},
|
||||
"max_failure_ratio": {"type": "number"},
|
||||
"order": {"type": "string", "enum": [
|
||||
"start-first", "stop-first"
|
||||
]}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"resources": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limits": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cpus": {"type": "string"},
|
||||
"memory": {"type": "string"},
|
||||
"pids": {"type": "integer"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"reservations": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cpus": {"type": "string"},
|
||||
"memory": {"type": "string"},
|
||||
"generic_resources": {"$ref": "#/definitions/generic_resources"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"memswap_limit": {
|
||||
"type": "integer"
|
||||
},
|
||||
"mem_swappiness": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"restart_policy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"condition": {"type": "string"},
|
||||
"delay": {"type": "string", "format": "duration"},
|
||||
"max_attempts": {"type": "integer"},
|
||||
"window": {"type": "string", "format": "duration"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"placement": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"constraints": {"type": "array", "items": {"type": "string"}},
|
||||
"preferences": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"spread": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"max_replicas_per_node": {"type": "integer"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"generic_resources": {
|
||||
"id": "#/definitions/generic_resources",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"discrete_resource_spec": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {"type": "string"},
|
||||
"value": {"type": "number"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
|
||||
"network": {
|
||||
"id": "#/definitions/network",
|
||||
"type": ["object", "null"],
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"driver": {"type": "string"},
|
||||
"driver_opts": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^.+$": {"type": ["string", "number"]}
|
||||
}
|
||||
},
|
||||
"ipam": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"driver": {"type": "string"},
|
||||
"config": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"subnet": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"external": {
|
||||
"type": ["boolean", "object"],
|
||||
"properties": {
|
||||
"name": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"internal": {"type": "boolean"},
|
||||
"attachable": {"type": "boolean"},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"}
|
||||
},
|
||||
"patternProperties": {"^x-": {}},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"volume": {
|
||||
"id": "#/definitions/volume",
|
||||
"type": ["object", "null"],
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"driver": {"type": "string"},
|
||||
"driver_opts": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^.+$": {"type": ["string", "number"]}
|
||||
}
|
||||
},
|
||||
"external": {
|
||||
"type": ["boolean", "object"],
|
||||
"properties": {
|
||||
"name": {"type": "string"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"x-cluster-spec": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"group": {"type": "string"},
|
||||
"access_mode": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"scope": {"type": "string"},
|
||||
"sharing": {"type": "string"},
|
||||
"block_volume": {"type": "object"},
|
||||
"mount_volume": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fs_type": {"type": "string"},
|
||||
"mount_flags": {"type": "array", "items": {"type": "string"}}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"accessibility_requirements": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"requisite": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"segments": {"$ref": "#/definitions/list_or_dict"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"preferred": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"segments": {"$ref": "#/definitions/list_or_dict"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"capacity_range": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"required_bytes": {"type": "string"},
|
||||
"limit_bytes": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"availability": {"type": "string"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"patternProperties": {"^x-": {}},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"secret": {
|
||||
"id": "#/definitions/secret",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"file": {"type": "string"},
|
||||
"external": {
|
||||
"type": ["boolean", "object"],
|
||||
"properties": {
|
||||
"name": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"driver": {"type": "string"},
|
||||
"driver_opts": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^.+$": {"type": ["string", "number"]}
|
||||
}
|
||||
},
|
||||
"template_driver": {"type": "string"}
|
||||
},
|
||||
"patternProperties": {"^x-": {}},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"config": {
|
||||
"id": "#/definitions/config",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"file": {"type": "string"},
|
||||
"external": {
|
||||
"type": ["boolean", "object"],
|
||||
"properties": {
|
||||
"name": {"type": "string"}
|
||||
}
|
||||
},
|
||||
"labels": {"$ref": "#/definitions/list_or_dict"},
|
||||
"template_driver": {"type": "string"}
|
||||
},
|
||||
"patternProperties": {"^x-": {}},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
||||
"string_or_list": {
|
||||
"oneOf": [
|
||||
{"type": "string"},
|
||||
{"$ref": "#/definitions/list_of_strings"}
|
||||
]
|
||||
},
|
||||
|
||||
"list_of_strings": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"uniqueItems": true
|
||||
},
|
||||
|
||||
"list_or_dict": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
".+": {
|
||||
"type": ["string", "number", "null"]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{"type": "array", "items": {"type": "string"}, "uniqueItems": true}
|
||||
]
|
||||
},
|
||||
|
||||
"constraints": {
|
||||
"service": {
|
||||
"id": "#/definitions/constraints/service",
|
||||
"anyOf": [
|
||||
{"required": ["build"]},
|
||||
{"required": ["image"]}
|
||||
],
|
||||
"properties": {
|
||||
"build": {
|
||||
"required": ["context"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
defaultVersion = "3.14"
|
||||
defaultVersion = "3.13"
|
||||
versionField = "version"
|
||||
)
|
||||
|
||||
@@ -52,7 +52,7 @@ func init() {
|
||||
}
|
||||
|
||||
// Version returns the version of the config, defaulting to the latest "3.x"
|
||||
// version (3.14). If only the major version "3" is specified, it is used as
|
||||
// version (3.13). If only the major version "3" is specified, it is used as
|
||||
// version "3.x" and returns the default version (latest 3.x).
|
||||
func Version(config map[string]any) string {
|
||||
version, ok := config[versionField]
|
||||
|
||||
@@ -210,7 +210,6 @@ func TestValidateCredentialSpecs(t *testing.T) {
|
||||
{version: "3.11"},
|
||||
{version: "3.12"},
|
||||
{version: "3.13"},
|
||||
{version: "3.14"},
|
||||
{version: "3"},
|
||||
{version: ""},
|
||||
}
|
||||
|
||||
@@ -307,10 +307,8 @@ type UpdateConfig struct {
|
||||
|
||||
// Resources the resource limits and reservations
|
||||
type Resources struct {
|
||||
Limits *ResourceLimit `yaml:",omitempty" json:"limits,omitempty"`
|
||||
Reservations *Resource `yaml:",omitempty" json:"reservations,omitempty"`
|
||||
MemswapLimit *int64 `mapstructure:"memswap_limit" yaml:"memswap_limit,omitempty" json:"memswap_limit,omitempty"`
|
||||
MemSwappiness *int64 `mapstructure:"mem_swappiness" yaml:"mem_swappiness,omitempty" json:"mem_swappiness,omitempty"`
|
||||
Limits *ResourceLimit `yaml:",omitempty" json:"limits,omitempty"`
|
||||
Reservations *Resource `yaml:",omitempty" json:"reservations,omitempty"`
|
||||
}
|
||||
|
||||
// ResourceLimit is a resource to be limited
|
||||
|
||||
@@ -233,11 +233,9 @@ func (c *commandConn) Close() error {
|
||||
defer c.closing.Store(false)
|
||||
|
||||
if err := c.CloseRead(); err != nil {
|
||||
logrus.Warnf("commandConn.Close: CloseRead: %v", err)
|
||||
return err
|
||||
}
|
||||
if err := c.CloseWrite(); err != nil {
|
||||
logrus.Warnf("commandConn.Close: CloseWrite: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ func (ep *Endpoint) ClientOpts() ([]client.Opt, error) {
|
||||
}
|
||||
}
|
||||
|
||||
result = append(result, client.WithVersionFromEnv(), client.WithAPIVersionNegotiation())
|
||||
result = append(result, client.WithAPIVersionFromEnv())
|
||||
return result, nil
|
||||
}
|
||||
|
||||
|
||||
+25
-25
@@ -1,23 +1,23 @@
|
||||
module github.com/docker/cli/cmd/docker-trust
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/containerd/errdefs v1.0.0
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/docker/cli v29.0.0-rc.2+incompatible
|
||||
github.com/docker/cli v29.0.4+incompatible
|
||||
github.com/docker/cli-docs-tool v0.10.0
|
||||
github.com/docker/distribution v2.8.3+incompatible
|
||||
github.com/docker/go-connections v0.6.0
|
||||
github.com/fvbommel/sortorder v1.1.0
|
||||
github.com/moby/moby/api v1.52.0-beta.4
|
||||
github.com/moby/moby/client v0.1.0-beta.3
|
||||
github.com/moby/moby/api v1.52.0
|
||||
github.com/moby/moby/client v0.1.0
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a
|
||||
go.opentelemetry.io/otel v1.35.0
|
||||
go.opentelemetry.io/otel v1.38.0
|
||||
gotest.tools/v3 v3.5.2
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudflare/cfssl v1.6.4 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
@@ -36,12 +36,12 @@ require (
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.17 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
@@ -58,23 +58,23 @@ require (
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/term v0.32.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
|
||||
google.golang.org/grpc v1.72.2 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/term v0.37.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
google.golang.org/grpc v1.75.0 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
k8s.io/klog/v2 v2.90.1 // indirect
|
||||
)
|
||||
|
||||
+52
-50
@@ -21,8 +21,8 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembj
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
@@ -46,8 +46,8 @@ 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 v29.0.0-rc.2+incompatible h1:eT9VoGWPytEDUDQb/mF2sSOdkWShUwtAeV/RzenY/9w=
|
||||
github.com/docker/cli v29.0.0-rc.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v29.0.4+incompatible h1:mffN/hPqaI39vx/4QiSkdldHeM0rP1ZZBIXRUOPI5+I=
|
||||
github.com/docker/cli v29.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli-docs-tool v0.10.0 h1:bOD6mKynPQgojQi3s2jgcUWGp/Ebqy1SeCr9VfKQLLU=
|
||||
github.com/docker/cli-docs-tool v0.10.0/go.mod h1:5EM5zPnT2E7yCLERZmrDA234Vwn09fzRHP4aX1qwp1U=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
@@ -79,8 +79,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
@@ -108,8 +108,8 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
@@ -154,10 +154,10 @@ github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KH
|
||||
github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/moby/api v1.52.0-beta.4 h1:p05ztW787RcwcQx8YFcda1sb0ExqxDLHTjBQA3Jq6BY=
|
||||
github.com/moby/moby/api v1.52.0-beta.4/go.mod h1:v0K/motq8oWmx+rtApG1rBTIpQ8KUONUjpf+U73gags=
|
||||
github.com/moby/moby/client v0.1.0-beta.3 h1:y/ed2VpRmW8As56TFOaXF2zVSli38IS032zAAGt6kcI=
|
||||
github.com/moby/moby/client v0.1.0-beta.3/go.mod h1:dvvUX065yfMkFXvPk14AXVz8wmew2MdeXJ/rcYEPr7g=
|
||||
github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg=
|
||||
github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
|
||||
github.com/moby/moby/client v0.1.0 h1:nt+hn6O9cyJQqq5UWnFGqsZRTS/JirUqzPjEl0Bdc/8=
|
||||
github.com/moby/moby/client v0.1.0/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE=
|
||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
@@ -242,8 +242,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a h1:tlJ7tGUHvcvL1v3yR6NcCc9nOqh2L+CG6HWrYQtwzQ0=
|
||||
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a/go.mod h1:Y94A6rPp2OwNfP/7vmf8O2xx2IykP8pPXQ1DLouGnEw=
|
||||
github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b h1:FsyNrX12e5BkplJq7wKOLk0+C6LZ+KGXvuEcKUYm5ss=
|
||||
@@ -254,26 +254,26 @@ github.com/zmap/zlint/v3 v3.1.0 h1:WjVytZo79m/L1+/Mlphl09WBob6YTGljN5IGWZFpAv0=
|
||||
github.com/zmap/zlint/v3 v3.1.0/go.mod h1:L7t8s3sEKkb0A2BxGy1IWrxt1ZATa1R4QfJZaQOD3zU=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -281,14 +281,14 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -302,22 +302,24 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
|
||||
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
|
||||
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
|
||||
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
variable "GO_VERSION" {
|
||||
default = "1.25.4"
|
||||
default = "1.25.5"
|
||||
}
|
||||
variable "VERSION" {
|
||||
default = ""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.25.4
|
||||
ARG GO_VERSION=1.25.5
|
||||
|
||||
# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image.
|
||||
# It must be a supported tag in the docker.io/library/alpine image repository
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.25.4
|
||||
ARG GO_VERSION=1.25.5
|
||||
|
||||
# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image.
|
||||
# It must be a supported tag in the docker.io/library/alpine image repository
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.25.4
|
||||
ARG GO_VERSION=1.25.5
|
||||
|
||||
# ALPINE_VERSION sets the version of the alpine base image to use, including for the golang image.
|
||||
# It must be a supported tag in the docker.io/library/alpine image repository
|
||||
|
||||
@@ -130,6 +130,8 @@ line:
|
||||
| `DOCKER_TLS` | Enable TLS for connections made by the `docker` CLI (equivalent of the `--tls` command-line option). Set to a non-empty value to enable TLS. Note that TLS is enabled automatically if any of the other TLS options are set. |
|
||||
| `DOCKER_TLS_VERIFY` | When set Docker uses TLS and verifies the remote. This variable is used both by the `docker` CLI and the [`dockerd` daemon](https://docs.docker.com/reference/cli/dockerd/) |
|
||||
| `BUILDKIT_PROGRESS` | Set type of progress output (`auto`, `plain`, `tty`, `rawjson`) when [building](https://docs.docker.com/reference/cli/docker/image/build/) with [BuildKit backend](https://docs.docker.com/build/buildkit/). Use plain to show container output (default `auto`). |
|
||||
| `NO_COLOR` | Disable any ANSI escape codes in the output in accordance with https://no-color.org/
|
||||
|
|
||||
|
||||
Because Docker is developed using Go, you can also use any environment
|
||||
variables used by the Go runtime. In particular, you may find these useful:
|
||||
@@ -317,8 +319,8 @@ be set for each environment:
|
||||
|
||||
These settings are used to configure proxy settings for containers only, and not
|
||||
used as proxy settings for the `docker` CLI or the `dockerd` daemon. Refer to the
|
||||
[environment variables](#environment-variables) and [HTTP/HTTPS proxy](https://docs.docker.com/engine/daemon/proxy/#httphttps-proxy)
|
||||
sections for configuring proxy settings for the CLI and daemon.
|
||||
[environment variables](#environment-variables) section and the [Daemon proxy configuration](https://docs.docker.com/engine/daemon/proxy/)
|
||||
guide for configuring proxy settings for the CLI and daemon.
|
||||
|
||||
> [!WARNING]
|
||||
> Proxy settings may contain sensitive information (for example, if the proxy
|
||||
|
||||
@@ -1076,11 +1076,11 @@ The following is a full example of the allowed configuration options on Linux:
|
||||
"builder": {
|
||||
"gc": {
|
||||
"enabled": true,
|
||||
"defaultKeepStorage": "10GB",
|
||||
"defaultReservedSpace": "10GB",
|
||||
"policy": [
|
||||
{ "keepStorage": "10GB", "filter": ["unused-for=2200h"] },
|
||||
{ "keepStorage": "50GB", "filter": ["unused-for=3300h"] },
|
||||
{ "keepStorage": "100GB", "all": true }
|
||||
{ "maxUsedSpace": "512MB", "keepDuration": "48h", "filter": [ "type=source.local" ] },
|
||||
{ "reservedSpace": "10GB", "maxUsedSpace": "100GB", "keepDuration": "1440h" },
|
||||
{ "reservedSpace": "50GB", "minFreeSpace": "20GB", "maxUsedSpace": "200GB", "all": true }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -28,7 +28,7 @@ func withHeader(header Str) noteOptions {
|
||||
}
|
||||
|
||||
func (o Output) printNoteWithOptions(format string, args []any, opts ...noteOptions) {
|
||||
if o.isTerminal {
|
||||
if !o.noColor {
|
||||
// TODO: Handle all flags
|
||||
format = strings.ReplaceAll(format, "--platform", ColorFlag.Apply("--platform"))
|
||||
}
|
||||
@@ -51,7 +51,7 @@ func (o Output) printNoteWithOptions(format string, args []any, opts ...noteOpti
|
||||
}
|
||||
|
||||
l := line
|
||||
if o.isTerminal {
|
||||
if !o.noColor {
|
||||
l = aec.Italic.Apply(l)
|
||||
}
|
||||
_, _ = fmt.Fprintln(o, l)
|
||||
|
||||
+13
-8
@@ -5,6 +5,7 @@ package tui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/morikuni/aec"
|
||||
@@ -12,7 +13,7 @@ import (
|
||||
|
||||
type Output struct {
|
||||
*streams.Out
|
||||
isTerminal bool
|
||||
noColor bool
|
||||
}
|
||||
|
||||
type terminalPrintable interface {
|
||||
@@ -20,24 +21,28 @@ type terminalPrintable interface {
|
||||
}
|
||||
|
||||
func NewOutput(out *streams.Out) Output {
|
||||
noColor := !out.IsTerminal()
|
||||
if os.Getenv("NO_COLOR") != "" {
|
||||
noColor = true
|
||||
}
|
||||
return Output{
|
||||
Out: out,
|
||||
isTerminal: out.IsTerminal(),
|
||||
Out: out,
|
||||
noColor: noColor,
|
||||
}
|
||||
}
|
||||
|
||||
func (o Output) Color(clr aec.ANSI) aec.ANSI {
|
||||
if o.isTerminal {
|
||||
return clr
|
||||
if o.noColor {
|
||||
return ColorNone
|
||||
}
|
||||
return ColorNone
|
||||
return clr
|
||||
}
|
||||
|
||||
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))
|
||||
out = append(out, s.String(!o.noColor))
|
||||
} else {
|
||||
out = append(out, p)
|
||||
}
|
||||
@@ -47,7 +52,7 @@ func (o Output) Sprint(all ...any) string {
|
||||
|
||||
func (o Output) PrintlnWithColor(clr aec.ANSI, args ...any) {
|
||||
msg := o.Sprint(args...)
|
||||
if o.isTerminal {
|
||||
if !o.noColor {
|
||||
msg = clr.Apply(msg)
|
||||
}
|
||||
_, _ = fmt.Fprintln(o.Out, msg)
|
||||
|
||||
+20
-14
@@ -137,12 +137,14 @@ func TestPortOptValidSimpleSyntax(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
var port PortOpt
|
||||
assert.NilError(t, port.Set(tc.value))
|
||||
assert.Check(t, is.Len(port.Value(), len(tc.expected)))
|
||||
for _, expectedPortConfig := range tc.expected {
|
||||
assertContains(t, port.Value(), expectedPortConfig)
|
||||
}
|
||||
t.Run(tc.value, func(t *testing.T) {
|
||||
var port PortOpt
|
||||
assert.NilError(t, port.Set(tc.value))
|
||||
assert.Check(t, is.Len(port.Value(), len(tc.expected)))
|
||||
for _, expectedPortConfig := range tc.expected {
|
||||
assertContains(t, port.Value(), expectedPortConfig)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,12 +230,14 @@ func TestPortOptValidComplexSyntax(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
var port PortOpt
|
||||
assert.NilError(t, port.Set(tc.value))
|
||||
assert.Check(t, is.Len(port.Value(), len(tc.expected)))
|
||||
for _, expectedPortConfig := range tc.expected {
|
||||
assertContains(t, port.Value(), expectedPortConfig)
|
||||
}
|
||||
t.Run(tc.value, func(t *testing.T) {
|
||||
var port PortOpt
|
||||
assert.NilError(t, port.Set(tc.value))
|
||||
assert.Check(t, is.Len(port.Value(), len(tc.expected)))
|
||||
for _, expectedPortConfig := range tc.expected {
|
||||
assertContains(t, port.Value(), expectedPortConfig)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,8 +323,10 @@ func TestPortOptInvalidSimpleSyntax(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
var port PortOpt
|
||||
assert.Error(t, port.Set(tc.value), tc.expectedError)
|
||||
t.Run(tc.value, func(t *testing.T) {
|
||||
var port PortOpt
|
||||
assert.Error(t, port.Set(tc.value), tc.expectedError)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+24
-24
@@ -28,8 +28,8 @@ require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/mattn/go-runewidth v0.0.19
|
||||
github.com/moby/go-archive v0.1.0
|
||||
github.com/moby/moby/api v1.52.0-rc.1
|
||||
github.com/moby/moby/client v0.1.0-rc.1
|
||||
github.com/moby/moby/api v1.52.0
|
||||
github.com/moby/moby/client v0.2.1
|
||||
github.com/moby/patternmatcher v0.6.0
|
||||
github.com/moby/swarmkit/v2 v2.1.1
|
||||
github.com/moby/sys/atomicwriter v0.1.0
|
||||
@@ -47,18 +47,18 @@ require (
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0
|
||||
go.opentelemetry.io/otel v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0
|
||||
go.opentelemetry.io/otel/metric v1.35.0
|
||||
go.opentelemetry.io/otel/sdk v1.35.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0
|
||||
go.opentelemetry.io/otel/trace v1.35.0
|
||||
golang.org/x/sync v0.17.0
|
||||
golang.org/x/sys v0.37.0
|
||||
golang.org/x/term v0.36.0
|
||||
golang.org/x/text v0.30.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0
|
||||
go.opentelemetry.io/otel v1.38.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0
|
||||
go.opentelemetry.io/otel/metric v1.38.0
|
||||
go.opentelemetry.io/otel/sdk v1.38.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0
|
||||
go.opentelemetry.io/otel/trace v1.38.0
|
||||
golang.org/x/sync v0.18.0
|
||||
golang.org/x/sys v0.38.0
|
||||
golang.org/x/term v0.37.0
|
||||
golang.org/x/text v0.31.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gotest.tools/v3 v3.5.2
|
||||
tags.cncf.io/container-device-interface v1.0.1
|
||||
@@ -68,7 +68,7 @@ require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
@@ -76,11 +76,11 @@ require (
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
@@ -95,13 +95,13 @@ require (
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
go.etcd.io/etcd/raft/v3 v3.5.16 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
golang.org/x/net v0.46.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
|
||||
google.golang.org/grpc v1.72.2 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
google.golang.org/grpc v1.75.0 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
)
|
||||
|
||||
+54
-52
@@ -12,8 +12,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
|
||||
@@ -62,8 +62,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
@@ -87,8 +87,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
@@ -113,10 +113,10 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
|
||||
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
|
||||
github.com/moby/moby/api v1.52.0-rc.1 h1:yiNz/QzD4Jr1gyKl2iMo7OCZwwY+Xb3BltKv1xipwXo=
|
||||
github.com/moby/moby/api v1.52.0-rc.1/go.mod h1:v0K/motq8oWmx+rtApG1rBTIpQ8KUONUjpf+U73gags=
|
||||
github.com/moby/moby/client v0.1.0-rc.1 h1:NfuQec3HvQkPf4EvVkoFGPsBvlAc8CCyQN1m1kGSEX8=
|
||||
github.com/moby/moby/client v0.1.0-rc.1/go.mod h1:qYzoKHz8qu4Ie1j41CWYhfNRHo8uhs5ay7cfx309Aqc=
|
||||
github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg=
|
||||
github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
|
||||
github.com/moby/moby/client v0.2.1 h1:1Grh1552mvv6i+sYOdY+xKKVTvzJegcVMhuXocyDz/k=
|
||||
github.com/moby/moby/client v0.2.1/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/swarmkit/v2 v2.1.1 h1:yvTJ8MMCc3f0qTA44J6R59EZ5yZawdYopkpuLk4+ICU=
|
||||
@@ -173,8 +173,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
|
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
@@ -190,8 +190,8 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346 h1:TvtdmeYsYEij78hS4oxnwikoiLdIrgav3BA+CbhaDAI=
|
||||
github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346/go.mod h1:xKQhd7snlzKFuUi1taTGWjpRE8iFTA06DeacYi3CVFQ=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@@ -205,28 +205,28 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/etcd/raft/v3 v3.5.16 h1:zBXA3ZUpYs1AwiLGPafYAKKl/CORn/uaxYDwlNwndAk=
|
||||
go.etcd.io/etcd/raft/v3 v3.5.16/go.mod h1:P4UP14AxofMJ/54boWilabqqWoW9eLodl6I5GdGzazI=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -241,15 +241,15 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -259,14 +259,14 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
||||
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -277,12 +277,14 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
|
||||
google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
|
||||
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
|
||||
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
|
||||
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BackOffContext is a backoff policy that stops retrying after the context
|
||||
// is canceled.
|
||||
type BackOffContext interface { // nolint: golint
|
||||
BackOff
|
||||
Context() context.Context
|
||||
}
|
||||
|
||||
type backOffContext struct {
|
||||
BackOff
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// WithContext returns a BackOffContext with context ctx
|
||||
//
|
||||
// ctx must not be nil
|
||||
func WithContext(b BackOff, ctx context.Context) BackOffContext { // nolint: golint
|
||||
if ctx == nil {
|
||||
panic("nil context")
|
||||
}
|
||||
|
||||
if b, ok := b.(*backOffContext); ok {
|
||||
return &backOffContext{
|
||||
BackOff: b.BackOff,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
return &backOffContext{
|
||||
BackOff: b,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func getContext(b BackOff) context.Context {
|
||||
if cb, ok := b.(BackOffContext); ok {
|
||||
return cb.Context()
|
||||
}
|
||||
if tb, ok := b.(*backOffTries); ok {
|
||||
return getContext(tb.delegate)
|
||||
}
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
func (b *backOffContext) Context() context.Context {
|
||||
return b.ctx
|
||||
}
|
||||
|
||||
func (b *backOffContext) NextBackOff() time.Duration {
|
||||
select {
|
||||
case <-b.ctx.Done():
|
||||
return Stop
|
||||
default:
|
||||
return b.BackOff.NextBackOff()
|
||||
}
|
||||
}
|
||||
-216
@@ -1,216 +0,0 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
ExponentialBackOff is a backoff implementation that increases the backoff
|
||||
period for each retry attempt using a randomization function that grows exponentially.
|
||||
|
||||
NextBackOff() is calculated using the following formula:
|
||||
|
||||
randomized interval =
|
||||
RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor])
|
||||
|
||||
In other words NextBackOff() will range between the randomization factor
|
||||
percentage below and above the retry interval.
|
||||
|
||||
For example, given the following parameters:
|
||||
|
||||
RetryInterval = 2
|
||||
RandomizationFactor = 0.5
|
||||
Multiplier = 2
|
||||
|
||||
the actual backoff period used in the next retry attempt will range between 1 and 3 seconds,
|
||||
multiplied by the exponential, that is, between 2 and 6 seconds.
|
||||
|
||||
Note: MaxInterval caps the RetryInterval and not the randomized interval.
|
||||
|
||||
If the time elapsed since an ExponentialBackOff instance is created goes past the
|
||||
MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop.
|
||||
|
||||
The elapsed time can be reset by calling Reset().
|
||||
|
||||
Example: Given the following default arguments, for 10 tries the sequence will be,
|
||||
and assuming we go over the MaxElapsedTime on the 10th try:
|
||||
|
||||
Request # RetryInterval (seconds) Randomized Interval (seconds)
|
||||
|
||||
1 0.5 [0.25, 0.75]
|
||||
2 0.75 [0.375, 1.125]
|
||||
3 1.125 [0.562, 1.687]
|
||||
4 1.687 [0.8435, 2.53]
|
||||
5 2.53 [1.265, 3.795]
|
||||
6 3.795 [1.897, 5.692]
|
||||
7 5.692 [2.846, 8.538]
|
||||
8 8.538 [4.269, 12.807]
|
||||
9 12.807 [6.403, 19.210]
|
||||
10 19.210 backoff.Stop
|
||||
|
||||
Note: Implementation is not thread-safe.
|
||||
*/
|
||||
type ExponentialBackOff struct {
|
||||
InitialInterval time.Duration
|
||||
RandomizationFactor float64
|
||||
Multiplier float64
|
||||
MaxInterval time.Duration
|
||||
// After MaxElapsedTime the ExponentialBackOff returns Stop.
|
||||
// It never stops if MaxElapsedTime == 0.
|
||||
MaxElapsedTime time.Duration
|
||||
Stop time.Duration
|
||||
Clock Clock
|
||||
|
||||
currentInterval time.Duration
|
||||
startTime time.Time
|
||||
}
|
||||
|
||||
// Clock is an interface that returns current time for BackOff.
|
||||
type Clock interface {
|
||||
Now() time.Time
|
||||
}
|
||||
|
||||
// ExponentialBackOffOpts is a function type used to configure ExponentialBackOff options.
|
||||
type ExponentialBackOffOpts func(*ExponentialBackOff)
|
||||
|
||||
// Default values for ExponentialBackOff.
|
||||
const (
|
||||
DefaultInitialInterval = 500 * time.Millisecond
|
||||
DefaultRandomizationFactor = 0.5
|
||||
DefaultMultiplier = 1.5
|
||||
DefaultMaxInterval = 60 * time.Second
|
||||
DefaultMaxElapsedTime = 15 * time.Minute
|
||||
)
|
||||
|
||||
// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
|
||||
func NewExponentialBackOff(opts ...ExponentialBackOffOpts) *ExponentialBackOff {
|
||||
b := &ExponentialBackOff{
|
||||
InitialInterval: DefaultInitialInterval,
|
||||
RandomizationFactor: DefaultRandomizationFactor,
|
||||
Multiplier: DefaultMultiplier,
|
||||
MaxInterval: DefaultMaxInterval,
|
||||
MaxElapsedTime: DefaultMaxElapsedTime,
|
||||
Stop: Stop,
|
||||
Clock: SystemClock,
|
||||
}
|
||||
for _, fn := range opts {
|
||||
fn(b)
|
||||
}
|
||||
b.Reset()
|
||||
return b
|
||||
}
|
||||
|
||||
// WithInitialInterval sets the initial interval between retries.
|
||||
func WithInitialInterval(duration time.Duration) ExponentialBackOffOpts {
|
||||
return func(ebo *ExponentialBackOff) {
|
||||
ebo.InitialInterval = duration
|
||||
}
|
||||
}
|
||||
|
||||
// WithRandomizationFactor sets the randomization factor to add jitter to intervals.
|
||||
func WithRandomizationFactor(randomizationFactor float64) ExponentialBackOffOpts {
|
||||
return func(ebo *ExponentialBackOff) {
|
||||
ebo.RandomizationFactor = randomizationFactor
|
||||
}
|
||||
}
|
||||
|
||||
// WithMultiplier sets the multiplier for increasing the interval after each retry.
|
||||
func WithMultiplier(multiplier float64) ExponentialBackOffOpts {
|
||||
return func(ebo *ExponentialBackOff) {
|
||||
ebo.Multiplier = multiplier
|
||||
}
|
||||
}
|
||||
|
||||
// WithMaxInterval sets the maximum interval between retries.
|
||||
func WithMaxInterval(duration time.Duration) ExponentialBackOffOpts {
|
||||
return func(ebo *ExponentialBackOff) {
|
||||
ebo.MaxInterval = duration
|
||||
}
|
||||
}
|
||||
|
||||
// WithMaxElapsedTime sets the maximum total time for retries.
|
||||
func WithMaxElapsedTime(duration time.Duration) ExponentialBackOffOpts {
|
||||
return func(ebo *ExponentialBackOff) {
|
||||
ebo.MaxElapsedTime = duration
|
||||
}
|
||||
}
|
||||
|
||||
// WithRetryStopDuration sets the duration after which retries should stop.
|
||||
func WithRetryStopDuration(duration time.Duration) ExponentialBackOffOpts {
|
||||
return func(ebo *ExponentialBackOff) {
|
||||
ebo.Stop = duration
|
||||
}
|
||||
}
|
||||
|
||||
// WithClockProvider sets the clock used to measure time.
|
||||
func WithClockProvider(clock Clock) ExponentialBackOffOpts {
|
||||
return func(ebo *ExponentialBackOff) {
|
||||
ebo.Clock = clock
|
||||
}
|
||||
}
|
||||
|
||||
type systemClock struct{}
|
||||
|
||||
func (t systemClock) Now() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// SystemClock implements Clock interface that uses time.Now().
|
||||
var SystemClock = systemClock{}
|
||||
|
||||
// Reset the interval back to the initial retry interval and restarts the timer.
|
||||
// Reset must be called before using b.
|
||||
func (b *ExponentialBackOff) Reset() {
|
||||
b.currentInterval = b.InitialInterval
|
||||
b.startTime = b.Clock.Now()
|
||||
}
|
||||
|
||||
// NextBackOff calculates the next backoff interval using the formula:
|
||||
// Randomized interval = RetryInterval * (1 ± RandomizationFactor)
|
||||
func (b *ExponentialBackOff) NextBackOff() time.Duration {
|
||||
// Make sure we have not gone over the maximum elapsed time.
|
||||
elapsed := b.GetElapsedTime()
|
||||
next := getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
|
||||
b.incrementCurrentInterval()
|
||||
if b.MaxElapsedTime != 0 && elapsed+next > b.MaxElapsedTime {
|
||||
return b.Stop
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance
|
||||
// is created and is reset when Reset() is called.
|
||||
//
|
||||
// The elapsed time is computed using time.Now().UnixNano(). It is
|
||||
// safe to call even while the backoff policy is used by a running
|
||||
// ticker.
|
||||
func (b *ExponentialBackOff) GetElapsedTime() time.Duration {
|
||||
return b.Clock.Now().Sub(b.startTime)
|
||||
}
|
||||
|
||||
// Increments the current interval by multiplying it with the multiplier.
|
||||
func (b *ExponentialBackOff) incrementCurrentInterval() {
|
||||
// Check for overflow, if overflow is detected set the current interval to the max interval.
|
||||
if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
|
||||
b.currentInterval = b.MaxInterval
|
||||
} else {
|
||||
b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a random value from the following interval:
|
||||
// [currentInterval - randomizationFactor * currentInterval, currentInterval + randomizationFactor * currentInterval].
|
||||
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
|
||||
if randomizationFactor == 0 {
|
||||
return currentInterval // make sure no randomness is used when randomizationFactor is 0.
|
||||
}
|
||||
var delta = randomizationFactor * float64(currentInterval)
|
||||
var minInterval = float64(currentInterval) - delta
|
||||
var maxInterval = float64(currentInterval) + delta
|
||||
|
||||
// Get a random value from the range [minInterval, maxInterval].
|
||||
// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
|
||||
// we want a 33% chance for selecting either 1, 2 or 3.
|
||||
return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
|
||||
}
|
||||
-146
@@ -1,146 +0,0 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// An OperationWithData is executing by RetryWithData() or RetryNotifyWithData().
|
||||
// The operation will be retried using a backoff policy if it returns an error.
|
||||
type OperationWithData[T any] func() (T, error)
|
||||
|
||||
// An Operation is executing by Retry() or RetryNotify().
|
||||
// The operation will be retried using a backoff policy if it returns an error.
|
||||
type Operation func() error
|
||||
|
||||
func (o Operation) withEmptyData() OperationWithData[struct{}] {
|
||||
return func() (struct{}, error) {
|
||||
return struct{}{}, o()
|
||||
}
|
||||
}
|
||||
|
||||
// Notify is a notify-on-error function. It receives an operation error and
|
||||
// backoff delay if the operation failed (with an error).
|
||||
//
|
||||
// NOTE that if the backoff policy stated to stop retrying,
|
||||
// the notify function isn't called.
|
||||
type Notify func(error, time.Duration)
|
||||
|
||||
// Retry the operation o until it does not return error or BackOff stops.
|
||||
// o is guaranteed to be run at least once.
|
||||
//
|
||||
// If o returns a *PermanentError, the operation is not retried, and the
|
||||
// wrapped error is returned.
|
||||
//
|
||||
// Retry sleeps the goroutine for the duration returned by BackOff after a
|
||||
// failed operation returns.
|
||||
func Retry(o Operation, b BackOff) error {
|
||||
return RetryNotify(o, b, nil)
|
||||
}
|
||||
|
||||
// RetryWithData is like Retry but returns data in the response too.
|
||||
func RetryWithData[T any](o OperationWithData[T], b BackOff) (T, error) {
|
||||
return RetryNotifyWithData(o, b, nil)
|
||||
}
|
||||
|
||||
// RetryNotify calls notify function with the error and wait duration
|
||||
// for each failed attempt before sleep.
|
||||
func RetryNotify(operation Operation, b BackOff, notify Notify) error {
|
||||
return RetryNotifyWithTimer(operation, b, notify, nil)
|
||||
}
|
||||
|
||||
// RetryNotifyWithData is like RetryNotify but returns data in the response too.
|
||||
func RetryNotifyWithData[T any](operation OperationWithData[T], b BackOff, notify Notify) (T, error) {
|
||||
return doRetryNotify(operation, b, notify, nil)
|
||||
}
|
||||
|
||||
// RetryNotifyWithTimer calls notify function with the error and wait duration using the given Timer
|
||||
// for each failed attempt before sleep.
|
||||
// A default timer that uses system timer is used when nil is passed.
|
||||
func RetryNotifyWithTimer(operation Operation, b BackOff, notify Notify, t Timer) error {
|
||||
_, err := doRetryNotify(operation.withEmptyData(), b, notify, t)
|
||||
return err
|
||||
}
|
||||
|
||||
// RetryNotifyWithTimerAndData is like RetryNotifyWithTimer but returns data in the response too.
|
||||
func RetryNotifyWithTimerAndData[T any](operation OperationWithData[T], b BackOff, notify Notify, t Timer) (T, error) {
|
||||
return doRetryNotify(operation, b, notify, t)
|
||||
}
|
||||
|
||||
func doRetryNotify[T any](operation OperationWithData[T], b BackOff, notify Notify, t Timer) (T, error) {
|
||||
var (
|
||||
err error
|
||||
next time.Duration
|
||||
res T
|
||||
)
|
||||
if t == nil {
|
||||
t = &defaultTimer{}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
t.Stop()
|
||||
}()
|
||||
|
||||
ctx := getContext(b)
|
||||
|
||||
b.Reset()
|
||||
for {
|
||||
res, err = operation()
|
||||
if err == nil {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
var permanent *PermanentError
|
||||
if errors.As(err, &permanent) {
|
||||
return res, permanent.Err
|
||||
}
|
||||
|
||||
if next = b.NextBackOff(); next == Stop {
|
||||
if cerr := ctx.Err(); cerr != nil {
|
||||
return res, cerr
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
if notify != nil {
|
||||
notify(err, next)
|
||||
}
|
||||
|
||||
t.Start(next)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return res, ctx.Err()
|
||||
case <-t.C():
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PermanentError signals that the operation should not be retried.
|
||||
type PermanentError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *PermanentError) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
func (e *PermanentError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
func (e *PermanentError) Is(target error) bool {
|
||||
_, ok := target.(*PermanentError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// Permanent wraps the given err in a *PermanentError.
|
||||
func Permanent(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &PermanentError{
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
package backoff
|
||||
|
||||
import "time"
|
||||
|
||||
/*
|
||||
WithMaxRetries creates a wrapper around another BackOff, which will
|
||||
return Stop if NextBackOff() has been called too many times since
|
||||
the last time Reset() was called
|
||||
|
||||
Note: Implementation is not thread-safe.
|
||||
*/
|
||||
func WithMaxRetries(b BackOff, max uint64) BackOff {
|
||||
return &backOffTries{delegate: b, maxTries: max}
|
||||
}
|
||||
|
||||
type backOffTries struct {
|
||||
delegate BackOff
|
||||
maxTries uint64
|
||||
numTries uint64
|
||||
}
|
||||
|
||||
func (b *backOffTries) NextBackOff() time.Duration {
|
||||
if b.maxTries == 0 {
|
||||
return Stop
|
||||
}
|
||||
if b.maxTries > 0 {
|
||||
if b.maxTries <= b.numTries {
|
||||
return Stop
|
||||
}
|
||||
b.numTries++
|
||||
}
|
||||
return b.delegate.NextBackOff()
|
||||
}
|
||||
|
||||
func (b *backOffTries) Reset() {
|
||||
b.numTries = 0
|
||||
b.delegate.Reset()
|
||||
}
|
||||
Generated
+29
@@ -0,0 +1,29 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [5.0.0] - 2024-12-19
|
||||
|
||||
### Added
|
||||
|
||||
- RetryAfterError can be returned from an operation to indicate how long to wait before the next retry.
|
||||
|
||||
### Changed
|
||||
|
||||
- Retry function now accepts additional options for specifying max number of tries and max elapsed time.
|
||||
- Retry function now accepts a context.Context.
|
||||
- Operation function signature changed to return result (any type) and error.
|
||||
|
||||
### Removed
|
||||
|
||||
- RetryNotify* and RetryWithData functions. Only single Retry function remains.
|
||||
- Optional arguments from ExponentialBackoff constructor.
|
||||
- Clock and Timer interfaces.
|
||||
|
||||
### Fixed
|
||||
|
||||
- The original error is returned from Retry if there's a PermanentError. (#144)
|
||||
- The Retry function respects the wrapped PermanentError. (#140)
|
||||
Generated
Vendored
Generated
Vendored
+8
-7
@@ -1,4 +1,4 @@
|
||||
# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Coverage Status][coveralls image]][coveralls]
|
||||
# Exponential Backoff [![GoDoc][godoc image]][godoc]
|
||||
|
||||
This is a Go port of the exponential backoff algorithm from [Google's HTTP Client Library for Java][google-http-java-client].
|
||||
|
||||
@@ -9,9 +9,11 @@ The retries exponentially increase and stop increasing when a certain threshold
|
||||
|
||||
## Usage
|
||||
|
||||
Import path is `github.com/cenkalti/backoff/v4`. Please note the version part at the end.
|
||||
Import path is `github.com/cenkalti/backoff/v5`. Please note the version part at the end.
|
||||
|
||||
Use https://pkg.go.dev/github.com/cenkalti/backoff/v4 to view the documentation.
|
||||
For most cases, use `Retry` function. See [example_test.go][example] for an example.
|
||||
|
||||
If you have specific needs, copy `Retry` function (from [retry.go][retry-src]) into your code and modify it as needed.
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -19,12 +21,11 @@ Use https://pkg.go.dev/github.com/cenkalti/backoff/v4 to view the documentation.
|
||||
* Please don't send a PR without opening an issue and discussing it first.
|
||||
* If proposed change is not a common use case, I will probably not accept it.
|
||||
|
||||
[godoc]: https://pkg.go.dev/github.com/cenkalti/backoff/v4
|
||||
[godoc]: https://pkg.go.dev/github.com/cenkalti/backoff/v5
|
||||
[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png
|
||||
[coveralls]: https://coveralls.io/github/cenkalti/backoff?branch=master
|
||||
[coveralls image]: https://coveralls.io/repos/github/cenkalti/backoff/badge.svg?branch=master
|
||||
|
||||
[google-http-java-client]: https://github.com/google/google-http-java-client/blob/da1aa993e90285ec18579f1553339b00e19b3ab5/google-http-client/src/main/java/com/google/api/client/util/ExponentialBackOff.java
|
||||
[exponential backoff wiki]: http://en.wikipedia.org/wiki/Exponential_backoff
|
||||
|
||||
[advanced example]: https://pkg.go.dev/github.com/cenkalti/backoff/v4?tab=doc#pkg-examples
|
||||
[retry-src]: https://github.com/cenkalti/backoff/blob/v5/retry.go
|
||||
[example]: https://github.com/cenkalti/backoff/blob/v5/example_test.go
|
||||
Generated
Vendored
+7
-7
@@ -15,16 +15,16 @@ import "time"
|
||||
// BackOff is a backoff policy for retrying an operation.
|
||||
type BackOff interface {
|
||||
// NextBackOff returns the duration to wait before retrying the operation,
|
||||
// or backoff. Stop to indicate that no more retries should be made.
|
||||
// backoff.Stop to indicate that no more retries should be made.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// duration := backoff.NextBackOff();
|
||||
// if (duration == backoff.Stop) {
|
||||
// // Do not retry operation.
|
||||
// } else {
|
||||
// // Sleep for duration and retry operation.
|
||||
// }
|
||||
// duration := backoff.NextBackOff()
|
||||
// if duration == backoff.Stop {
|
||||
// // Do not retry operation.
|
||||
// } else {
|
||||
// // Sleep for duration and retry operation.
|
||||
// }
|
||||
//
|
||||
NextBackOff() time.Duration
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PermanentError signals that the operation should not be retried.
|
||||
type PermanentError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// Permanent wraps the given err in a *PermanentError.
|
||||
func Permanent(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &PermanentError{
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// Error returns a string representation of the Permanent error.
|
||||
func (e *PermanentError) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
// Unwrap returns the wrapped error.
|
||||
func (e *PermanentError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// RetryAfterError signals that the operation should be retried after the given duration.
|
||||
type RetryAfterError struct {
|
||||
Duration time.Duration
|
||||
}
|
||||
|
||||
// RetryAfter returns a RetryAfter error that specifies how long to wait before retrying.
|
||||
func RetryAfter(seconds int) error {
|
||||
return &RetryAfterError{Duration: time.Duration(seconds) * time.Second}
|
||||
}
|
||||
|
||||
// Error returns a string representation of the RetryAfter error.
|
||||
func (e *RetryAfterError) Error() string {
|
||||
return fmt.Sprintf("retry after %s", e.Duration)
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"math/rand/v2"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
ExponentialBackOff is a backoff implementation that increases the backoff
|
||||
period for each retry attempt using a randomization function that grows exponentially.
|
||||
|
||||
NextBackOff() is calculated using the following formula:
|
||||
|
||||
randomized interval =
|
||||
RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor])
|
||||
|
||||
In other words NextBackOff() will range between the randomization factor
|
||||
percentage below and above the retry interval.
|
||||
|
||||
For example, given the following parameters:
|
||||
|
||||
RetryInterval = 2
|
||||
RandomizationFactor = 0.5
|
||||
Multiplier = 2
|
||||
|
||||
the actual backoff period used in the next retry attempt will range between 1 and 3 seconds,
|
||||
multiplied by the exponential, that is, between 2 and 6 seconds.
|
||||
|
||||
Note: MaxInterval caps the RetryInterval and not the randomized interval.
|
||||
|
||||
Example: Given the following default arguments, for 9 tries the sequence will be:
|
||||
|
||||
Request # RetryInterval (seconds) Randomized Interval (seconds)
|
||||
|
||||
1 0.5 [0.25, 0.75]
|
||||
2 0.75 [0.375, 1.125]
|
||||
3 1.125 [0.562, 1.687]
|
||||
4 1.687 [0.8435, 2.53]
|
||||
5 2.53 [1.265, 3.795]
|
||||
6 3.795 [1.897, 5.692]
|
||||
7 5.692 [2.846, 8.538]
|
||||
8 8.538 [4.269, 12.807]
|
||||
9 12.807 [6.403, 19.210]
|
||||
|
||||
Note: Implementation is not thread-safe.
|
||||
*/
|
||||
type ExponentialBackOff struct {
|
||||
InitialInterval time.Duration
|
||||
RandomizationFactor float64
|
||||
Multiplier float64
|
||||
MaxInterval time.Duration
|
||||
|
||||
currentInterval time.Duration
|
||||
}
|
||||
|
||||
// Default values for ExponentialBackOff.
|
||||
const (
|
||||
DefaultInitialInterval = 500 * time.Millisecond
|
||||
DefaultRandomizationFactor = 0.5
|
||||
DefaultMultiplier = 1.5
|
||||
DefaultMaxInterval = 60 * time.Second
|
||||
)
|
||||
|
||||
// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
|
||||
func NewExponentialBackOff() *ExponentialBackOff {
|
||||
return &ExponentialBackOff{
|
||||
InitialInterval: DefaultInitialInterval,
|
||||
RandomizationFactor: DefaultRandomizationFactor,
|
||||
Multiplier: DefaultMultiplier,
|
||||
MaxInterval: DefaultMaxInterval,
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the interval back to the initial retry interval and restarts the timer.
|
||||
// Reset must be called before using b.
|
||||
func (b *ExponentialBackOff) Reset() {
|
||||
b.currentInterval = b.InitialInterval
|
||||
}
|
||||
|
||||
// NextBackOff calculates the next backoff interval using the formula:
|
||||
//
|
||||
// Randomized interval = RetryInterval * (1 ± RandomizationFactor)
|
||||
func (b *ExponentialBackOff) NextBackOff() time.Duration {
|
||||
if b.currentInterval == 0 {
|
||||
b.currentInterval = b.InitialInterval
|
||||
}
|
||||
|
||||
next := getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
|
||||
b.incrementCurrentInterval()
|
||||
return next
|
||||
}
|
||||
|
||||
// Increments the current interval by multiplying it with the multiplier.
|
||||
func (b *ExponentialBackOff) incrementCurrentInterval() {
|
||||
// Check for overflow, if overflow is detected set the current interval to the max interval.
|
||||
if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
|
||||
b.currentInterval = b.MaxInterval
|
||||
} else {
|
||||
b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a random value from the following interval:
|
||||
//
|
||||
// [currentInterval - randomizationFactor * currentInterval, currentInterval + randomizationFactor * currentInterval].
|
||||
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
|
||||
if randomizationFactor == 0 {
|
||||
return currentInterval // make sure no randomness is used when randomizationFactor is 0.
|
||||
}
|
||||
var delta = randomizationFactor * float64(currentInterval)
|
||||
var minInterval = float64(currentInterval) - delta
|
||||
var maxInterval = float64(currentInterval) + delta
|
||||
|
||||
// Get a random value from the range [minInterval, maxInterval].
|
||||
// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
|
||||
// we want a 33% chance for selecting either 1, 2 or 3.
|
||||
return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultMaxElapsedTime sets a default limit for the total retry duration.
|
||||
const DefaultMaxElapsedTime = 15 * time.Minute
|
||||
|
||||
// Operation is a function that attempts an operation and may be retried.
|
||||
type Operation[T any] func() (T, error)
|
||||
|
||||
// Notify is a function called on operation error with the error and backoff duration.
|
||||
type Notify func(error, time.Duration)
|
||||
|
||||
// retryOptions holds configuration settings for the retry mechanism.
|
||||
type retryOptions struct {
|
||||
BackOff BackOff // Strategy for calculating backoff periods.
|
||||
Timer timer // Timer to manage retry delays.
|
||||
Notify Notify // Optional function to notify on each retry error.
|
||||
MaxTries uint // Maximum number of retry attempts.
|
||||
MaxElapsedTime time.Duration // Maximum total time for all retries.
|
||||
}
|
||||
|
||||
type RetryOption func(*retryOptions)
|
||||
|
||||
// WithBackOff configures a custom backoff strategy.
|
||||
func WithBackOff(b BackOff) RetryOption {
|
||||
return func(args *retryOptions) {
|
||||
args.BackOff = b
|
||||
}
|
||||
}
|
||||
|
||||
// withTimer sets a custom timer for managing delays between retries.
|
||||
func withTimer(t timer) RetryOption {
|
||||
return func(args *retryOptions) {
|
||||
args.Timer = t
|
||||
}
|
||||
}
|
||||
|
||||
// WithNotify sets a notification function to handle retry errors.
|
||||
func WithNotify(n Notify) RetryOption {
|
||||
return func(args *retryOptions) {
|
||||
args.Notify = n
|
||||
}
|
||||
}
|
||||
|
||||
// WithMaxTries limits the number of all attempts.
|
||||
func WithMaxTries(n uint) RetryOption {
|
||||
return func(args *retryOptions) {
|
||||
args.MaxTries = n
|
||||
}
|
||||
}
|
||||
|
||||
// WithMaxElapsedTime limits the total duration for retry attempts.
|
||||
func WithMaxElapsedTime(d time.Duration) RetryOption {
|
||||
return func(args *retryOptions) {
|
||||
args.MaxElapsedTime = d
|
||||
}
|
||||
}
|
||||
|
||||
// Retry attempts the operation until success, a permanent error, or backoff completion.
|
||||
// It ensures the operation is executed at least once.
|
||||
//
|
||||
// Returns the operation result or error if retries are exhausted or context is cancelled.
|
||||
func Retry[T any](ctx context.Context, operation Operation[T], opts ...RetryOption) (T, error) {
|
||||
// Initialize default retry options.
|
||||
args := &retryOptions{
|
||||
BackOff: NewExponentialBackOff(),
|
||||
Timer: &defaultTimer{},
|
||||
MaxElapsedTime: DefaultMaxElapsedTime,
|
||||
}
|
||||
|
||||
// Apply user-provided options to the default settings.
|
||||
for _, opt := range opts {
|
||||
opt(args)
|
||||
}
|
||||
|
||||
defer args.Timer.Stop()
|
||||
|
||||
startedAt := time.Now()
|
||||
args.BackOff.Reset()
|
||||
for numTries := uint(1); ; numTries++ {
|
||||
// Execute the operation.
|
||||
res, err := operation()
|
||||
if err == nil {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Stop retrying if maximum tries exceeded.
|
||||
if args.MaxTries > 0 && numTries >= args.MaxTries {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Handle permanent errors without retrying.
|
||||
var permanent *PermanentError
|
||||
if errors.As(err, &permanent) {
|
||||
return res, permanent.Unwrap()
|
||||
}
|
||||
|
||||
// Stop retrying if context is cancelled.
|
||||
if cerr := context.Cause(ctx); cerr != nil {
|
||||
return res, cerr
|
||||
}
|
||||
|
||||
// Calculate next backoff duration.
|
||||
next := args.BackOff.NextBackOff()
|
||||
if next == Stop {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Reset backoff if RetryAfterError is encountered.
|
||||
var retryAfter *RetryAfterError
|
||||
if errors.As(err, &retryAfter) {
|
||||
next = retryAfter.Duration
|
||||
args.BackOff.Reset()
|
||||
}
|
||||
|
||||
// Stop retrying if maximum elapsed time exceeded.
|
||||
if args.MaxElapsedTime > 0 && time.Since(startedAt)+next > args.MaxElapsedTime {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Notify on error if a notifier function is provided.
|
||||
if args.Notify != nil {
|
||||
args.Notify(err, next)
|
||||
}
|
||||
|
||||
// Wait for the next backoff period or context cancellation.
|
||||
args.Timer.Start(next)
|
||||
select {
|
||||
case <-args.Timer.C():
|
||||
case <-ctx.Done():
|
||||
return res, context.Cause(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+2
-16
@@ -1,7 +1,6 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -14,8 +13,7 @@ type Ticker struct {
|
||||
C <-chan time.Time
|
||||
c chan time.Time
|
||||
b BackOff
|
||||
ctx context.Context
|
||||
timer Timer
|
||||
timer timer
|
||||
stop chan struct{}
|
||||
stopOnce sync.Once
|
||||
}
|
||||
@@ -27,22 +25,12 @@ type Ticker struct {
|
||||
// provided backoff policy (notably calling NextBackOff or Reset)
|
||||
// while the ticker is running.
|
||||
func NewTicker(b BackOff) *Ticker {
|
||||
return NewTickerWithTimer(b, &defaultTimer{})
|
||||
}
|
||||
|
||||
// NewTickerWithTimer returns a new Ticker with a custom timer.
|
||||
// A default timer that uses system timer is used when nil is passed.
|
||||
func NewTickerWithTimer(b BackOff, timer Timer) *Ticker {
|
||||
if timer == nil {
|
||||
timer = &defaultTimer{}
|
||||
}
|
||||
c := make(chan time.Time)
|
||||
t := &Ticker{
|
||||
C: c,
|
||||
c: c,
|
||||
b: b,
|
||||
ctx: getContext(b),
|
||||
timer: timer,
|
||||
timer: &defaultTimer{},
|
||||
stop: make(chan struct{}),
|
||||
}
|
||||
t.b.Reset()
|
||||
@@ -73,8 +61,6 @@ func (t *Ticker) run() {
|
||||
case <-t.stop:
|
||||
t.c = nil // Prevent future ticks from being sent to the channel.
|
||||
return
|
||||
case <-t.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+1
-1
@@ -2,7 +2,7 @@ package backoff
|
||||
|
||||
import "time"
|
||||
|
||||
type Timer interface {
|
||||
type timer interface {
|
||||
Start(duration time.Duration)
|
||||
Stop()
|
||||
C() <-chan time.Time
|
||||
+9
-7
@@ -1,26 +1,28 @@
|
||||
version: "2"
|
||||
|
||||
run:
|
||||
timeout: 1m
|
||||
tests: true
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
default: none
|
||||
enable: # please keep this alphabetized
|
||||
- asasalint
|
||||
- asciicheck
|
||||
- copyloopvar
|
||||
- dupl
|
||||
- errcheck
|
||||
- forcetypeassert
|
||||
- goconst
|
||||
- gocritic
|
||||
- gofmt
|
||||
- goimports
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- musttag
|
||||
- revive
|
||||
- staticcheck
|
||||
- typecheck
|
||||
- unused
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 10
|
||||
|
||||
+4
-4
@@ -77,7 +77,7 @@ func newSink(fn func(prefix, args string), formatter Formatter) logr.LogSink {
|
||||
write: fn,
|
||||
}
|
||||
// For skipping fnlogger.Info and fnlogger.Error.
|
||||
l.Formatter.AddCallDepth(1)
|
||||
l.AddCallDepth(1) // via Formatter
|
||||
return l
|
||||
}
|
||||
|
||||
@@ -164,17 +164,17 @@ type fnlogger struct {
|
||||
}
|
||||
|
||||
func (l fnlogger) WithName(name string) logr.LogSink {
|
||||
l.Formatter.AddName(name)
|
||||
l.AddName(name) // via Formatter
|
||||
return &l
|
||||
}
|
||||
|
||||
func (l fnlogger) WithValues(kvList ...any) logr.LogSink {
|
||||
l.Formatter.AddValues(kvList)
|
||||
l.AddValues(kvList) // via Formatter
|
||||
return &l
|
||||
}
|
||||
|
||||
func (l fnlogger) WithCallDepth(depth int) logr.LogSink {
|
||||
l.Formatter.AddCallDepth(depth)
|
||||
l.AddCallDepth(depth) // via Formatter
|
||||
return &l
|
||||
}
|
||||
|
||||
|
||||
+13
-15
@@ -148,22 +148,20 @@ func DefaultHTTPErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marsh
|
||||
}
|
||||
|
||||
md, ok := ServerMetadataFromContext(ctx)
|
||||
if !ok {
|
||||
grpclog.Error("Failed to extract ServerMetadata from context")
|
||||
}
|
||||
if ok {
|
||||
handleForwardResponseServerMetadata(w, mux, md)
|
||||
|
||||
handleForwardResponseServerMetadata(w, mux, md)
|
||||
// RFC 7230 https://tools.ietf.org/html/rfc7230#section-4.1.2
|
||||
// Unless the request includes a TE header field indicating "trailers"
|
||||
// is acceptable, as described in Section 4.3, a server SHOULD NOT
|
||||
// generate trailer fields that it believes are necessary for the user
|
||||
// agent to receive.
|
||||
doForwardTrailers := requestAcceptsTrailers(r)
|
||||
|
||||
// RFC 7230 https://tools.ietf.org/html/rfc7230#section-4.1.2
|
||||
// Unless the request includes a TE header field indicating "trailers"
|
||||
// is acceptable, as described in Section 4.3, a server SHOULD NOT
|
||||
// generate trailer fields that it believes are necessary for the user
|
||||
// agent to receive.
|
||||
doForwardTrailers := requestAcceptsTrailers(r)
|
||||
|
||||
if doForwardTrailers {
|
||||
handleForwardResponseTrailerHeader(w, mux, md)
|
||||
w.Header().Set("Transfer-Encoding", "chunked")
|
||||
if doForwardTrailers {
|
||||
handleForwardResponseTrailerHeader(w, mux, md)
|
||||
w.Header().Set("Transfer-Encoding", "chunked")
|
||||
}
|
||||
}
|
||||
|
||||
st := HTTPStatusFromCode(s.Code())
|
||||
@@ -176,7 +174,7 @@ func DefaultHTTPErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marsh
|
||||
grpclog.Errorf("Failed to write response: %v", err)
|
||||
}
|
||||
|
||||
if doForwardTrailers {
|
||||
if ok && requestAcceptsTrailers(r) {
|
||||
handleForwardResponseTrailer(w, mux, md)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-6
@@ -153,12 +153,10 @@ type responseBody interface {
|
||||
// ForwardResponseMessage forwards the message "resp" from gRPC server to REST client.
|
||||
func ForwardResponseMessage(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, req *http.Request, resp proto.Message, opts ...func(context.Context, http.ResponseWriter, proto.Message) error) {
|
||||
md, ok := ServerMetadataFromContext(ctx)
|
||||
if !ok {
|
||||
grpclog.Error("Failed to extract ServerMetadata from context")
|
||||
if ok {
|
||||
handleForwardResponseServerMetadata(w, mux, md)
|
||||
}
|
||||
|
||||
handleForwardResponseServerMetadata(w, mux, md)
|
||||
|
||||
// RFC 7230 https://tools.ietf.org/html/rfc7230#section-4.1.2
|
||||
// Unless the request includes a TE header field indicating "trailers"
|
||||
// is acceptable, as described in Section 4.3, a server SHOULD NOT
|
||||
@@ -166,7 +164,7 @@ func ForwardResponseMessage(ctx context.Context, mux *ServeMux, marshaler Marsha
|
||||
// agent to receive.
|
||||
doForwardTrailers := requestAcceptsTrailers(req)
|
||||
|
||||
if doForwardTrailers {
|
||||
if ok && doForwardTrailers {
|
||||
handleForwardResponseTrailerHeader(w, mux, md)
|
||||
w.Header().Set("Transfer-Encoding", "chunked")
|
||||
}
|
||||
@@ -204,7 +202,7 @@ func ForwardResponseMessage(ctx context.Context, mux *ServeMux, marshaler Marsha
|
||||
grpclog.Errorf("Failed to write response: %v", err)
|
||||
}
|
||||
|
||||
if doForwardTrailers {
|
||||
if ok && doForwardTrailers {
|
||||
handleForwardResponseTrailer(w, mux, md)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -7,9 +7,7 @@ import (
|
||||
)
|
||||
|
||||
// HealthStatus is a string representation of the container's health.
|
||||
//
|
||||
// It currently is an alias for string, but may become a distinct type in future.
|
||||
type HealthStatus = string
|
||||
type HealthStatus string
|
||||
|
||||
// Health states
|
||||
const (
|
||||
@@ -41,7 +39,10 @@ type HealthcheckResult struct {
|
||||
}
|
||||
|
||||
var validHealths = []string{
|
||||
NoHealthcheck, Starting, Healthy, Unhealthy,
|
||||
string(NoHealthcheck),
|
||||
string(Starting),
|
||||
string(Healthy),
|
||||
string(Unhealthy),
|
||||
}
|
||||
|
||||
// ValidateHealthStatus checks if the provided string is a valid
|
||||
|
||||
+9
-5
@@ -6,9 +6,7 @@ import (
|
||||
)
|
||||
|
||||
// ContainerState is a string representation of the container's current state.
|
||||
//
|
||||
// It currently is an alias for string, but may become a distinct type in the future.
|
||||
type ContainerState = string
|
||||
type ContainerState string
|
||||
|
||||
const (
|
||||
StateCreated ContainerState = "created" // StateCreated indicates the container is created, but not (yet) started.
|
||||
@@ -20,8 +18,14 @@ const (
|
||||
StateDead ContainerState = "dead" // StateDead indicates that the container failed to be deleted. Containers in this state are attempted to be cleaned up when the daemon restarts.
|
||||
)
|
||||
|
||||
var validStates = []ContainerState{
|
||||
StateCreated, StateRunning, StatePaused, StateRestarting, StateRemoving, StateExited, StateDead,
|
||||
var validStates = []string{
|
||||
string(StateCreated),
|
||||
string(StateRunning),
|
||||
string(StatePaused),
|
||||
string(StateRestarting),
|
||||
string(StateRemoving),
|
||||
string(StateExited),
|
||||
string(StateDead),
|
||||
}
|
||||
|
||||
// ValidateContainerState checks if the provided string is a valid
|
||||
|
||||
-7
@@ -51,13 +51,6 @@ type Config struct {
|
||||
// Required: true
|
||||
Description string `json:"Description"`
|
||||
|
||||
// Docker Version used to create the plugin.
|
||||
//
|
||||
// Depending on how the plugin was created, this field may be empty or omitted.
|
||||
//
|
||||
// Deprecated: this field is no longer set, and will be removed in the next API version.
|
||||
DockerVersion string `json:"DockerVersion,omitempty"`
|
||||
|
||||
// documentation
|
||||
// Example: https://docs.docker.com/engine/extend/plugins/
|
||||
// Required: true
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
// ListResponse contains the response for the Engine API
|
||||
type ListResponse []*Plugin
|
||||
type ListResponse []Plugin
|
||||
|
||||
// Privilege describes a permission the user has to accept
|
||||
// upon installing a plugin.
|
||||
|
||||
-19
@@ -24,27 +24,8 @@ const (
|
||||
// DiskUsage contains response of Engine API:
|
||||
// GET "/system/df"
|
||||
type DiskUsage struct {
|
||||
LegacyDiskUsage
|
||||
|
||||
ImageUsage *image.DiskUsage `json:"ImageUsage,omitempty"`
|
||||
ContainerUsage *container.DiskUsage `json:"ContainerUsage,omitempty"`
|
||||
VolumeUsage *volume.DiskUsage `json:"VolumeUsage,omitempty"`
|
||||
BuildCacheUsage *build.DiskUsage `json:"BuildCacheUsage,omitempty"`
|
||||
}
|
||||
|
||||
type LegacyDiskUsage struct {
|
||||
// Deprecated: kept to maintain backwards compatibility with API < v1.52, use [ImagesDiskUsage.TotalSize] instead.
|
||||
LayersSize int64 `json:"LayersSize,omitempty"`
|
||||
|
||||
// Deprecated: kept to maintain backwards compatibility with API < v1.52, use [ImagesDiskUsage.Items] instead.
|
||||
Images []image.Summary `json:"Images,omitzero"`
|
||||
|
||||
// Deprecated: kept to maintain backwards compatibility with API < v1.52, use [ContainersDiskUsage.Items] instead.
|
||||
Containers []container.Summary `json:"Containers,omitzero"`
|
||||
|
||||
// Deprecated: kept to maintain backwards compatibility with API < v1.52, use [VolumesDiskUsage.Items] instead.
|
||||
Volumes []volume.Volume `json:"Volumes,omitzero"`
|
||||
|
||||
// Deprecated: kept to maintain backwards compatibility with API < v1.52, use [BuildCacheDiskUsage.Items] instead.
|
||||
BuildCache []build.CacheRecord `json:"BuildCache,omitzero"`
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ package volume
|
||||
type ListResponse struct {
|
||||
|
||||
// List of volumes
|
||||
Volumes []*Volume `json:"Volumes"`
|
||||
Volumes []Volume `json:"Volumes"`
|
||||
|
||||
// Warnings that occurred when fetching the list of volumes.
|
||||
//
|
||||
|
||||
+13
-4
@@ -23,19 +23,28 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
apiClient, err := client.New(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||
// Create a new client that handles common environment variables
|
||||
// for configuration (DOCKER_HOST, DOCKER_API_VERSION), and does
|
||||
// API-version negotiation to allow downgrading the API version
|
||||
// when connecting with an older daemon version.
|
||||
apiClient, err := client.New(client.FromEnv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer apiClient.Close()
|
||||
|
||||
containers, err := apiClient.ContainerList(context.Background(), client.ContainerListOptions{All: true})
|
||||
// List all containers (both stopped and running).
|
||||
result, err := apiClient.ContainerList(context.Background(), client.ContainerListOptions{
|
||||
All: true,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, ctr := range containers {
|
||||
fmt.Printf("%s %s (status: %s)\n", ctr.ID, ctr.Image, ctr.Status)
|
||||
// Print each container's ID, status and the image it was created from.
|
||||
fmt.Printf("%s %-22s %s\n", "ID", "STATUS", "IMAGE")
|
||||
for _, ctr := range result.Items {
|
||||
fmt.Printf("%s %-22s %s\n", ctr.ID, ctr.Status, ctr.Image)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// CheckpointAPIClient defines API client methods for the checkpoints.
|
||||
//
|
||||
// Experimental: checkpoint and restore is still an experimental feature,
|
||||
// and only available if the daemon is running with experimental features
|
||||
// enabled.
|
||||
type CheckpointAPIClient interface {
|
||||
CheckpointCreate(ctx context.Context, container string, options CheckpointCreateOptions) error
|
||||
CheckpointDelete(ctx context.Context, container string, options CheckpointDeleteOptions) error
|
||||
CheckpointList(ctx context.Context, container string, options CheckpointListOptions) (CheckpointListResult, error)
|
||||
}
|
||||
+8
-3
@@ -13,11 +13,16 @@ type CheckpointCreateOptions struct {
|
||||
Exit bool
|
||||
}
|
||||
|
||||
// CheckpointCreateResult holds the result from [client.CheckpointCreate].
|
||||
type CheckpointCreateResult struct {
|
||||
// Add future fields here
|
||||
}
|
||||
|
||||
// CheckpointCreate creates a checkpoint from the given container.
|
||||
func (cli *Client) CheckpointCreate(ctx context.Context, containerID string, options CheckpointCreateOptions) error {
|
||||
func (cli *Client) CheckpointCreate(ctx context.Context, containerID string, options CheckpointCreateOptions) (CheckpointCreateResult, error) {
|
||||
containerID, err := trimID("container", containerID)
|
||||
if err != nil {
|
||||
return err
|
||||
return CheckpointCreateResult{}, err
|
||||
}
|
||||
requestBody := checkpoint.CreateRequest{
|
||||
CheckpointID: options.CheckpointID,
|
||||
@@ -27,5 +32,5 @@ func (cli *Client) CheckpointCreate(ctx context.Context, containerID string, opt
|
||||
|
||||
resp, err := cli.post(ctx, "/containers/"+containerID+"/checkpoints", nil, requestBody, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
return err
|
||||
return CheckpointCreateResult{}, err
|
||||
}
|
||||
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// CheckpointDeleteOptions holds parameters to delete a checkpoint from a container.
|
||||
type CheckpointDeleteOptions struct {
|
||||
CheckpointID string
|
||||
CheckpointDir string
|
||||
}
|
||||
|
||||
// CheckpointDelete deletes the checkpoint with the given name from the given container.
|
||||
func (cli *Client) CheckpointDelete(ctx context.Context, containerID string, options CheckpointDeleteOptions) error {
|
||||
containerID, err := trimID("container", containerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
if options.CheckpointDir != "" {
|
||||
query.Set("dir", options.CheckpointDir)
|
||||
}
|
||||
|
||||
resp, err := cli.delete(ctx, "/containers/"+containerID+"/checkpoints/"+options.CheckpointID, query, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
return err
|
||||
}
|
||||
+2
-2
@@ -15,7 +15,7 @@ type CheckpointListOptions struct {
|
||||
|
||||
// CheckpointListResult holds the result from the CheckpointList method.
|
||||
type CheckpointListResult struct {
|
||||
Checkpoints []checkpoint.Summary
|
||||
Items []checkpoint.Summary
|
||||
}
|
||||
|
||||
// CheckpointList returns the checkpoints of the given container in the docker host.
|
||||
@@ -33,6 +33,6 @@ func (cli *Client) CheckpointList(ctx context.Context, container string, options
|
||||
return out, err
|
||||
}
|
||||
|
||||
err = json.NewDecoder(resp.Body).Decode(&out.Checkpoints)
|
||||
err = json.NewDecoder(resp.Body).Decode(&out.Items)
|
||||
return out, err
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// CheckpointRemoveOptions holds parameters to delete a checkpoint from a container.
|
||||
type CheckpointRemoveOptions struct {
|
||||
CheckpointID string
|
||||
CheckpointDir string
|
||||
}
|
||||
|
||||
// CheckpointRemoveResult represents the result of [Client.CheckpointRemove].
|
||||
type CheckpointRemoveResult struct {
|
||||
// No fields currently; placeholder for future use.
|
||||
}
|
||||
|
||||
// CheckpointRemove deletes the checkpoint with the given name from the given container.
|
||||
func (cli *Client) CheckpointRemove(ctx context.Context, containerID string, options CheckpointRemoveOptions) (CheckpointRemoveResult, error) {
|
||||
containerID, err := trimID("container", containerID)
|
||||
if err != nil {
|
||||
return CheckpointRemoveResult{}, err
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
if options.CheckpointDir != "" {
|
||||
query.Set("dir", options.CheckpointDir)
|
||||
}
|
||||
|
||||
resp, err := cli.delete(ctx, "/containers/"+containerID+"/checkpoints/"+options.CheckpointID, query, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
return CheckpointRemoveResult{}, err
|
||||
}
|
||||
+50
-32
@@ -8,10 +8,8 @@ https://docs.docker.com/reference/api/engine/
|
||||
|
||||
You use the library by constructing a client object using [New]
|
||||
and calling methods on it. The client can be configured from environment
|
||||
variables by passing the [FromEnv] option, and the [WithAPIVersionNegotiation]
|
||||
option to allow downgrading the API version used when connecting with an older
|
||||
daemon version. Other options cen be configured manually by passing any of
|
||||
the available [Opt] options.
|
||||
variables by passing the [FromEnv] option. Other options can be configured
|
||||
manually by passing any of the available [Opt] options.
|
||||
|
||||
For example, to list running containers (the equivalent of "docker ps"):
|
||||
|
||||
@@ -30,7 +28,7 @@ For example, to list running containers (the equivalent of "docker ps"):
|
||||
// for configuration (DOCKER_HOST, DOCKER_API_VERSION), and does
|
||||
// API-version negotiation to allow downgrading the API version
|
||||
// when connecting with an older daemon version.
|
||||
apiClient, err := client.New(client.FromEnv, client.WithAPIVersionNegotiation())
|
||||
apiClient, err := client.New(client.FromEnv)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -103,18 +101,16 @@ import (
|
||||
const DummyHost = "api.moby.localhost"
|
||||
|
||||
// MaxAPIVersion is the highest REST API version supported by the client.
|
||||
// If API-version negotiation is enabled (see [WithAPIVersionNegotiation],
|
||||
// [Client.NegotiateAPIVersion]), the client may downgrade its API version.
|
||||
// Similarly, the [WithVersion] and [WithVersionFromEnv] allow overriding
|
||||
// the version.
|
||||
// If API-version negotiation is enabled, the client may downgrade its API version.
|
||||
// Similarly, the [WithAPIVersion] and [WithAPIVersionFromEnv] options allow
|
||||
// overriding the version and disable API-version negotiation.
|
||||
//
|
||||
// This version may be lower than the version of the api library module used.
|
||||
const MaxAPIVersion = "1.52"
|
||||
|
||||
// fallbackAPIVersion is the version to fall back to if API-version negotiation
|
||||
// fails. API versions below this version are not supported by the client,
|
||||
// and not considered when negotiating.
|
||||
const fallbackAPIVersion = "1.44"
|
||||
// MinAPIVersion is the minimum API version supported by the client. API versions
|
||||
// below this version are not considered when performing API-version negotiation.
|
||||
const MinAPIVersion = "1.44"
|
||||
|
||||
// Ensure that Client always implements APIClient.
|
||||
var _ APIClient = &Client{}
|
||||
@@ -174,8 +170,13 @@ func NewClientWithOpts(ops ...Opt) (*Client, error) {
|
||||
// It takes an optional list of [Opt] functional arguments, which are applied in
|
||||
// the order they're provided, which allows modifying the defaults when creating
|
||||
// the client. For example, the following initializes a client that configures
|
||||
// itself with values from environment variables ([FromEnv]), and has automatic
|
||||
// API version negotiation enabled ([WithAPIVersionNegotiation]).
|
||||
// itself with values from environment variables ([FromEnv]).
|
||||
//
|
||||
// By default, the client automatically negotiates the API version to use when
|
||||
// making requests. API version negotiation is performed on the first request;
|
||||
// subsequent requests do not re-negotiate. Use [WithAPIVersion] or
|
||||
// [WithAPIVersionFromEnv] to configure the client with a fixed API version
|
||||
// and disable API version negotiation.
|
||||
//
|
||||
// cli, err := client.New(
|
||||
// client.FromEnv,
|
||||
@@ -213,6 +214,12 @@ func New(ops ...Opt) (*Client, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.envAPIVersion != "" {
|
||||
c.setAPIVersion(cfg.envAPIVersion)
|
||||
} else if cfg.manualAPIVersion != "" {
|
||||
c.setAPIVersion(cfg.manualAPIVersion)
|
||||
}
|
||||
|
||||
if tr, ok := c.client.Transport.(*http.Transport); ok {
|
||||
// Store the base transport before we wrap it in tracing libs below
|
||||
// This is used, as an example, to close idle connections when the client is closed
|
||||
@@ -278,7 +285,7 @@ func (cli *Client) Close() error {
|
||||
// be negotiated when making the actual requests, and for which cases
|
||||
// we cannot do the negotiation lazily.
|
||||
func (cli *Client) checkVersion(ctx context.Context) error {
|
||||
if cli.manualOverride || !cli.negotiateVersion || cli.negotiated.Load() {
|
||||
if cli.negotiated.Load() {
|
||||
return nil
|
||||
}
|
||||
_, err := cli.Ping(ctx, PingOptions{
|
||||
@@ -306,36 +313,47 @@ func (cli *Client) ClientVersion() string {
|
||||
}
|
||||
|
||||
// negotiateAPIVersion updates the version to match the API version from
|
||||
// the ping response. It falls back to the lowest version supported if the
|
||||
// API version is empty, or returns an error if the API version is lower than
|
||||
// the lowest supported API version, in which case the version is not modified.
|
||||
// the ping response.
|
||||
//
|
||||
// It returns an error if version is invalid, or lower than the minimum
|
||||
// supported API version in which case the client's API version is not
|
||||
// updated, and negotiation is not marked as completed.
|
||||
func (cli *Client) negotiateAPIVersion(pingVersion string) error {
|
||||
pingVersion = strings.TrimPrefix(pingVersion, "v")
|
||||
if pingVersion == "" {
|
||||
// TODO(thaJeztah): consider returning an error on empty value or not falling back; see https://github.com/moby/moby/pull/51119#discussion_r2413148487
|
||||
pingVersion = fallbackAPIVersion
|
||||
} else if versions.LessThan(pingVersion, fallbackAPIVersion) {
|
||||
return cerrdefs.ErrInvalidArgument.WithMessage(fmt.Sprintf("API version %s is not supported by this client: the minimum supported API version is %s", pingVersion, fallbackAPIVersion))
|
||||
var err error
|
||||
pingVersion, err = parseAPIVersion(pingVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if versions.LessThan(pingVersion, MinAPIVersion) {
|
||||
return cerrdefs.ErrInvalidArgument.WithMessage(fmt.Sprintf("API version %s is not supported by this client: the minimum supported API version is %s", pingVersion, MinAPIVersion))
|
||||
}
|
||||
|
||||
// if the client is not initialized with a version, start with the latest supported version
|
||||
if cli.version == "" {
|
||||
cli.version = MaxAPIVersion
|
||||
negotiatedVersion := cli.version
|
||||
if negotiatedVersion == "" {
|
||||
negotiatedVersion = MaxAPIVersion
|
||||
}
|
||||
|
||||
// if server version is lower than the client version, downgrade
|
||||
if versions.LessThan(pingVersion, cli.version) {
|
||||
cli.version = pingVersion
|
||||
if versions.LessThan(pingVersion, negotiatedVersion) {
|
||||
negotiatedVersion = pingVersion
|
||||
}
|
||||
|
||||
// Store the results, so that automatic API version negotiation (if enabled)
|
||||
// won't be performed on the next request.
|
||||
if cli.negotiateVersion {
|
||||
cli.negotiated.Store(true)
|
||||
}
|
||||
cli.setAPIVersion(negotiatedVersion)
|
||||
return nil
|
||||
}
|
||||
|
||||
// setAPIVersion sets the client's API version and marks API version negotiation
|
||||
// as completed, so that automatic API version negotiation (if enabled) won't
|
||||
// be performed on the next request.
|
||||
func (cli *Client) setAPIVersion(version string) {
|
||||
cli.version = version
|
||||
cli.negotiated.Store(true)
|
||||
}
|
||||
|
||||
// DaemonHost returns the host address used by the client
|
||||
func (cli *Client) DaemonHost() string {
|
||||
return cli.host
|
||||
|
||||
+11
@@ -49,6 +49,17 @@ type HijackDialer interface {
|
||||
DialHijack(ctx context.Context, url, proto string, meta map[string][]string) (net.Conn, error)
|
||||
}
|
||||
|
||||
// CheckpointAPIClient defines API client methods for the checkpoints.
|
||||
//
|
||||
// Experimental: checkpoint and restore is still an experimental feature,
|
||||
// and only available if the daemon is running with experimental features
|
||||
// enabled.
|
||||
type CheckpointAPIClient interface {
|
||||
CheckpointCreate(ctx context.Context, container string, options CheckpointCreateOptions) (CheckpointCreateResult, error)
|
||||
CheckpointRemove(ctx context.Context, container string, options CheckpointRemoveOptions) (CheckpointRemoveResult, error)
|
||||
CheckpointList(ctx context.Context, container string, options CheckpointListOptions) (CheckpointListResult, error)
|
||||
}
|
||||
|
||||
// ContainerAPIClient defines API client methods for the containers
|
||||
type ContainerAPIClient interface {
|
||||
ContainerCreate(ctx context.Context, options ContainerCreateOptions) (ContainerCreateResult, error)
|
||||
|
||||
vendor/github.com/moby/moby/client/options.go → vendor/github.com/moby/moby/client/client_options.go
Generated
Vendored
+73
-28
@@ -38,14 +38,22 @@ type clientConfig struct {
|
||||
userAgent *string
|
||||
// custom HTTP headers configured by users.
|
||||
customHTTPHeaders map[string]string
|
||||
// manualOverride is set to true when the version was set by users.
|
||||
manualOverride bool
|
||||
|
||||
// negotiateVersion indicates if the client should automatically negotiate
|
||||
// the API version to use when making requests. API version negotiation is
|
||||
// performed on the first request, after which negotiated is set to "true"
|
||||
// so that subsequent requests do not re-negotiate.
|
||||
negotiateVersion bool
|
||||
// manualAPIVersion contains the API version set by users. This field
|
||||
// will only be non-empty if a valid-formed version was set through
|
||||
// [WithAPIVersion].
|
||||
//
|
||||
// If both manualAPIVersion and envAPIVersion are set, manualAPIVersion
|
||||
// takes precedence. Either field disables API-version negotiation.
|
||||
manualAPIVersion string
|
||||
|
||||
// envAPIVersion contains the API version set by users. This field
|
||||
// will only be non-empty if a valid-formed version was set through
|
||||
// [WithAPIVersionFromEnv].
|
||||
//
|
||||
// If both manualAPIVersion and envAPIVersion are set, manualAPIVersion
|
||||
// takes precedence. Either field disables API-version negotiation.
|
||||
envAPIVersion string
|
||||
|
||||
// traceOpts is a list of options to configure the tracing span.
|
||||
traceOpts []otelhttp.Option
|
||||
@@ -56,7 +64,7 @@ type Opt func(*clientConfig) error
|
||||
|
||||
// FromEnv configures the client with values from environment variables. It
|
||||
// is the equivalent of using the [WithTLSClientConfigFromEnv], [WithHostFromEnv],
|
||||
// and [WithVersionFromEnv] options.
|
||||
// and [WithAPIVersionFromEnv] options.
|
||||
//
|
||||
// FromEnv uses the following environment variables:
|
||||
//
|
||||
@@ -71,7 +79,7 @@ func FromEnv(c *clientConfig) error {
|
||||
ops := []Opt{
|
||||
WithTLSClientConfigFromEnv(),
|
||||
WithHostFromEnv(),
|
||||
WithVersionFromEnv(),
|
||||
WithAPIVersionFromEnv(),
|
||||
}
|
||||
for _, op := range ops {
|
||||
if err := op(c); err != nil {
|
||||
@@ -241,18 +249,59 @@ func WithTLSClientConfigFromEnv() Opt {
|
||||
}
|
||||
}
|
||||
|
||||
// WithVersion overrides the client version with the specified one. If an empty
|
||||
// version is provided, the value is ignored to allow version negotiation
|
||||
// (see [WithAPIVersionNegotiation]).
|
||||
// WithAPIVersion overrides the client's API version with the specified one,
|
||||
// and disables API version negotiation. If an empty version is provided,
|
||||
// this option is ignored to allow version negotiation. The given version
|
||||
// should be formatted "<major>.<minor>" (for example, "1.52"). It returns
|
||||
// an error if the given value not in the correct format.
|
||||
//
|
||||
// WithVersion does not validate if the client supports the given version,
|
||||
// and callers should verify if the version is in the correct format and
|
||||
// lower than the maximum supported version as defined by [MaxAPIVersion].
|
||||
func WithVersion(version string) Opt {
|
||||
// WithAPIVersion does not validate if the client supports the given version,
|
||||
// and callers should verify if the version lower than the maximum supported
|
||||
// version as defined by [MaxAPIVersion].
|
||||
//
|
||||
// [WithAPIVersionFromEnv] takes precedence if [WithAPIVersion] and
|
||||
// [WithAPIVersionFromEnv] are both set.
|
||||
func WithAPIVersion(version string) Opt {
|
||||
return func(c *clientConfig) error {
|
||||
if v := strings.TrimPrefix(version, "v"); v != "" {
|
||||
c.version = v
|
||||
c.manualOverride = true
|
||||
version = strings.TrimSpace(version)
|
||||
if val := strings.TrimPrefix(version, "v"); val != "" {
|
||||
ver, err := parseAPIVersion(val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid API version (%s): %w", version, err)
|
||||
}
|
||||
c.manualAPIVersion = ver
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithVersion overrides the client version with the specified one.
|
||||
//
|
||||
// Deprecated: use [WithAPIVersion] instead.
|
||||
func WithVersion(version string) Opt {
|
||||
return WithAPIVersion(version)
|
||||
}
|
||||
|
||||
// WithAPIVersionFromEnv overrides the client version with the version specified in
|
||||
// the DOCKER_API_VERSION ([EnvOverrideAPIVersion]) environment variable.
|
||||
// If DOCKER_API_VERSION is not set, or set to an empty value, the version
|
||||
// is not modified.
|
||||
//
|
||||
// WithAPIVersion does not validate if the client supports the given version,
|
||||
// and callers should verify if the version lower than the maximum supported
|
||||
// version as defined by [MaxAPIVersion].
|
||||
//
|
||||
// [WithAPIVersionFromEnv] takes precedence if [WithAPIVersion] and
|
||||
// [WithAPIVersionFromEnv] are both set.
|
||||
func WithAPIVersionFromEnv() Opt {
|
||||
return func(c *clientConfig) error {
|
||||
version := strings.TrimSpace(os.Getenv(EnvOverrideAPIVersion))
|
||||
if val := strings.TrimPrefix(version, "v"); val != "" {
|
||||
ver, err := parseAPIVersion(val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid API version (%s): %w", version, err)
|
||||
}
|
||||
c.envAPIVersion = ver
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -260,25 +309,21 @@ func WithVersion(version string) Opt {
|
||||
|
||||
// WithVersionFromEnv overrides the client version with the version specified in
|
||||
// the DOCKER_API_VERSION ([EnvOverrideAPIVersion]) environment variable.
|
||||
// If DOCKER_API_VERSION is not set, or set to an empty value, the version
|
||||
// is not modified.
|
||||
//
|
||||
// WithVersion does not validate if the client supports the given version,
|
||||
// and callers should verify if the version is in the correct format and
|
||||
// lower than the maximum supported version as defined by [MaxAPIVersion].
|
||||
// Deprecated: use [WithAPIVersionFromEnv] instead.
|
||||
func WithVersionFromEnv() Opt {
|
||||
return func(c *clientConfig) error {
|
||||
return WithVersion(os.Getenv(EnvOverrideAPIVersion))(c)
|
||||
}
|
||||
return WithAPIVersionFromEnv()
|
||||
}
|
||||
|
||||
// WithAPIVersionNegotiation enables automatic API version negotiation for the client.
|
||||
// With this option enabled, the client automatically negotiates the API version
|
||||
// to use when making requests. API version negotiation is performed on the first
|
||||
// request; subsequent requests do not re-negotiate.
|
||||
//
|
||||
// Deprecated: API-version negotiation is now enabled by default. Use [WithAPIVersion]
|
||||
// or [WithAPIVersionFromEnv] to disable API version negotiation.
|
||||
func WithAPIVersionNegotiation() Opt {
|
||||
return func(c *clientConfig) error {
|
||||
c.negotiateVersion = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -13,7 +13,7 @@ const (
|
||||
// be used to override the API version to use. Value must be
|
||||
// formatted as MAJOR.MINOR, for example, "1.19".
|
||||
//
|
||||
// This env-var is read by [FromEnv] and [WithVersionFromEnv] and when set to a
|
||||
// This env-var is read by [FromEnv] and [WithAPIVersionFromEnv] and when set to a
|
||||
// non-empty value, takes precedence over API version negotiation.
|
||||
//
|
||||
// This environment variable should be used for debugging purposes only, as
|
||||
|
||||
+24
-9
@@ -20,7 +20,7 @@ type PingOptions struct {
|
||||
//
|
||||
// If a manual override is in place, either through the "DOCKER_API_VERSION"
|
||||
// ([EnvOverrideAPIVersion]) environment variable, or if the client is initialized
|
||||
// with a fixed version ([WithVersion]), no negotiation is performed.
|
||||
// with a fixed version ([WithAPIVersion]), no negotiation is performed.
|
||||
//
|
||||
// If the API server's ping response does not contain an API version, or if the
|
||||
// client did not get a successful ping response, it assumes it is connected with
|
||||
@@ -29,9 +29,8 @@ type PingOptions struct {
|
||||
NegotiateAPIVersion bool
|
||||
|
||||
// ForceNegotiate forces the client to re-negotiate the API version, even if
|
||||
// API-version negotiation already happened. This option cannot be
|
||||
// used if the client is configured with a fixed version using (using
|
||||
// [WithVersion] or [WithVersionFromEnv]).
|
||||
// API-version negotiation already happened or it the client is configured
|
||||
// with a fixed version (using [WithAPIVersion] or [WithAPIVersionFromEnv]).
|
||||
//
|
||||
// This option has no effect if NegotiateAPIVersion is not set.
|
||||
ForceNegotiate bool
|
||||
@@ -72,10 +71,12 @@ type SwarmStatus struct {
|
||||
// for other non-success status codes, failing to connect to the API, or failing
|
||||
// to parse the API response.
|
||||
func (cli *Client) Ping(ctx context.Context, options PingOptions) (PingResult, error) {
|
||||
if cli.manualOverride {
|
||||
if !options.NegotiateAPIVersion {
|
||||
// No API version negotiation needed; just return ping response.
|
||||
return cli.ping(ctx)
|
||||
}
|
||||
if !options.NegotiateAPIVersion && !cli.negotiateVersion {
|
||||
if cli.negotiated.Load() && !options.ForceNegotiate {
|
||||
// API version was already negotiated or manually set.
|
||||
return cli.ping(ctx)
|
||||
}
|
||||
|
||||
@@ -85,10 +86,19 @@ func (cli *Client) Ping(ctx context.Context, options PingOptions) (PingResult, e
|
||||
|
||||
ping, err := cli.ping(ctx)
|
||||
if err != nil {
|
||||
return cli.ping(ctx)
|
||||
return ping, err
|
||||
}
|
||||
|
||||
if cli.negotiated.Load() && !options.ForceNegotiate {
|
||||
// API version was already negotiated or manually set.
|
||||
//
|
||||
// We check cli.negotiated again under lock, to account for race
|
||||
// conditions with the check at the start of this function.
|
||||
return ping, nil
|
||||
}
|
||||
|
||||
if ping.APIVersion == "" {
|
||||
cli.setAPIVersion(MaxAPIVersion)
|
||||
return ping, nil
|
||||
}
|
||||
|
||||
@@ -112,10 +122,15 @@ func (cli *Client) ping(ctx context.Context) (PingResult, error) {
|
||||
// response-body to get error details from.
|
||||
return newPingResult(resp), nil
|
||||
}
|
||||
// close to allow reusing connection.
|
||||
ensureReaderClosed(resp)
|
||||
|
||||
// HEAD failed or returned a non-OK status; fallback to GET.
|
||||
req.Method = http.MethodGet
|
||||
resp, err = cli.doRequest(req)
|
||||
req2, err := cli.buildRequest(ctx, http.MethodGet, path.Join(cli.basePath, "/_ping"), nil, nil)
|
||||
if err != nil {
|
||||
return PingResult{}, err
|
||||
}
|
||||
resp, err = cli.doRequest(req2)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
// Failed to connect.
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ type PluginListOptions struct {
|
||||
|
||||
// PluginListResult represents the result of a plugin list operation.
|
||||
type PluginListResult struct {
|
||||
Items []*plugin.Plugin
|
||||
Items []plugin.Plugin
|
||||
}
|
||||
|
||||
// PluginList returns the installed plugins
|
||||
|
||||
+50
-34
@@ -67,31 +67,22 @@ func (cli *Client) delete(ctx context.Context, path string, query url.Values, he
|
||||
// prepareJSONRequest encodes the given body to JSON and returns it as an [io.Reader], and sets the Content-Type
|
||||
// header. If body is nil, or a nil-interface, a "nil" body is returned without
|
||||
// error.
|
||||
//
|
||||
// TODO(thaJeztah): should this return an error if a different Content-Type is already set?
|
||||
// TODO(thaJeztah): is "nil" the appropriate approach for an empty body, or should we use [http.NoBody] (or similar)?
|
||||
func prepareJSONRequest(body any, headers http.Header) (io.Reader, http.Header, error) {
|
||||
if body == nil {
|
||||
return nil, headers, nil
|
||||
}
|
||||
// encoding/json encodes a nil pointer as the JSON document `null`,
|
||||
// irrespective of whether the type implements json.Marshaler or encoding.TextMarshaler.
|
||||
// That is almost certainly not what the caller intended as the request body.
|
||||
//
|
||||
// TODO(thaJeztah): consider moving this to jsonEncode, which would also allow returning an (empty) reader instead of nil.
|
||||
if reflect.TypeOf(body).Kind() == reflect.Ptr && reflect.ValueOf(body).IsNil() {
|
||||
return nil, headers, nil
|
||||
}
|
||||
|
||||
jsonBody, err := jsonEncode(body)
|
||||
if err != nil {
|
||||
return nil, headers, err
|
||||
}
|
||||
if jsonBody == nil || jsonBody == http.NoBody {
|
||||
// no content-type is set on empty requests.
|
||||
return jsonBody, headers, nil
|
||||
}
|
||||
|
||||
hdr := http.Header{}
|
||||
if headers != nil {
|
||||
hdr = headers.Clone()
|
||||
}
|
||||
|
||||
// TODO(thaJeztah): should this return an error if a different Content-Type is already set?
|
||||
hdr.Set("Content-Type", "application/json")
|
||||
return jsonBody, hdr, nil
|
||||
}
|
||||
@@ -110,9 +101,6 @@ func (cli *Client) buildRequest(ctx context.Context, method, path string, body i
|
||||
req.Host = DummyHost
|
||||
}
|
||||
|
||||
if body != nil && req.Header.Get("Content-Type") == "" {
|
||||
req.Header.Set("Content-Type", "text/plain")
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
@@ -248,7 +236,11 @@ func checkResponseErr(serverResp *http.Response) (retErr error) {
|
||||
if statusMsg == "" {
|
||||
statusMsg = http.StatusText(serverResp.StatusCode)
|
||||
}
|
||||
if serverResp.Body != nil {
|
||||
var reqMethod string
|
||||
if serverResp.Request != nil {
|
||||
reqMethod = serverResp.Request.Method
|
||||
}
|
||||
if serverResp.Body != nil && reqMethod != http.MethodHead {
|
||||
bodyMax := 1 * 1024 * 1024 // 1 MiB
|
||||
bodyR := &io.LimitedReader{
|
||||
R: serverResp.Body,
|
||||
@@ -333,25 +325,49 @@ func (cli *Client) addHeaders(req *http.Request, headers http.Header) *http.Requ
|
||||
}
|
||||
|
||||
func jsonEncode(data any) (io.Reader, error) {
|
||||
var params bytes.Buffer
|
||||
if data != nil {
|
||||
if err := json.NewEncoder(¶ms).Encode(data); err != nil {
|
||||
return nil, err
|
||||
switch x := data.(type) {
|
||||
case nil:
|
||||
return http.NoBody, nil
|
||||
case io.Reader:
|
||||
// http.NoBody or other readers
|
||||
return x, nil
|
||||
case json.RawMessage:
|
||||
if len(x) == 0 {
|
||||
return http.NoBody, nil
|
||||
}
|
||||
return bytes.NewReader(x), nil
|
||||
}
|
||||
return ¶ms, nil
|
||||
|
||||
// encoding/json encodes a nil pointer as the JSON document `null`,
|
||||
// irrespective of whether the type implements json.Marshaler or encoding.TextMarshaler.
|
||||
// That is almost certainly not what the caller intended as the request body.
|
||||
if v := reflect.ValueOf(data); v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
return http.NoBody, nil
|
||||
}
|
||||
|
||||
b, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bytes.NewReader(b), nil
|
||||
}
|
||||
|
||||
func ensureReaderClosed(response *http.Response) {
|
||||
if response != nil && response.Body != nil {
|
||||
// Drain up to 512 bytes and close the body to let the Transport reuse the connection
|
||||
// see https://github.com/google/go-github/pull/317/files#r57536827
|
||||
//
|
||||
// TODO(thaJeztah): see if this optimization is still needed, or already implemented in stdlib,
|
||||
// and check if context-cancellation should handle this as well. If still needed, consider
|
||||
// wrapping response.Body, or returning a "closer()" from [Client.sendRequest] and related
|
||||
// methods.
|
||||
_, _ = io.CopyN(io.Discard, response.Body, 512)
|
||||
_ = response.Body.Close()
|
||||
if response == nil || response.Body == nil {
|
||||
return
|
||||
}
|
||||
if response.ContentLength == 0 || (response.Request != nil && response.Request.Method == http.MethodHead) {
|
||||
// No need to drain head requests or zero-length responses.
|
||||
_ = response.Body.Close()
|
||||
return
|
||||
}
|
||||
// Drain up to 512 bytes and close the body to let the Transport reuse the connection
|
||||
// see https://github.com/google/go-github/pull/317/files#r57536827
|
||||
//
|
||||
// TODO(thaJeztah): see if this optimization is still needed, or already implemented in stdlib,
|
||||
// and check if context-cancellation should handle this as well. If still needed, consider
|
||||
// wrapping response.Body, or returning a "closer()" from [Client.sendRequest] and related
|
||||
// methods.
|
||||
_, _ = io.CopyN(io.Discard, response.Body, 512)
|
||||
_ = response.Body.Close()
|
||||
}
|
||||
|
||||
+28
-28
@@ -59,16 +59,18 @@ func (cli *Client) ServiceCreate(ctx context.Context, options ServiceCreateOptio
|
||||
options.Spec.TaskTemplate.ContainerSpec.Image = taggedImg
|
||||
}
|
||||
if options.QueryRegistry {
|
||||
resolveWarning := resolveContainerSpecImage(ctx, cli, &options.Spec.TaskTemplate, options.EncodedRegistryAuth)
|
||||
warnings = append(warnings, resolveWarning)
|
||||
if warning := resolveContainerSpecImage(ctx, cli, &options.Spec.TaskTemplate, options.EncodedRegistryAuth); warning != "" {
|
||||
warnings = append(warnings, warning)
|
||||
}
|
||||
}
|
||||
case options.Spec.TaskTemplate.PluginSpec != nil:
|
||||
if taggedImg := imageWithTagString(options.Spec.TaskTemplate.PluginSpec.Remote); taggedImg != "" {
|
||||
options.Spec.TaskTemplate.PluginSpec.Remote = taggedImg
|
||||
}
|
||||
if options.QueryRegistry {
|
||||
resolveWarning := resolvePluginSpecRemote(ctx, cli, &options.Spec.TaskTemplate, options.EncodedRegistryAuth)
|
||||
warnings = append(warnings, resolveWarning)
|
||||
if warning := resolvePluginSpecRemote(ctx, cli, &options.Spec.TaskTemplate, options.EncodedRegistryAuth); warning != "" {
|
||||
warnings = append(warnings, warning)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,35 +95,33 @@ func (cli *Client) ServiceCreate(ctx context.Context, options ServiceCreateOptio
|
||||
}
|
||||
|
||||
func resolveContainerSpecImage(ctx context.Context, cli DistributionAPIClient, taskSpec *swarm.TaskSpec, encodedAuth string) string {
|
||||
var warning string
|
||||
if img, imgPlatforms, err := imageDigestAndPlatforms(ctx, cli, taskSpec.ContainerSpec.Image, encodedAuth); err != nil {
|
||||
warning = digestWarning(taskSpec.ContainerSpec.Image)
|
||||
} else {
|
||||
taskSpec.ContainerSpec.Image = img
|
||||
if len(imgPlatforms) > 0 {
|
||||
if taskSpec.Placement == nil {
|
||||
taskSpec.Placement = &swarm.Placement{}
|
||||
}
|
||||
taskSpec.Placement.Platforms = imgPlatforms
|
||||
}
|
||||
img, imgPlatforms, err := imageDigestAndPlatforms(ctx, cli, taskSpec.ContainerSpec.Image, encodedAuth)
|
||||
if err != nil {
|
||||
return digestWarning(taskSpec.ContainerSpec.Image)
|
||||
}
|
||||
return warning
|
||||
taskSpec.ContainerSpec.Image = img
|
||||
if len(imgPlatforms) > 0 {
|
||||
if taskSpec.Placement == nil {
|
||||
taskSpec.Placement = &swarm.Placement{}
|
||||
}
|
||||
taskSpec.Placement.Platforms = imgPlatforms
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func resolvePluginSpecRemote(ctx context.Context, cli DistributionAPIClient, taskSpec *swarm.TaskSpec, encodedAuth string) string {
|
||||
var warning string
|
||||
if img, imgPlatforms, err := imageDigestAndPlatforms(ctx, cli, taskSpec.PluginSpec.Remote, encodedAuth); err != nil {
|
||||
warning = digestWarning(taskSpec.PluginSpec.Remote)
|
||||
} else {
|
||||
taskSpec.PluginSpec.Remote = img
|
||||
if len(imgPlatforms) > 0 {
|
||||
if taskSpec.Placement == nil {
|
||||
taskSpec.Placement = &swarm.Placement{}
|
||||
}
|
||||
taskSpec.Placement.Platforms = imgPlatforms
|
||||
}
|
||||
img, imgPlatforms, err := imageDigestAndPlatforms(ctx, cli, taskSpec.PluginSpec.Remote, encodedAuth)
|
||||
if err != nil {
|
||||
return digestWarning(taskSpec.PluginSpec.Remote)
|
||||
}
|
||||
return warning
|
||||
taskSpec.PluginSpec.Remote = img
|
||||
if len(imgPlatforms) > 0 {
|
||||
if taskSpec.Placement == nil {
|
||||
taskSpec.Placement = &swarm.Placement{}
|
||||
}
|
||||
taskSpec.Placement.Platforms = imgPlatforms
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func imageDigestAndPlatforms(ctx context.Context, cli DistributionAPIClient, image, encodedAuth string) (string, []swarm.Platform, error) {
|
||||
|
||||
+6
-4
@@ -82,16 +82,18 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, options
|
||||
options.Spec.TaskTemplate.ContainerSpec.Image = taggedImg
|
||||
}
|
||||
if options.QueryRegistry {
|
||||
resolveWarning := resolveContainerSpecImage(ctx, cli, &options.Spec.TaskTemplate, options.EncodedRegistryAuth)
|
||||
warnings = append(warnings, resolveWarning)
|
||||
if warning := resolveContainerSpecImage(ctx, cli, &options.Spec.TaskTemplate, options.EncodedRegistryAuth); warning != "" {
|
||||
warnings = append(warnings, warning)
|
||||
}
|
||||
}
|
||||
case options.Spec.TaskTemplate.PluginSpec != nil:
|
||||
if taggedImg := imageWithTagString(options.Spec.TaskTemplate.PluginSpec.Remote); taggedImg != "" {
|
||||
options.Spec.TaskTemplate.PluginSpec.Remote = taggedImg
|
||||
}
|
||||
if options.QueryRegistry {
|
||||
resolveWarning := resolvePluginSpecRemote(ctx, cli, &options.Spec.TaskTemplate, options.EncodedRegistryAuth)
|
||||
warnings = append(warnings, resolveWarning)
|
||||
if warning := resolvePluginSpecRemote(ctx, cli, &options.Spec.TaskTemplate, options.EncodedRegistryAuth); warning != "" {
|
||||
warnings = append(warnings, warning)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+27
-14
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/moby/moby/api/types/build"
|
||||
"github.com/moby/moby/api/types/container"
|
||||
@@ -149,14 +148,19 @@ func (cli *Client) DiskUsage(ctx context.Context, options DiskUsageOptions) (Dis
|
||||
return DiskUsageResult{}, err
|
||||
}
|
||||
|
||||
var du system.DiskUsage
|
||||
if err := json.NewDecoder(resp.Body).Decode(&du); err != nil {
|
||||
return DiskUsageResult{}, fmt.Errorf("Error retrieving disk usage: %v", err)
|
||||
if versions.LessThan(cli.version, "1.52") {
|
||||
// Generate result from a legacy response.
|
||||
var du legacyDiskUsage
|
||||
if err := json.NewDecoder(resp.Body).Decode(&du); err != nil {
|
||||
return DiskUsageResult{}, fmt.Errorf("retrieving disk usage: %v", err)
|
||||
}
|
||||
|
||||
return diskUsageResultFromLegacyAPI(&du), nil
|
||||
}
|
||||
|
||||
// Generate result from a legacy response.
|
||||
if versions.LessThan(cli.version, "1.52") {
|
||||
return diskUsageResultFromLegacyAPI(&du), nil
|
||||
var du system.DiskUsage
|
||||
if err := json.NewDecoder(resp.Body).Decode(&du); err != nil {
|
||||
return DiskUsageResult{}, fmt.Errorf("retrieving disk usage: %v", err)
|
||||
}
|
||||
|
||||
var r DiskUsageResult
|
||||
@@ -215,7 +219,16 @@ func (cli *Client) DiskUsage(ctx context.Context, options DiskUsageOptions) (Dis
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func diskUsageResultFromLegacyAPI(du *system.DiskUsage) DiskUsageResult {
|
||||
// legacyDiskUsage is the response as was used by API < v1.52.
|
||||
type legacyDiskUsage struct {
|
||||
LayersSize int64 `json:"LayersSize,omitempty"`
|
||||
Images []image.Summary `json:"Images,omitzero"`
|
||||
Containers []container.Summary `json:"Containers,omitzero"`
|
||||
Volumes []volume.Volume `json:"Volumes,omitzero"`
|
||||
BuildCache []build.CacheRecord `json:"BuildCache,omitzero"`
|
||||
}
|
||||
|
||||
func diskUsageResultFromLegacyAPI(du *legacyDiskUsage) DiskUsageResult {
|
||||
return DiskUsageResult{
|
||||
Images: imageDiskUsageFromLegacyAPI(du),
|
||||
Containers: containerDiskUsageFromLegacyAPI(du),
|
||||
@@ -224,7 +237,7 @@ func diskUsageResultFromLegacyAPI(du *system.DiskUsage) DiskUsageResult {
|
||||
}
|
||||
}
|
||||
|
||||
func imageDiskUsageFromLegacyAPI(du *system.DiskUsage) ImagesDiskUsage {
|
||||
func imageDiskUsageFromLegacyAPI(du *legacyDiskUsage) ImagesDiskUsage {
|
||||
idu := ImagesDiskUsage{
|
||||
TotalSize: du.LayersSize,
|
||||
TotalCount: int64(len(du.Images)),
|
||||
@@ -250,7 +263,7 @@ func imageDiskUsageFromLegacyAPI(du *system.DiskUsage) ImagesDiskUsage {
|
||||
return idu
|
||||
}
|
||||
|
||||
func containerDiskUsageFromLegacyAPI(du *system.DiskUsage) ContainersDiskUsage {
|
||||
func containerDiskUsageFromLegacyAPI(du *legacyDiskUsage) ContainersDiskUsage {
|
||||
cdu := ContainersDiskUsage{
|
||||
TotalCount: int64(len(du.Containers)),
|
||||
Items: du.Containers,
|
||||
@@ -259,8 +272,8 @@ func containerDiskUsageFromLegacyAPI(du *system.DiskUsage) ContainersDiskUsage {
|
||||
var used int64
|
||||
for _, c := range cdu.Items {
|
||||
cdu.TotalSize += c.SizeRw
|
||||
switch strings.ToLower(c.State) {
|
||||
case "running", "paused", "restarting":
|
||||
switch c.State {
|
||||
case container.StateRunning, container.StatePaused, container.StateRestarting:
|
||||
cdu.ActiveCount++
|
||||
used += c.SizeRw
|
||||
}
|
||||
@@ -270,7 +283,7 @@ func containerDiskUsageFromLegacyAPI(du *system.DiskUsage) ContainersDiskUsage {
|
||||
return cdu
|
||||
}
|
||||
|
||||
func buildCacheDiskUsageFromLegacyAPI(du *system.DiskUsage) BuildCacheDiskUsage {
|
||||
func buildCacheDiskUsageFromLegacyAPI(du *legacyDiskUsage) BuildCacheDiskUsage {
|
||||
bdu := BuildCacheDiskUsage{
|
||||
TotalCount: int64(len(du.BuildCache)),
|
||||
Items: du.BuildCache,
|
||||
@@ -294,7 +307,7 @@ func buildCacheDiskUsageFromLegacyAPI(du *system.DiskUsage) BuildCacheDiskUsage
|
||||
return bdu
|
||||
}
|
||||
|
||||
func volumeDiskUsageFromLegacyAPI(du *system.DiskUsage) VolumesDiskUsage {
|
||||
func volumeDiskUsageFromLegacyAPI(du *legacyDiskUsage) VolumesDiskUsage {
|
||||
vdu := VolumesDiskUsage{
|
||||
TotalCount: int64(len(du.Volumes)),
|
||||
Items: du.Volumes,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user