mirror of
https://github.com/docker/cli.git
synced 2026-08-26 10:05:28 -05:00
Compare commits
@@ -4,9 +4,6 @@ https://github.com/docker/cli/blob/master/CONTRIBUTING.md
|
||||
|
||||
** Make sure all your commits include a signature generated with `git commit -s` **
|
||||
|
||||
For additional information on our contributing process, read our contributing
|
||||
guide https://docs.docker.com/opensource/code/
|
||||
|
||||
If this is a bug fix, make sure your description includes "fixes #xxxx", or
|
||||
"closes #xxxx"
|
||||
|
||||
@@ -20,6 +17,7 @@ Provide the following information:
|
||||
**- How to verify it**
|
||||
|
||||
**- Human readable description for the release notes**
|
||||
|
||||
<!--
|
||||
Write a short (one line) summary that describes the changes in this
|
||||
pull request for inclusion in the changelog.
|
||||
@@ -28,10 +26,9 @@ It must be placed inside the below triple backticks section.
|
||||
NOTE: Only fill this section if changes introduced in this PR are user-facing.
|
||||
The PR must have a relevant impact/ label.
|
||||
-->
|
||||
```markdown changelog
|
||||
|
||||
```markdown changelog
|
||||
|
||||
```
|
||||
|
||||
**- A picture of a cute animal (not mandatory but encouraged)**
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
|
||||
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
set: |
|
||||
@@ -88,7 +88,7 @@ jobs:
|
||||
fi
|
||||
-
|
||||
name: Upload artifacts
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_NAME }}
|
||||
path: /tmp/out/*
|
||||
@@ -101,7 +101,7 @@ jobs:
|
||||
-
|
||||
name: Login to DockerHub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_CLIBIN_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_CLIBIN_TOKEN }}
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
-
|
||||
name: Build and push image
|
||||
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
|
||||
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
|
||||
with:
|
||||
files: |
|
||||
./docker-bake.hcl
|
||||
@@ -168,7 +168,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
|
||||
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
|
||||
with:
|
||||
targets: plugins-cross
|
||||
set: |
|
||||
|
||||
@@ -63,18 +63,18 @@ jobs:
|
||||
name: Update Go
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
|
||||
with:
|
||||
go-version: "1.26.1"
|
||||
go-version: "1.26.3"
|
||||
cache: false
|
||||
-
|
||||
name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||
uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
|
||||
with:
|
||||
languages: go
|
||||
-
|
||||
name: Autobuild
|
||||
uses: github/codeql-action/autobuild@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||
uses: github/codeql-action/autobuild@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
|
||||
-
|
||||
name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@38697555549f1db7851b81482ff19f1fa5c4fedc # v4.34.1
|
||||
uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
|
||||
with:
|
||||
category: "/language:go"
|
||||
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||
-
|
||||
name: Test
|
||||
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
|
||||
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
|
||||
with:
|
||||
targets: test-coverage
|
||||
-
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
|
||||
with:
|
||||
go-version: "1.26.1"
|
||||
go-version: "1.26.3"
|
||||
cache: false
|
||||
-
|
||||
name: Test
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
name: validate-milestone
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, milestoned, demilestoned, edited]
|
||||
|
||||
jobs:
|
||||
validate-milestone:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
sparse-checkout: VERSION
|
||||
|
||||
- name: Validate milestone matches VERSION
|
||||
run: |
|
||||
expected=$(cat VERSION)
|
||||
milestone="${{ github.event.pull_request.milestone.title }}"
|
||||
|
||||
if [[ -z "$milestone" ]]; then
|
||||
echo "::error::PR must have a milestone set (expected: $expected)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$milestone" != "$expected" ]]; then
|
||||
echo "::error::Milestone '$milestone' does not match VERSION '$expected'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Milestone: $milestone ✓"
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Run
|
||||
uses: docker/bake-action@82490499d2e5613fcead7e128237ef0b0ea210f7 # v7
|
||||
uses: docker/bake-action@a66e1c87e2eca0503c343edf1d208c716d54b8a8 # v7.1.0
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6
|
||||
with:
|
||||
go-version: "1.26.1"
|
||||
go-version: "1.26.3"
|
||||
cache: false
|
||||
-
|
||||
name: Run gocompat check
|
||||
|
||||
+8
-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.26.1"
|
||||
go: "1.26.3"
|
||||
|
||||
timeout: 5m
|
||||
|
||||
@@ -110,8 +110,15 @@ linters:
|
||||
excludes:
|
||||
- G104 # G104: Errors unhandled; (TODO: reduce unhandled errors, or explicitly ignore)
|
||||
- G115 # G115: integer overflow conversion; (TODO: verify these: https://github.com/docker/cli/issues/5584)
|
||||
- G117 # G117: Exported struct field matches secret pattern (false positives for legitimate field names)
|
||||
- G118 # G118: Goroutine uses context.Background/TODO while request-scoped context is available (TODO: evaluate these)
|
||||
- G122 # G122: Filesystem operation in filepath.Walk/WalkDir callback uses race-prone path (TODO: evaluate these)
|
||||
- G306 # G306: Expect WriteFile permissions to be 0600 or less (too restrictive; also flags "0o644" permissions)
|
||||
- G307 # G307: Deferring unsafe method "*os.File" on type "Close" (also EXC0008); (TODO: evaluate these and fix where needed: G307: Deferring unsafe method "*os.File" on type "Close")
|
||||
- G702 # G702: Command injection via taint analysis (TODO: evaluate these)
|
||||
- G703 # G703: Path traversal via taint analysis (TODO: evaluate these)
|
||||
- G704 # G704: SSRF via taint analysis (TODO: evaluate these)
|
||||
- G705 # G705: XSS via taint analysis (TODO: evaluate these)
|
||||
|
||||
govet:
|
||||
enable:
|
||||
|
||||
+8
-5
@@ -66,7 +66,7 @@ anybody starts working on it.
|
||||
We are always thrilled to receive pull requests. We do our best to process them
|
||||
quickly. If your pull request is not accepted on the first try,
|
||||
don't get discouraged! Our contributor's guide explains [the review process we
|
||||
use for simple changes](https://github.com/docker/docker/blob/master/project/REVIEWING.md).
|
||||
use for simple changes](https://github.com/moby/moby/blob/master/project/REVIEWING.md).
|
||||
|
||||
### Talking to other Docker users and contributors
|
||||
|
||||
@@ -124,8 +124,7 @@ submitting a pull request.
|
||||
Update the documentation when creating or modifying features. Test your
|
||||
documentation changes for clarity, concision, and correctness, as well as a
|
||||
clean documentation build. See our contributors guide for [our style
|
||||
guide](https://docs.docker.com/contribute/style/grammar/) and instructions on [building
|
||||
the documentation](https://docs.docker.com/contribute/).
|
||||
guide](https://github.com/docker/docs/blob/main/STYLE.md).
|
||||
|
||||
Write clean code. Universally formatted code promotes ease of writing, reading,
|
||||
and maintenance. Always run `gofmt -s -w file.go` on each changed file before
|
||||
@@ -145,6 +144,7 @@ not enforced. Common prefixes are `docs: <message>`, `vendor: <message>`,
|
||||
or `telemetry: <message>`.
|
||||
|
||||
A standard commit.
|
||||
|
||||
```
|
||||
Fix the exploding flux capacitor
|
||||
|
||||
@@ -153,6 +153,7 @@ the sun and the moon align.
|
||||
```
|
||||
|
||||
Using a package as prefix.
|
||||
|
||||
```
|
||||
pkg/foo: prevent panic in flux capacitor
|
||||
|
||||
@@ -160,12 +161,14 @@ Calling function A causes the flux capacitor to blow up every time
|
||||
the sun and the moon align.
|
||||
```
|
||||
|
||||
Updating a specific vendored package.
|
||||
Updating a specific vendored dependency.
|
||||
|
||||
```
|
||||
vendor: github.com/docker/docker 6ac445c42bad (master, v28.0-dev)
|
||||
vendor: github.com/moby/moby/client v0.4.0
|
||||
```
|
||||
|
||||
Fixing a broken docs link.
|
||||
|
||||
```
|
||||
docs: fix style/lint issues in deprecated.md
|
||||
```
|
||||
|
||||
+3
-3
@@ -8,7 +8,7 @@ ARG BASE_VARIANT=alpine
|
||||
ARG ALPINE_VERSION=3.23
|
||||
ARG BASE_DEBIAN_DISTRO=bookworm
|
||||
|
||||
ARG GO_VERSION=1.26.1
|
||||
ARG GO_VERSION=1.26.3
|
||||
|
||||
# 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.
|
||||
@@ -25,12 +25,12 @@ ARG GOTESTSUM_VERSION=v1.13.0
|
||||
# BUILDX_VERSION sets the version of buildx to use for the e2e tests.
|
||||
# It must be a tag in the docker.io/docker/buildx-bin image repository
|
||||
# on Docker Hub.
|
||||
ARG BUILDX_VERSION=0.31.1
|
||||
ARG BUILDX_VERSION=0.33.0
|
||||
|
||||
# COMPOSE_VERSION is the version of compose to install in the dev container.
|
||||
# It must be a tag in the docker.io/docker/compose-bin image repository
|
||||
# on Docker Hub.
|
||||
ARG COMPOSE_VERSION=v5.1.0
|
||||
ARG COMPOSE_VERSION=v5.1.3
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
||||
|
||||
@@ -51,6 +51,18 @@ Run test:
|
||||
docker buildx bake test
|
||||
```
|
||||
|
||||
Run the unit test:
|
||||
|
||||
```
|
||||
$ make -f docker.Makefile test-unit
|
||||
```
|
||||
|
||||
Run the full test suite:
|
||||
|
||||
```
|
||||
$ make -f docker.Makefile test
|
||||
```
|
||||
|
||||
List all the available targets:
|
||||
|
||||
```shell
|
||||
@@ -62,7 +74,7 @@ make help
|
||||
Start an interactive development environment:
|
||||
|
||||
```shell
|
||||
make -f docker.Makefile shell
|
||||
make shell
|
||||
```
|
||||
|
||||
## Legal
|
||||
|
||||
@@ -13,6 +13,8 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const maxMessages = 10
|
||||
|
||||
func ParseTemplate(hookTemplate string, cmd *cobra.Command) ([]string, error) {
|
||||
out := hookTemplate
|
||||
if strings.Contains(hookTemplate, "{{") {
|
||||
@@ -38,7 +40,10 @@ func ParseTemplate(hookTemplate string, cmd *cobra.Command) ([]string, error) {
|
||||
}
|
||||
out = b.String()
|
||||
}
|
||||
return strings.Split(out, "\n"), nil
|
||||
if n := strings.Count(out, "\n"); n > maxMessages {
|
||||
return nil, fmt.Errorf("hook template contains too many messages (%d): maximum is %d", n, maxMessages)
|
||||
}
|
||||
return strings.SplitN(out, "\n", maxMessages), nil
|
||||
}
|
||||
|
||||
var ErrHookTemplateParse = errors.New("failed to parse hook template")
|
||||
|
||||
@@ -177,9 +177,8 @@ func TestGetPluginDirs(t *testing.T) {
|
||||
pluginDirs := getPluginDirs(cli.ConfigFile())
|
||||
assert.Equal(t, strings.Join(expected, ":"), strings.Join(pluginDirs, ":"))
|
||||
|
||||
extras := []string{
|
||||
"foo", "bar", "baz",
|
||||
}
|
||||
extras := make([]string, 0, 3+len(expected))
|
||||
extras = append(extras, "foo", "bar", "baz")
|
||||
expected = append(extras, expected...)
|
||||
cli.SetConfigFile(&configfile.ConfigFile{
|
||||
CLIPluginsExtraDirs: extras,
|
||||
|
||||
+3
-2
@@ -566,11 +566,12 @@ type ServerInfo struct {
|
||||
// It applies by default the standard streams, and the content trust from
|
||||
// environment.
|
||||
func NewDockerCli(ops ...CLIOption) (*DockerCli, error) {
|
||||
defaultOps := []CLIOption{
|
||||
defaultOps := make([]CLIOption, 0, 3+len(ops))
|
||||
defaultOps = append(defaultOps,
|
||||
WithDefaultContextStoreConfig(),
|
||||
WithStandardStreams(),
|
||||
WithUserAgent(UserAgent()),
|
||||
}
|
||||
)
|
||||
ops = append(defaultOps, ops...)
|
||||
|
||||
cli := &DockerCli{baseCtx: context.Background()}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.25
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -102,6 +106,7 @@ func (c *configContext) Labels() string {
|
||||
for k, v := range mapLabels {
|
||||
joinLabels = append(joinLabels, k+"="+v)
|
||||
}
|
||||
slices.Sort(joinLabels)
|
||||
return strings.Join(joinLabels, ",")
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ container source to stdout.`,
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.followLink, "follow-link", "L", false, "Always follow symbol link in SRC_PATH")
|
||||
flags.BoolVarP(&opts.followLink, "follow-link", "L", false, "Always follow symlinks in SRC_PATH")
|
||||
flags.BoolVarP(&opts.copyUIDGID, "archive", "a", false, "Archive mode (copy all uid/gid information)")
|
||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress progress output during copy. Progress output is automatically suppressed if no terminal is attached")
|
||||
return cmd
|
||||
@@ -271,7 +271,7 @@ func copyFromContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cp
|
||||
}
|
||||
|
||||
apiClient := dockerCLI.Client()
|
||||
// if client requests to follow symbol link, then must decide target file to be copied
|
||||
// if client requests to follow symlinks, then must decide target file to be copied
|
||||
var rebaseName string
|
||||
if copyConfig.followLink {
|
||||
src, err := apiClient.ContainerStatPath(ctx, copyConfig.container, client.ContainerStatPathOptions{
|
||||
|
||||
@@ -371,7 +371,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
|
||||
var binds []string
|
||||
volumes := copts.volumes.GetMap()
|
||||
// add any bind targets to the list of container volumes
|
||||
for bind := range copts.volumes.GetMap() {
|
||||
for bind := range volumes {
|
||||
parsed, err := volumespec.Parse(bind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -506,13 +506,13 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
|
||||
// collect all the environment variables for the container
|
||||
envVariables, err := opts.ReadKVEnvStrings(copts.envFile.GetSlice(), copts.env.GetSlice())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("--env-file: %w", err)
|
||||
}
|
||||
|
||||
// collect all the labels for the container
|
||||
labels, err := opts.ReadKVStrings(copts.labelsFile.GetSlice(), copts.labels.GetSlice())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("--label-file: %w", err)
|
||||
}
|
||||
|
||||
pidMode := container.PidMode(copts.pidMode)
|
||||
|
||||
@@ -829,13 +829,6 @@ func TestParseRestartPolicy(t *testing.T) {
|
||||
Name: container.RestartPolicyAlways,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "always:1",
|
||||
expected: container.RestartPolicy{
|
||||
Name: container.RestartPolicyAlways,
|
||||
MaximumRetryCount: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "always:2:3",
|
||||
expectedErr: "invalid restart policy format: maximum retry count must be an integer",
|
||||
@@ -861,6 +854,16 @@ func TestParseRestartPolicy(t *testing.T) {
|
||||
input: "unless-stopped:invalid",
|
||||
expectedErr: "invalid restart policy format: maximum retry count must be an integer",
|
||||
},
|
||||
|
||||
// Unknown / invalid combinations: validation is handled by the daemon>
|
||||
{
|
||||
input: "anything:123",
|
||||
expected: container.RestartPolicy{Name: "anything", MaximumRetryCount: 123},
|
||||
},
|
||||
{
|
||||
input: "negative:-123",
|
||||
expected: container.RestartPolicy{Name: "negative", MaximumRetryCount: -123},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.input, func(t *testing.T) {
|
||||
@@ -937,13 +940,13 @@ func TestParseLoggingOpts(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseEnvfileVariables(t *testing.T) {
|
||||
e := "open nonexistent: no such file or directory"
|
||||
expErr := "--env-file: open nonexistent: no such file or directory"
|
||||
if runtime.GOOS == "windows" {
|
||||
e = "open nonexistent: The system cannot find the file specified."
|
||||
expErr = "--env-file: open nonexistent: The system cannot find the file specified."
|
||||
}
|
||||
// env ko
|
||||
if _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
||||
t.Fatalf("Expected an error with message '%s', got %v", e, err)
|
||||
if _, _, _, err := parseRun([]string{"--env-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != expErr {
|
||||
t.Fatalf("Expected an error with message '%s', got %v", expErr, err)
|
||||
}
|
||||
// env ok
|
||||
config, _, _, err := parseRun([]string{"--env-file=testdata/valid.env", "img", "cmd"})
|
||||
@@ -990,13 +993,13 @@ func TestParseEnvfileVariablesWithBOMUnicode(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseLabelfileVariables(t *testing.T) {
|
||||
e := "open nonexistent: no such file or directory"
|
||||
expErr := "--label-file: open nonexistent: no such file or directory"
|
||||
if runtime.GOOS == "windows" {
|
||||
e = "open nonexistent: The system cannot find the file specified."
|
||||
expErr = "--label-file: open nonexistent: The system cannot find the file specified."
|
||||
}
|
||||
// label ko
|
||||
if _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != e {
|
||||
t.Fatalf("Expected an error with message '%s', got %v", e, err)
|
||||
if _, _, _, err := parseRun([]string{"--label-file=nonexistent", "img", "cmd"}); err == nil || err.Error() != expErr {
|
||||
t.Fatalf("Expected an error with message '%s', got %v", expErr, err)
|
||||
}
|
||||
// label ok
|
||||
config, _, _, err := parseRun([]string{"--label-file=testdata/valid.label", "img", "cmd"})
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
func newUseCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "use CONTEXT",
|
||||
Short: "Set the current docker context",
|
||||
Short: "Set the default docker context",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
name := args[0]
|
||||
|
||||
@@ -21,13 +21,14 @@ import (
|
||||
const (
|
||||
defaultContainerTableFormat = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.RunningFor}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}"
|
||||
|
||||
namesHeader = "NAMES"
|
||||
commandHeader = "COMMAND"
|
||||
runningForHeader = "CREATED"
|
||||
mountsHeader = "MOUNTS"
|
||||
localVolumes = "LOCAL VOLUMES"
|
||||
networksHeader = "NETWORKS"
|
||||
platformHeader = "PLATFORM"
|
||||
namesHeader = "NAMES"
|
||||
commandHeader = "COMMAND"
|
||||
runningForHeader = "CREATED"
|
||||
mountsHeader = "MOUNTS"
|
||||
localVolumes = "LOCAL VOLUMES"
|
||||
networksHeader = "NETWORKS"
|
||||
platformHeader = "PLATFORM"
|
||||
healthStatusHeader = "HEALTH STATUS"
|
||||
)
|
||||
|
||||
// Platform wraps a [ocispec.Platform] to implement the stringer interface.
|
||||
@@ -121,6 +122,7 @@ func NewContainerContext() *ContainerContext {
|
||||
"LocalVolumes": localVolumes,
|
||||
"Networks": networksHeader,
|
||||
"Platform": platformHeader,
|
||||
"HealthStatus": healthStatusHeader,
|
||||
}
|
||||
return &containerCtx
|
||||
}
|
||||
@@ -352,6 +354,35 @@ func (c *ContainerContext) Networks() string {
|
||||
return strings.Join(networks, ",")
|
||||
}
|
||||
|
||||
// HealthStatus returns the container's health status (for example, "healthy","unhealthy", or "starting").
|
||||
// If no healthcheck is configured, an empty
|
||||
// string is returned.
|
||||
func (c *ContainerContext) HealthStatus() string {
|
||||
if c.c.Health != nil && c.c.Health.Status != "" {
|
||||
return string(c.c.Health.Status)
|
||||
}
|
||||
|
||||
// Fallback for API versions before v1.52, which include health only in Status text;
|
||||
// see https://github.com/moby/moby/pull/50281
|
||||
// see https://github.com/moby/moby/blob/docker-v29.4.3/daemon/container/health.go#L18-L43
|
||||
_, health, ok := strings.Cut(c.c.Status, "(")
|
||||
if !ok || !strings.HasSuffix(health, ")") {
|
||||
return ""
|
||||
}
|
||||
|
||||
health = strings.TrimSuffix(health, ")")
|
||||
health = strings.TrimPrefix(health, "health: ")
|
||||
|
||||
switch container.HealthStatus(health) {
|
||||
case container.Healthy, container.Unhealthy, container.Starting:
|
||||
return health
|
||||
case container.NoHealthcheck:
|
||||
return ""
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// DisplayablePorts returns formatted string representing open ports of container
|
||||
// e.g. "0.0.0.0:80->9090/tcp, 9988/tcp"
|
||||
// it's used by command 'docker ps'
|
||||
|
||||
@@ -494,6 +494,7 @@ func TestContainerContextWriteJSON(t *testing.T) {
|
||||
{
|
||||
"Command": `""`,
|
||||
"CreatedAt": expectedCreated,
|
||||
"HealthStatus": "",
|
||||
"ID": "containerID1",
|
||||
"Image": "ubuntu",
|
||||
"Labels": "",
|
||||
@@ -511,6 +512,7 @@ func TestContainerContextWriteJSON(t *testing.T) {
|
||||
{
|
||||
"Command": `""`,
|
||||
"CreatedAt": expectedCreated,
|
||||
"HealthStatus": "",
|
||||
"ID": "containerID2",
|
||||
"Image": "ubuntu",
|
||||
"Labels": "",
|
||||
@@ -528,6 +530,7 @@ func TestContainerContextWriteJSON(t *testing.T) {
|
||||
{
|
||||
"Command": `""`,
|
||||
"CreatedAt": expectedCreated,
|
||||
"HealthStatus": "",
|
||||
"ID": "containerID3",
|
||||
"Image": "ubuntu",
|
||||
"Labels": "",
|
||||
@@ -615,6 +618,7 @@ func TestContainerBackCompat(t *testing.T) {
|
||||
{field: "Image", expected: "docker.io/library/ubuntu"},
|
||||
{field: "Command", expected: `"/bin/sh"`},
|
||||
{field: "CreatedAt", expected: time.Unix(createdAtTime.Unix(), 0).String()},
|
||||
{field: "HealthStatus", expected: ""},
|
||||
{field: "RunningFor", expected: "12 months ago"},
|
||||
{field: "Ports", expected: "8080/tcp"},
|
||||
{field: "Status", expected: "running"},
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.25
|
||||
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -104,6 +108,7 @@ func (c *volumeContext) Labels() string {
|
||||
for k, v := range c.v.Labels {
|
||||
joinLabels = append(joinLabels, k+"="+v)
|
||||
}
|
||||
slices.Sort(joinLabels)
|
||||
return strings.Join(joinLabels, ",")
|
||||
}
|
||||
|
||||
|
||||
@@ -48,9 +48,7 @@ func TestVolumeContext(t *testing.T) {
|
||||
for _, c := range cases {
|
||||
ctx = c.volumeCtx
|
||||
v := c.call()
|
||||
if strings.Contains(v, ",") {
|
||||
test.CompareMultipleValues(t, v, c.expValue)
|
||||
} else if v != c.expValue {
|
||||
if v != c.expValue {
|
||||
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +25,6 @@ import (
|
||||
"github.com/moby/patternmatcher"
|
||||
)
|
||||
|
||||
// DefaultDockerfileName is the Default filename with Docker commands, read by docker build
|
||||
//
|
||||
// Deprecated: this const is no longer used and will be removed in the next release.
|
||||
const DefaultDockerfileName string = "Dockerfile"
|
||||
|
||||
const (
|
||||
// defaultDockerfileName is the Default filename with Docker commands, read by docker build
|
||||
defaultDockerfileName string = "Dockerfile"
|
||||
@@ -100,17 +95,6 @@ func filepathMatches(matcher *patternmatcher.PatternMatcher, file string) (bool,
|
||||
return matcher.MatchesOrParentMatches(file)
|
||||
}
|
||||
|
||||
// DetectArchiveReader detects whether the input stream is an archive or a
|
||||
// Dockerfile and returns a buffered version of input, safe to consume in lieu
|
||||
// of input. If an archive is detected, ok is set to true, and to false
|
||||
// otherwise, in which case it is safe to assume input represents the contents
|
||||
// of a Dockerfile.
|
||||
//
|
||||
// Deprecated: this utility was only used internally, and will be removed in the next release.
|
||||
func DetectArchiveReader(input io.ReadCloser) (rc io.ReadCloser, ok bool, err error) {
|
||||
return detectArchiveReader(input)
|
||||
}
|
||||
|
||||
// detectArchiveReader detects whether the input stream is an archive or a
|
||||
// Dockerfile and returns a buffered version of input, safe to consume in lieu
|
||||
// of input. If an archive is detected, ok is set to true, and to false
|
||||
@@ -127,15 +111,6 @@ func detectArchiveReader(input io.ReadCloser) (rc io.ReadCloser, ok bool, err er
|
||||
return newReadCloserWrapper(buf, func() error { return input.Close() }), isArchive(magic), nil
|
||||
}
|
||||
|
||||
// WriteTempDockerfile writes a Dockerfile stream to a temporary file with a
|
||||
// name specified by defaultDockerfileName and returns the path to the
|
||||
// temporary directory containing the Dockerfile.
|
||||
//
|
||||
// Deprecated: this utility was only used internally, and will be removed in the next release.
|
||||
func WriteTempDockerfile(rc io.ReadCloser) (dockerfileDir string, err error) {
|
||||
return writeTempDockerfile(rc)
|
||||
}
|
||||
|
||||
// writeTempDockerfile writes a Dockerfile stream to a temporary file with a
|
||||
// name specified by defaultDockerfileName and returns the path to the
|
||||
// temporary directory containing the Dockerfile.
|
||||
@@ -201,14 +176,6 @@ func GetContextFromReader(rc io.ReadCloser, dockerfileName string) (out io.ReadC
|
||||
}), defaultDockerfileName, nil
|
||||
}
|
||||
|
||||
// IsArchive checks for the magic bytes of a tar or any supported compression
|
||||
// algorithm.
|
||||
//
|
||||
// Deprecated: this utility was used internally and will be removed in the next release.
|
||||
func IsArchive(header []byte) bool {
|
||||
return isArchive(header)
|
||||
}
|
||||
|
||||
// isArchive checks for the magic bytes of a tar or any supported compression
|
||||
// algorithm.
|
||||
func isArchive(header []byte) bool {
|
||||
@@ -305,17 +272,6 @@ func GetContextFromLocalDir(localDir, dockerfileName string) (string, string, er
|
||||
return localDir, relDockerfile, err
|
||||
}
|
||||
|
||||
// ResolveAndValidateContextPath uses the given context directory for a `docker build`
|
||||
// and returns the absolute path to the context directory.
|
||||
//
|
||||
// Deprecated: this utility was used internally and will be removed in the next
|
||||
// release. Use [DetectContextType] to detect the context-type, and use
|
||||
// [GetContextFromLocalDir], [GetContextFromLocalDir], [GetContextFromGitURL],
|
||||
// or [GetContextFromURL] instead.
|
||||
func ResolveAndValidateContextPath(givenContextDir string) (string, error) {
|
||||
return resolveAndValidateContextPath(givenContextDir)
|
||||
}
|
||||
|
||||
// resolveAndValidateContextPath uses the given context directory for a `docker build`
|
||||
// and returns the absolute path to the context directory.
|
||||
func resolveAndValidateContextPath(givenContextDir string) (string, error) {
|
||||
|
||||
@@ -211,6 +211,6 @@ func printAmbiguousHint(stdErr io.Writer, matchName string) {
|
||||
"save",
|
||||
"tag":
|
||||
|
||||
_, _ = fmt.Fprintf(stdErr, "\nNo images found matching %q: did you mean \"docker image %[1]s\"?\n", matchName)
|
||||
_, _ = fmt.Fprintf(stdErr, "No images found matching %q: did you mean \"docker image %[1]s\"?\n", matchName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
WARNING: This output is designed for human readability. For machine-readable output, please use --format.
|
||||
|
||||
No images found matching "ls": did you mean "docker image ls"?
|
||||
|
||||
@@ -6,14 +6,12 @@ package image
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/cli/internal/tui"
|
||||
"github.com/docker/go-units"
|
||||
imagetypes "github.com/moby/moby/api/types/image"
|
||||
@@ -241,10 +239,6 @@ func getPossibleChips(view treeView) (chips []imageChip) {
|
||||
}
|
||||
|
||||
func printImageTree(outs command.Streams, view treeView) {
|
||||
if streamRedirected(outs.Out()) {
|
||||
_, _ = fmt.Fprintln(outs.Err(), "WARNING: This output is designed for human readability. For machine-readable output, please use --format.")
|
||||
}
|
||||
|
||||
out := tui.NewOutput(outs.Out())
|
||||
isTerm := out.IsTerminal()
|
||||
|
||||
@@ -569,17 +563,3 @@ func widestFirstColumnValue(headers []imgColumn, images []topImage) int {
|
||||
}
|
||||
return width
|
||||
}
|
||||
|
||||
func streamRedirected(s *streams.Out) bool {
|
||||
fd := s.FD()
|
||||
if os.Stdout.Fd() != fd {
|
||||
return true
|
||||
}
|
||||
|
||||
fi, err := os.Stdout.Stat()
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return fi.Mode()&os.ModeCharDevice == 0
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.25
|
||||
|
||||
package network
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -115,6 +119,7 @@ func (c *networkContext) Labels() string {
|
||||
for k, v := range c.n.Labels {
|
||||
joinLabels = append(joinLabels, k+"="+v)
|
||||
}
|
||||
slices.Sort(joinLabels)
|
||||
return strings.Join(joinLabels, ",")
|
||||
}
|
||||
|
||||
|
||||
@@ -68,9 +68,7 @@ func TestNetworkContext(t *testing.T) {
|
||||
for _, c := range cases {
|
||||
ctx = c.networkCtx
|
||||
v := c.call()
|
||||
if strings.Contains(v, ",") {
|
||||
test.CompareMultipleValues(t, v, c.expValue)
|
||||
} else if v != c.expValue {
|
||||
if v != c.expValue {
|
||||
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-17
@@ -194,8 +194,7 @@ func RetrieveAuthTokenFromImage(cfg *configfile.ConfigFile, image string) (strin
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
configKey := getAuthConfigKey(reference.Domain(registryRef))
|
||||
authConfig, err := cfg.GetAuthConfig(configKey)
|
||||
authConfig, err := cfg.GetAuthConfig(reference.Domain(registryRef))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -211,18 +210,3 @@ func RetrieveAuthTokenFromImage(cfg *configfile.ConfigFile, image string) (strin
|
||||
RegistryToken: authConfig.RegistryToken,
|
||||
})
|
||||
}
|
||||
|
||||
// getAuthConfigKey special-cases using the full index address of the official
|
||||
// index as the AuthConfig key, and uses the (host)name[:port] for private indexes.
|
||||
//
|
||||
// It is similar to [registry.GetAuthConfigKey], but does not require on
|
||||
// [registrytypes.IndexInfo] as intermediate.
|
||||
//
|
||||
// [registry.GetAuthConfigKey]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/registry#GetAuthConfigKey
|
||||
// [registrytypes.IndexInfo]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/api/types/registry#IndexInfo
|
||||
func getAuthConfigKey(domainName string) string {
|
||||
if domainName == "docker.io" || domainName == "index.docker.io" {
|
||||
return authConfigKey
|
||||
}
|
||||
return domainName
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.25
|
||||
|
||||
package secret
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -109,6 +113,7 @@ func (c *secretContext) Labels() string {
|
||||
for k, v := range mapLabels {
|
||||
joinLabels = append(joinLabels, k+"="+v)
|
||||
}
|
||||
slices.Sort(joinLabels)
|
||||
return strings.Join(joinLabels, ",")
|
||||
}
|
||||
|
||||
|
||||
@@ -85,8 +85,7 @@ func Service(
|
||||
return swarm.ServiceSpec{}, err
|
||||
}
|
||||
|
||||
restartPolicy, err := convertRestartPolicy(
|
||||
service.Restart, service.Deploy.RestartPolicy)
|
||||
restartPolicy, err := convertRestartPolicy(service.Restart, service.Deploy.RestartPolicy)
|
||||
if err != nil {
|
||||
return swarm.ServiceSpec{}, err
|
||||
}
|
||||
@@ -472,37 +471,58 @@ func convertHealthcheck(healthcheck *composetypes.HealthCheckConfig) (*container
|
||||
}, nil
|
||||
}
|
||||
|
||||
func convertRestartPolicy(restart string, source *composetypes.RestartPolicy) (*swarm.RestartPolicy, error) {
|
||||
// TODO: log if restart is being ignored
|
||||
if source == nil {
|
||||
policy, err := opts.ParseRestartPolicy(restart)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch {
|
||||
case policy.IsNone():
|
||||
return nil, nil
|
||||
case policy.IsAlways(), policy.IsUnlessStopped():
|
||||
return &swarm.RestartPolicy{
|
||||
Condition: swarm.RestartPolicyConditionAny,
|
||||
}, nil
|
||||
case policy.IsOnFailure():
|
||||
attempts := uint64(policy.MaximumRetryCount)
|
||||
return &swarm.RestartPolicy{
|
||||
Condition: swarm.RestartPolicyConditionOnFailure,
|
||||
MaxAttempts: &attempts,
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown restart policy: %s", restart)
|
||||
}
|
||||
// convertRestartPolicy converts the service's restart-policy. It prefers
|
||||
// service.deploy.restart_policy, but falls back to parsing the legacy
|
||||
// service.restart if service.deploy.restart_policy is not set.
|
||||
func convertRestartPolicy(restart string, restartPolicy *composetypes.RestartPolicy) (*swarm.RestartPolicy, error) {
|
||||
// Use service.deploy.restart_policy, if set.
|
||||
if restartPolicy != nil {
|
||||
// TODO: log or error if both "service.restart" and "service.deploy.restartpolicy" are set.
|
||||
return &swarm.RestartPolicy{
|
||||
Condition: swarm.RestartPolicyCondition(restartPolicy.Condition),
|
||||
Delay: composetypes.ConvertDurationPtr(restartPolicy.Delay),
|
||||
MaxAttempts: restartPolicy.MaxAttempts,
|
||||
Window: composetypes.ConvertDurationPtr(restartPolicy.Window),
|
||||
}, nil
|
||||
}
|
||||
if restart == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &swarm.RestartPolicy{
|
||||
Condition: swarm.RestartPolicyCondition(source.Condition),
|
||||
Delay: composetypes.ConvertDurationPtr(source.Delay),
|
||||
MaxAttempts: source.MaxAttempts,
|
||||
Window: composetypes.ConvertDurationPtr(source.Window),
|
||||
}, nil
|
||||
// Fall back to the legacy service.restart restart-policy.
|
||||
policy, err := opts.ParseRestartPolicy(restart)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if policy.MaximumRetryCount < 0 {
|
||||
return nil, errors.New("invalid restart policy: maximum retry count cannot be negative")
|
||||
}
|
||||
uint64Ptr := func(i int) *uint64 {
|
||||
if i <= 0 {
|
||||
return nil
|
||||
}
|
||||
p := uint64(i)
|
||||
return &p
|
||||
}
|
||||
|
||||
switch policy.Name {
|
||||
case container.RestartPolicyDisabled, "":
|
||||
return nil, nil
|
||||
case container.RestartPolicyAlways, container.RestartPolicyUnlessStopped:
|
||||
return &swarm.RestartPolicy{
|
||||
Condition: swarm.RestartPolicyConditionAny,
|
||||
MaxAttempts: uint64Ptr(policy.MaximumRetryCount),
|
||||
}, nil
|
||||
case container.RestartPolicyOnFailure:
|
||||
return &swarm.RestartPolicy{
|
||||
Condition: swarm.RestartPolicyConditionOnFailure,
|
||||
MaxAttempts: uint64Ptr(policy.MaximumRetryCount),
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid restart policy: unknown policy '%s' (must be one of '%s', '%s', '%s', or '%s')",
|
||||
policy.Name, container.RestartPolicyDisabled, container.RestartPolicyAlways, container.RestartPolicyOnFailure, container.RestartPolicyUnlessStopped,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func convertUpdateConfig(source *composetypes.UpdateConfig) *swarm.UpdateConfig {
|
||||
|
||||
@@ -18,35 +18,52 @@ import (
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestConvertRestartPolicyFromNone(t *testing.T) {
|
||||
policy, err := convertRestartPolicy("no", nil)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual((*swarm.RestartPolicy)(nil), policy))
|
||||
}
|
||||
|
||||
func TestConvertRestartPolicyFromUnknown(t *testing.T) {
|
||||
_, err := convertRestartPolicy("unknown", nil)
|
||||
assert.Error(t, err, "unknown restart policy: unknown")
|
||||
}
|
||||
|
||||
func TestConvertRestartPolicyFromAlways(t *testing.T) {
|
||||
policy, err := convertRestartPolicy("always", nil)
|
||||
expected := &swarm.RestartPolicy{
|
||||
Condition: swarm.RestartPolicyConditionAny,
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, policy))
|
||||
}
|
||||
|
||||
func TestConvertRestartPolicyFromFailure(t *testing.T) {
|
||||
policy, err := convertRestartPolicy("on-failure:4", nil)
|
||||
func TestConvertRestartPolicy(t *testing.T) {
|
||||
attempts := uint64(4)
|
||||
expected := &swarm.RestartPolicy{
|
||||
Condition: swarm.RestartPolicyConditionOnFailure,
|
||||
MaxAttempts: &attempts,
|
||||
tests := []struct {
|
||||
input string
|
||||
expected *swarm.RestartPolicy
|
||||
expError string
|
||||
}{
|
||||
{},
|
||||
{
|
||||
input: "no",
|
||||
},
|
||||
{
|
||||
input: "unknown",
|
||||
expError: "invalid restart policy: unknown policy 'unknown'",
|
||||
},
|
||||
{
|
||||
input: "always",
|
||||
expected: &swarm.RestartPolicy{
|
||||
Condition: swarm.RestartPolicyConditionAny,
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "on-failure:4",
|
||||
expected: &swarm.RestartPolicy{
|
||||
Condition: swarm.RestartPolicyConditionOnFailure,
|
||||
MaxAttempts: &attempts,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
name := tc.input
|
||||
if name == "" {
|
||||
name = "empty"
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
policy, err := convertRestartPolicy(tc.input, nil)
|
||||
if tc.expError != "" {
|
||||
assert.Check(t, is.ErrorContains(err, tc.expError))
|
||||
assert.Check(t, is.Nil(policy))
|
||||
return
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(policy, tc.expected))
|
||||
})
|
||||
}
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.DeepEqual(expected, policy))
|
||||
}
|
||||
|
||||
func strPtr(val string) *string {
|
||||
|
||||
@@ -117,20 +117,21 @@ func TestValidatePorts(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
config := dict{
|
||||
"version": "3.0",
|
||||
"services": dict{
|
||||
"foo": dict{
|
||||
"image": "busybox",
|
||||
"ports": tc.ports,
|
||||
t.Run(fmt.Sprint(tc.ports), func(t *testing.T) {
|
||||
config := dict{
|
||||
"services": dict{
|
||||
"foo": dict{
|
||||
"image": "busybox",
|
||||
"ports": tc.ports,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if tc.hasError {
|
||||
assert.ErrorContains(t, Validate(config, "3"), "services.foo.ports.0 Does not match format 'ports'")
|
||||
} else {
|
||||
assert.NilError(t, Validate(config, "3"))
|
||||
}
|
||||
}
|
||||
if tc.hasError {
|
||||
assert.ErrorContains(t, Validate(config, "3"), "services.foo.ports.0 Does not match format 'ports'")
|
||||
} else {
|
||||
assert.NilError(t, Validate(config, "3"))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,34 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// authConfigKey is the key used to store credentials for Docker Hub. It is
|
||||
// a copy of [registry.IndexServer].
|
||||
//
|
||||
// [registry.IndexServer]: https://pkg.go.dev/github.com/docker/docker@v28.5.1+incompatible/registry#IndexServer
|
||||
const authConfigKey = "https://index.docker.io/v1/"
|
||||
|
||||
// getAuthConfigKey returns the canonical key used to look up stored
|
||||
// registry credentials for the given registry domain.
|
||||
//
|
||||
// For the official Docker Hub registry ("docker.io"), credentials are stored
|
||||
// under the historical full index address ("https://index.docker.io/v1/").
|
||||
//
|
||||
// For all other registries, the input is domainName to already be a normalized
|
||||
// hostname (optionally including ":port") and is returned unchanged.
|
||||
//
|
||||
// This function performs key normalization only; it does not validate or parse
|
||||
// the input.
|
||||
//
|
||||
// It is similar to [registry.GetAuthConfigKey] in the daemon.
|
||||
//
|
||||
// [registry.GetAuthConfigKey]: https://pkg.go.dev/github.com/docker/docker@v28.5.1+incompatible/registry#GetAuthConfigKey
|
||||
func getAuthConfigKey(domainName string) string {
|
||||
if domainName == "docker.io" || domainName == "index.docker.io" {
|
||||
return authConfigKey
|
||||
}
|
||||
return domainName
|
||||
}
|
||||
|
||||
// ConfigFile ~/.docker/config.json file info
|
||||
type ConfigFile struct {
|
||||
AuthConfigs map[string]types.AuthConfig `json:"auths"`
|
||||
@@ -96,12 +124,12 @@ func New(fn string) *ConfigFile {
|
||||
|
||||
// LoadFromReader reads the configuration data given and sets up the auth config
|
||||
// information with given directory and populates the receiver object
|
||||
func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
|
||||
if err := json.NewDecoder(configData).Decode(configFile); err != nil && !errors.Is(err, io.EOF) {
|
||||
func (c *ConfigFile) LoadFromReader(configData io.Reader) error {
|
||||
if err := json.NewDecoder(configData).Decode(c); err != nil && !errors.Is(err, io.EOF) {
|
||||
return err
|
||||
}
|
||||
var err error
|
||||
for addr, ac := range configFile.AuthConfigs {
|
||||
for addr, ac := range c.AuthConfigs {
|
||||
if ac.Auth != "" {
|
||||
ac.Username, ac.Password, err = decodeAuth(ac.Auth)
|
||||
if err != nil {
|
||||
@@ -110,33 +138,33 @@ func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
|
||||
}
|
||||
ac.Auth = ""
|
||||
ac.ServerAddress = addr
|
||||
configFile.AuthConfigs[addr] = ac
|
||||
c.AuthConfigs[addr] = ac
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContainsAuth returns whether there is authentication configured
|
||||
// in this file or not.
|
||||
func (configFile *ConfigFile) ContainsAuth() bool {
|
||||
return configFile.CredentialsStore != "" ||
|
||||
len(configFile.CredentialHelpers) > 0 ||
|
||||
len(configFile.AuthConfigs) > 0
|
||||
func (c *ConfigFile) ContainsAuth() bool {
|
||||
return c.CredentialsStore != "" ||
|
||||
len(c.CredentialHelpers) > 0 ||
|
||||
len(c.AuthConfigs) > 0
|
||||
}
|
||||
|
||||
// GetAuthConfigs returns the mapping of repo to auth configuration
|
||||
func (configFile *ConfigFile) GetAuthConfigs() map[string]types.AuthConfig {
|
||||
if configFile.AuthConfigs == nil {
|
||||
configFile.AuthConfigs = make(map[string]types.AuthConfig)
|
||||
func (c *ConfigFile) GetAuthConfigs() map[string]types.AuthConfig {
|
||||
if c.AuthConfigs == nil {
|
||||
c.AuthConfigs = make(map[string]types.AuthConfig)
|
||||
}
|
||||
return configFile.AuthConfigs
|
||||
return c.AuthConfigs
|
||||
}
|
||||
|
||||
// SaveToWriter encodes and writes out all the authorization information to
|
||||
// the given writer
|
||||
func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
|
||||
func (c *ConfigFile) SaveToWriter(writer io.Writer) error {
|
||||
// Encode sensitive data into a new/temp struct
|
||||
tmpAuthConfigs := make(map[string]types.AuthConfig, len(configFile.AuthConfigs))
|
||||
for k, authConfig := range configFile.AuthConfigs {
|
||||
tmpAuthConfigs := make(map[string]types.AuthConfig, len(c.AuthConfigs))
|
||||
for k, authConfig := range c.AuthConfigs {
|
||||
authCopy := authConfig
|
||||
// encode and save the authstring, while blanking out the original fields
|
||||
authCopy.Auth = encodeAuth(&authCopy)
|
||||
@@ -146,18 +174,18 @@ func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
|
||||
tmpAuthConfigs[k] = authCopy
|
||||
}
|
||||
|
||||
saveAuthConfigs := configFile.AuthConfigs
|
||||
configFile.AuthConfigs = tmpAuthConfigs
|
||||
defer func() { configFile.AuthConfigs = saveAuthConfigs }()
|
||||
saveAuthConfigs := c.AuthConfigs
|
||||
c.AuthConfigs = tmpAuthConfigs
|
||||
defer func() { c.AuthConfigs = saveAuthConfigs }()
|
||||
|
||||
// User-Agent header is automatically set, and should not be stored in the configuration
|
||||
for v := range configFile.HTTPHeaders {
|
||||
for v := range c.HTTPHeaders {
|
||||
if strings.EqualFold(v, "User-Agent") {
|
||||
delete(configFile.HTTPHeaders, v)
|
||||
delete(c.HTTPHeaders, v)
|
||||
}
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(configFile, "", "\t")
|
||||
data, err := json.MarshalIndent(c, "", "\t")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -166,16 +194,16 @@ func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
|
||||
}
|
||||
|
||||
// Save encodes and writes out all the authorization information
|
||||
func (configFile *ConfigFile) Save() (retErr error) {
|
||||
if configFile.Filename == "" {
|
||||
func (c *ConfigFile) Save() (retErr error) {
|
||||
if c.Filename == "" {
|
||||
return errors.New("can't save config with empty filename")
|
||||
}
|
||||
|
||||
dir := filepath.Dir(configFile.Filename)
|
||||
dir := filepath.Dir(c.Filename)
|
||||
if err := os.MkdirAll(dir, 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
temp, err := os.CreateTemp(dir, filepath.Base(configFile.Filename))
|
||||
temp, err := os.CreateTemp(dir, filepath.Base(c.Filename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -189,7 +217,7 @@ func (configFile *ConfigFile) Save() (retErr error) {
|
||||
}
|
||||
}()
|
||||
|
||||
err = configFile.SaveToWriter(temp)
|
||||
err = c.SaveToWriter(temp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -199,7 +227,7 @@ func (configFile *ConfigFile) Save() (retErr error) {
|
||||
}
|
||||
|
||||
// Handle situation where the configfile is a symlink, and allow for dangling symlinks
|
||||
cfgFile := configFile.Filename
|
||||
cfgFile := c.Filename
|
||||
if f, err := filepath.EvalSymlinks(cfgFile); err == nil {
|
||||
cfgFile = f
|
||||
} else if os.IsNotExist(err) {
|
||||
@@ -217,16 +245,16 @@ func (configFile *ConfigFile) Save() (retErr error) {
|
||||
|
||||
// ParseProxyConfig computes proxy configuration by retrieving the config for the provided host and
|
||||
// then checking this against any environment variables provided to the container
|
||||
func (configFile *ConfigFile) ParseProxyConfig(host string, runOpts map[string]*string) map[string]*string {
|
||||
func (c *ConfigFile) ParseProxyConfig(host string, runOpts map[string]*string) map[string]*string {
|
||||
var cfgKey string
|
||||
|
||||
if _, ok := configFile.Proxies[host]; !ok {
|
||||
if _, ok := c.Proxies[host]; !ok {
|
||||
cfgKey = "default"
|
||||
} else {
|
||||
cfgKey = host
|
||||
}
|
||||
|
||||
config := configFile.Proxies[cfgKey]
|
||||
config := c.Proxies[cfgKey]
|
||||
permitted := map[string]*string{
|
||||
"HTTP_PROXY": &config.HTTPProxy,
|
||||
"HTTPS_PROXY": &config.HTTPSProxy,
|
||||
@@ -290,11 +318,11 @@ func decodeAuth(authStr string) (string, string, error) {
|
||||
|
||||
// GetCredentialsStore returns a new credentials store from the settings in the
|
||||
// configuration file
|
||||
func (configFile *ConfigFile) GetCredentialsStore(registryHostname string) credentials.Store {
|
||||
store := credentials.NewFileStore(configFile)
|
||||
func (c *ConfigFile) GetCredentialsStore(registryHostname string) credentials.Store {
|
||||
store := credentials.NewFileStore(c)
|
||||
|
||||
if helper := getConfiguredCredentialStore(configFile, registryHostname); helper != "" {
|
||||
store = newNativeStore(configFile, helper)
|
||||
if helper := getConfiguredCredentialStore(c, getAuthConfigKey(registryHostname)); helper != "" {
|
||||
store = newNativeStore(c, helper)
|
||||
}
|
||||
|
||||
envConfig := os.Getenv(DockerEnvConfigKey)
|
||||
@@ -357,8 +385,9 @@ var newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentia
|
||||
}
|
||||
|
||||
// GetAuthConfig for a repository from the credential store
|
||||
func (configFile *ConfigFile) GetAuthConfig(registryHostname string) (types.AuthConfig, error) {
|
||||
return configFile.GetCredentialsStore(registryHostname).Get(registryHostname)
|
||||
func (c *ConfigFile) GetAuthConfig(registryHostname string) (types.AuthConfig, error) {
|
||||
acKey := getAuthConfigKey(registryHostname)
|
||||
return c.GetCredentialsStore(acKey).Get(acKey)
|
||||
}
|
||||
|
||||
// getConfiguredCredentialStore returns the credential helper configured for the
|
||||
@@ -375,13 +404,13 @@ func getConfiguredCredentialStore(c *ConfigFile, registryHostname string) string
|
||||
|
||||
// GetAllCredentials returns all of the credentials stored in all of the
|
||||
// configured credential stores.
|
||||
func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig, error) {
|
||||
func (c *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig, error) {
|
||||
auths := make(map[string]types.AuthConfig)
|
||||
addAll := func(from map[string]types.AuthConfig) {
|
||||
maps.Copy(auths, from)
|
||||
}
|
||||
|
||||
defaultStore := configFile.GetCredentialsStore("")
|
||||
defaultStore := c.GetCredentialsStore("")
|
||||
newAuths, err := defaultStore.GetAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -389,8 +418,8 @@ func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig,
|
||||
addAll(newAuths)
|
||||
|
||||
// Auth configs from a registry-specific helper should override those from the default store.
|
||||
for registryHostname := range configFile.CredentialHelpers {
|
||||
newAuth, err := configFile.GetAuthConfig(registryHostname)
|
||||
for registryHostname := range c.CredentialHelpers {
|
||||
newAuth, err := c.GetAuthConfig(registryHostname)
|
||||
if err != nil {
|
||||
// TODO(thaJeztah): use context-logger, so that this output can be suppressed (in tests).
|
||||
logrus.WithError(err).Warnf("Failed to get credentials for registry: %s", registryHostname)
|
||||
@@ -402,16 +431,16 @@ func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig,
|
||||
}
|
||||
|
||||
// GetFilename returns the file name that this config file is based on.
|
||||
func (configFile *ConfigFile) GetFilename() string {
|
||||
return configFile.Filename
|
||||
func (c *ConfigFile) GetFilename() string {
|
||||
return c.Filename
|
||||
}
|
||||
|
||||
// PluginConfig retrieves the requested option for the given plugin.
|
||||
func (configFile *ConfigFile) PluginConfig(pluginname, option string) (string, bool) {
|
||||
if configFile.Plugins == nil {
|
||||
func (c *ConfigFile) PluginConfig(pluginname, option string) (string, bool) {
|
||||
if c.Plugins == nil {
|
||||
return "", false
|
||||
}
|
||||
pluginConfig, ok := configFile.Plugins[pluginname]
|
||||
pluginConfig, ok := c.Plugins[pluginname]
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
@@ -423,14 +452,14 @@ func (configFile *ConfigFile) PluginConfig(pluginname, option string) (string, b
|
||||
// plugin. Passing a value of "" will remove the option. If removing
|
||||
// the final config item for a given plugin then also cleans up the
|
||||
// overall plugin entry.
|
||||
func (configFile *ConfigFile) SetPluginConfig(pluginname, option, value string) {
|
||||
if configFile.Plugins == nil {
|
||||
configFile.Plugins = make(map[string]map[string]string)
|
||||
func (c *ConfigFile) SetPluginConfig(pluginname, option, value string) {
|
||||
if c.Plugins == nil {
|
||||
c.Plugins = make(map[string]map[string]string)
|
||||
}
|
||||
pluginConfig, ok := configFile.Plugins[pluginname]
|
||||
pluginConfig, ok := c.Plugins[pluginname]
|
||||
if !ok {
|
||||
pluginConfig = make(map[string]string)
|
||||
configFile.Plugins[pluginname] = pluginConfig
|
||||
c.Plugins[pluginname] = pluginConfig
|
||||
}
|
||||
if value != "" {
|
||||
pluginConfig[option] = value
|
||||
@@ -438,6 +467,6 @@ func (configFile *ConfigFile) SetPluginConfig(pluginname, option, value string)
|
||||
delete(pluginConfig, option)
|
||||
}
|
||||
if len(pluginConfig) == 0 {
|
||||
delete(configFile.Plugins, pluginname)
|
||||
delete(c.Plugins, pluginname)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,19 @@ package credentials
|
||||
|
||||
import "os/exec"
|
||||
|
||||
// DetectDefaultStore return the default credentials store for the platform if
|
||||
// no user-defined store is passed, and the store executable is available.
|
||||
func DetectDefaultStore(store string) string {
|
||||
if store != "" {
|
||||
// DetectDefaultStore returns the credentials store to use if no user-defined
|
||||
// custom helper is passed.
|
||||
//
|
||||
// Some platforms define a preferred helper, in which case it attempts to look
|
||||
// up the helper binary before falling back to the platform's default.
|
||||
//
|
||||
// If no user-defined helper is passed, and no helper is found, it returns an
|
||||
// empty string, which means credentials are stored unencrypted in the CLI's
|
||||
// config-file without the use of a credentials store.
|
||||
func DetectDefaultStore(customStore string) string {
|
||||
if customStore != "" {
|
||||
// use user-defined
|
||||
return store
|
||||
return customStore
|
||||
}
|
||||
|
||||
platformDefault := defaultCredentialsStore()
|
||||
|
||||
+23
-23
@@ -5,19 +5,19 @@ go 1.25.0
|
||||
require (
|
||||
github.com/containerd/errdefs v1.0.0
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/docker/cli v29.3.1+incompatible
|
||||
github.com/docker/cli v29.4.0+incompatible
|
||||
github.com/docker/cli-docs-tool v0.11.0
|
||||
github.com/docker/distribution v2.8.3+incompatible
|
||||
github.com/docker/go-connections v0.6.0
|
||||
github.com/docker/go-connections v0.7.0
|
||||
github.com/fvbommel/sortorder v1.1.0
|
||||
github.com/moby/moby/api v1.54.0
|
||||
github.com/moby/moby/client v0.3.0
|
||||
github.com/moby/moby/api v1.54.2
|
||||
github.com/moby/moby/client v0.4.1
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/sirupsen/logrus v1.9.4
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a
|
||||
go.opentelemetry.io/otel v1.42.0
|
||||
go.opentelemetry.io/otel v1.43.0
|
||||
gotest.tools/v3 v3.5.2
|
||||
)
|
||||
|
||||
@@ -41,7 +41,7 @@ require (
|
||||
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.27.7 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.20 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.2 // indirect
|
||||
@@ -56,22 +56,22 @@ require (
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.42.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/term v0.41.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
|
||||
google.golang.org/grpc v1.79.3 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||
golang.org/x/crypto v0.50.0 // indirect
|
||||
golang.org/x/net v0.53.0 // indirect
|
||||
golang.org/x/sys v0.43.0 // indirect
|
||||
golang.org/x/term v0.42.0 // indirect
|
||||
golang.org/x/text v0.36.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/grpc v1.80.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
)
|
||||
|
||||
+48
-48
@@ -47,8 +47,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.3.1+incompatible h1:M04FDj2TRehDacrosh7Vlkgc7AuQoWloQkf1PA5hmoI=
|
||||
github.com/docker/cli v29.3.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v29.4.0+incompatible h1:+IjXULMetlvWJiuSI0Nbor36lcJ5BTcVpUmB21KBoVM=
|
||||
github.com/docker/cli v29.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli-docs-tool v0.11.0 h1:7d8QARFb7QEobizqxmEM7fOteZEHwH/zWgHQtHZEcfE=
|
||||
github.com/docker/cli-docs-tool v0.11.0/go.mod h1:ma8BKiisUo8D6W05XEYIh3oa1UbgrZhi1nowyKFJa8Q=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
@@ -59,8 +59,8 @@ github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c=
|
||||
github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
|
||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
|
||||
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
|
||||
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
|
||||
@@ -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.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||
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=
|
||||
@@ -148,10 +148,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.54.0 h1:7kbUgyiKcoBhm0UrWbdrMs7RX8dnwzURKVbZGy2GnL0=
|
||||
github.com/moby/moby/api v1.54.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
|
||||
github.com/moby/moby/client v0.3.0 h1:UUGL5okry+Aomj3WhGt9Aigl3ZOxZGqR7XPo+RLPlKs=
|
||||
github.com/moby/moby/client v0.3.0/go.mod h1:HJgFbJRvogDQjbM8fqc1MCEm4mIAGMLjXbgwoZp6jCQ=
|
||||
github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg=
|
||||
github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
|
||||
github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY=
|
||||
github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ=
|
||||
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=
|
||||
@@ -237,26 +237,26 @@ github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a h1:tlJ
|
||||
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a/go.mod h1:Y94A6rPp2OwNfP/7vmf8O2xx2IykP8pPXQ1DLouGnEw=
|
||||
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.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
|
||||
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
|
||||
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0 h1:NOyNnS19BF2SUDApbOKbDtWZ0IK7b8FJ2uAGdIWOGb0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.40.0/go.mod h1:VL6EgVikRLcJa9ftukrHu/ZkkhFBSo1lzvdBC9CF1ss=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
|
||||
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
|
||||
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
||||
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
|
||||
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo=
|
||||
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk=
|
||||
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
@@ -265,14 +265,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.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||
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.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
|
||||
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
|
||||
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=
|
||||
@@ -285,24 +285,24 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
|
||||
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
||||
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
|
||||
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
|
||||
@@ -4,13 +4,13 @@ package registry
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/distribution/registry/client/transport"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -48,7 +48,7 @@ func loadTLSConfig(ctx context.Context, directory string, tlsConfig *tls.Config)
|
||||
switch filepath.Ext(f.Name()) {
|
||||
case ".crt":
|
||||
if tlsConfig.RootCAs == nil {
|
||||
systemPool, err := tlsconfig.SystemCertPool()
|
||||
systemPool, err := x509.SystemCertPool()
|
||||
if err != nil {
|
||||
return invalidParam(fmt.Errorf("unable to get system cert pool: %w", err))
|
||||
}
|
||||
|
||||
@@ -1929,7 +1929,6 @@ _docker_container_run_and_create() {
|
||||
--ip
|
||||
--ip6
|
||||
--ipc
|
||||
--kernel-memory
|
||||
--label-file
|
||||
--label -l
|
||||
--link
|
||||
@@ -2310,7 +2309,6 @@ _docker_container_update() {
|
||||
--cpuset-cpus
|
||||
--cpuset-mems
|
||||
--cpu-shares -c
|
||||
--kernel-memory
|
||||
--memory -m
|
||||
--memory-reservation
|
||||
--memory-swap
|
||||
|
||||
@@ -170,7 +170,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -a '(__fish_pri
|
||||
# cp
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a cp -d "Copy files/folders between a container and the local filesystem"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from cp' -s a -l archive -d 'Archive mode (copy all uid/gid information)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from cp' -s L -l follow-link -d 'Always follow symbol link in SRC_PATH'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from cp' -s L -l follow-link -d 'Always follow symlinks in SRC_PATH'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from cp' -l help -d 'Print usage'
|
||||
|
||||
# create
|
||||
@@ -226,7 +226,6 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l ip -d 'IPv4
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l ip6 -d 'IPv6 address (e.g., 2001:db8::33)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l ipc -d 'IPC mode to use'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l isolation -d 'Container isolation technology'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l kernel-memory -d 'Kernel memory limit'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s l -l label -d 'Set meta data on a container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l label-file -d 'Read in a line delimited file of labels'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l link -d 'Add link to another container'
|
||||
@@ -252,7 +251,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s p -l publish
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s P -l publish-all -d 'Publish all exposed ports to random ports'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l read-only -d "Mount the container's root filesystem as read only"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l restart -d 'Restart policy to apply when a container exits'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l rm -d 'Automatically remove the container when it exits'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l rm -d 'Automatically remove the container and its associated anonymous volumes when it exits'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l runtime -d 'Runtime to use for this container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l security-opt -d 'Security Options'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l shm-size -d 'Size of /dev/shm'
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
#
|
||||
# zsh completion for docker (http://docker.com)
|
||||
#
|
||||
# version: 0.3.0
|
||||
# github: https://github.com/felixr/docker-zsh-completion
|
||||
# github: https://github.com/docker/cli
|
||||
#
|
||||
# contributors:
|
||||
# - Felix Riedel
|
||||
@@ -675,7 +674,6 @@ __docker_container_subcommand() {
|
||||
"($help)--cpu-rt-runtime=[Limit the CPU real-time runtime]:CPU real-time runtime in microseconds: "
|
||||
"($help)--cpuset-cpus=[CPUs in which to allow execution]:CPUs: "
|
||||
"($help)--cpuset-mems=[MEMs in which to allow execution]:MEMs: "
|
||||
"($help)--kernel-memory=[Kernel memory limit in bytes]:Memory limit: "
|
||||
"($help -m --memory)"{-m=,--memory=}"[Memory limit]:Memory limit: "
|
||||
"($help)--memory-reservation=[Memory soft limit]:Memory limit: "
|
||||
"($help)--memory-swap=[Total memory limit with swap]:Memory limit: "
|
||||
@@ -707,7 +705,7 @@ __docker_container_subcommand() {
|
||||
local state
|
||||
_arguments $(__docker_arguments) \
|
||||
$opts_help \
|
||||
"($help -L --follow-link)"{-L,--follow-link}"[Always follow symbol link]" \
|
||||
"($help -L --follow-link)"{-L,--follow-link}"[Always follow symlinks]" \
|
||||
"($help -)1:container:->container" \
|
||||
"($help -)2:hostpath:_files" && ret=0
|
||||
case $state in
|
||||
@@ -938,11 +936,7 @@ __docker_container_subcommand() {
|
||||
"($help -)*: :->values" && ret=0
|
||||
case $state in
|
||||
(values)
|
||||
if [[ ${words[(r)--kernel-memory*]} = (--kernel-memory*) ]]; then
|
||||
__docker_complete_stopped_containers && ret=0
|
||||
else
|
||||
__docker_complete_containers && ret=0
|
||||
fi
|
||||
__docker_complete_containers && ret=0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ CACHE_VOLUME_NAME := docker-cli-dev-cache
|
||||
ifeq ($(DOCKER_CLI_GO_BUILD_CACHE),y)
|
||||
DOCKER_CLI_MOUNTS += -v "$(CACHE_VOLUME_NAME):/root/.cache/go-build"
|
||||
endif
|
||||
VERSION = $(shell cat VERSION)
|
||||
VERSION ?= $(shell cat VERSION)-dev
|
||||
ENVVARS = -e VERSION=$(VERSION) -e GITCOMMIT -e PLATFORM -e TESTFLAGS -e TESTDIRS -e GOOS -e GOARCH -e GOARM -e ENGINE_VERSION
|
||||
|
||||
# Some Dockerfiles use features that are only supported with BuildKit enabled
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.26.1
|
||||
ARG GO_VERSION=1.26.3
|
||||
|
||||
# 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
|
||||
@@ -10,7 +10,7 @@ ARG ALPINE_VERSION=3.23
|
||||
# BUILDX_VERSION sets the version of buildx to install in the dev container.
|
||||
# It must be a valid tag in the docker.io/docker/buildx-bin image repository
|
||||
# on Docker Hub.
|
||||
ARG BUILDX_VERSION=0.31.1
|
||||
ARG BUILDX_VERSION=0.33.0
|
||||
FROM docker/buildx-bin:${BUILDX_VERSION} AS buildx
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS golang
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.26.1
|
||||
ARG GO_VERSION=1.26.3
|
||||
|
||||
# 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
|
||||
# that's also available as alpine image variant for the Golang version used.
|
||||
ARG ALPINE_VERSION=3.23
|
||||
# GOLANGCI_LINT_VERSION sets the version of the golangci/golangci-lint image to use.
|
||||
ARG GOLANGCI_LINT_VERSION=v2.9.0
|
||||
ARG GOLANGCI_LINT_VERSION=v2.10.1
|
||||
|
||||
FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION}-alpine AS golangci-lint
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.26.1
|
||||
ARG GO_VERSION=1.26.3
|
||||
|
||||
# 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,36 +0,0 @@
|
||||
# The non-reference docs have been moved!
|
||||
|
||||
<!-- This file is maintained within the docker/cli GitHub
|
||||
repository at https://github.com/docker/cli/. Make all
|
||||
pull requests against that repo. If you see this file in
|
||||
another repository, consider it read-only there, as it will
|
||||
periodically be overwritten by the definitive file. Pull
|
||||
requests which include edits to this file in other repositories
|
||||
will be rejected.
|
||||
-->
|
||||
|
||||
The documentation for Docker Engine has been merged into
|
||||
[the general documentation repo](https://github.com/docker/docker.github.io).
|
||||
|
||||
See the [README](https://github.com/docker/docker.github.io/blob/master/README.md)
|
||||
for instructions on contributing to and building the documentation.
|
||||
|
||||
If you'd like to edit the current published version of the Engine docs,
|
||||
do it in the master branch here:
|
||||
https://github.com/docker/docker.github.io/tree/master/engine
|
||||
|
||||
If you need to document the functionality of an upcoming Engine release,
|
||||
use the `vnext-engine` branch:
|
||||
https://github.com/docker/docker.github.io/tree/vnext-engine/engine
|
||||
|
||||
The reference docs have been left in docker/docker (this repo), which remains
|
||||
the place to edit them.
|
||||
|
||||
The docs in the general repo are open-source and we appreciate
|
||||
your feedback and pull requests!
|
||||
|
||||
# Generate docs
|
||||
|
||||
```shell
|
||||
$ make -f docker.Makefile yamldocs
|
||||
```
|
||||
@@ -10,6 +10,14 @@ This document describes the Docker Engine plugins generally available in Docker
|
||||
Engine. To view information on plugins managed by Docker,
|
||||
refer to [Docker Engine plugin system](_index.md).
|
||||
|
||||
> [!NOTE]
|
||||
> Legacy plugins are superseded by Docker Engine's managed plugin system.
|
||||
> For plugins installed and managed by Docker, use
|
||||
> [`docker plugin install`](https://docs.docker.com/reference/cli/docker/plugin/install/)
|
||||
> and the [Docker Engine plugin system](_index.md). The third-party plugins
|
||||
> listed on this page are provided for historical reference, and archived
|
||||
> projects are marked accordingly.
|
||||
|
||||
You can extend the capabilities of the Docker Engine by loading third-party
|
||||
plugins. This page explains the types of plugins and provides links to several
|
||||
volume and network plugins for Docker.
|
||||
@@ -30,7 +38,8 @@ Follow the instructions in the plugin's documentation.
|
||||
|
||||
## Finding a plugin
|
||||
|
||||
The sections below provide an overview of available third-party plugins.
|
||||
The sections below provide an overview of third-party plugins that use the
|
||||
legacy plugin model.
|
||||
|
||||
### Network plugins
|
||||
|
||||
@@ -44,22 +53,21 @@ The sections below provide an overview of available third-party plugins.
|
||||
|
||||
| Plugin | Description |
|
||||
|:---------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [Azure File Storage plugin](https://github.com/Azure/azurefile-dockervolumedriver) | Lets you mount Microsoft [Azure File Storage](https://azure.microsoft.com/blog/azure-file-storage-now-generally-available/) shares to Docker containers as volumes using the SMB 3.0 protocol. [Learn more](https://azure.microsoft.com/blog/persistent-docker-volumes-with-azure-file-storage/). |
|
||||
| [Azure File Storage plugin](https://github.com/Azure/azurefile-dockervolumedriver) (archived) | Lets you mount Microsoft [Azure File Storage](https://azure.microsoft.com/blog/azure-file-storage-now-generally-available/) shares to Docker containers as volumes using the SMB 3.0 protocol. [Learn more](https://azure.microsoft.com/blog/persistent-docker-volumes-with-azure-file-storage/). |
|
||||
| [BeeGFS Volume Plugin](https://github.com/RedCoolBeans/docker-volume-beegfs) | An open source volume plugin to create persistent volumes in a BeeGFS parallel file system. |
|
||||
| [Blockbridge plugin](https://github.com/blockbridge/blockbridge-docker-volume) | A volume plugin that provides access to an extensible set of container-based persistent storage options. It supports single and multi-host Docker environments with features that include tenant isolation, automated provisioning, encryption, secure deletion, snapshots and QoS. |
|
||||
| [Contiv Volume Plugin](https://github.com/contiv/volplugin) | An open source volume plugin that provides multi-tenant, persistent, distributed storage with intent based consumption. It has support for Ceph and NFS. |
|
||||
| [Convoy plugin](https://github.com/rancher/convoy) | A volume plugin for a variety of storage back-ends including device mapper and NFS. It's a simple standalone executable written in Go and provides the framework to support vendor-specific extensions such as snapshots, backups and restore. |
|
||||
| [Convoy plugin](https://github.com/rancher/convoy) (archived) | A volume plugin for a variety of storage back-ends including device mapper and NFS. It's a simple standalone executable written in Go and provides the framework to support vendor-specific extensions such as snapshots, backups and restore. |
|
||||
| [DigitalOcean Block Storage plugin](https://github.com/omallo/docker-volume-plugin-dostorage) | Integrates DigitalOcean's [block storage solution](https://www.digitalocean.com/products/storage/) into the Docker ecosystem by automatically attaching a given block storage volume to a DigitalOcean droplet and making the contents of the volume available to Docker containers running on that droplet. |
|
||||
| [DRBD plugin](https://www.drbd.org/en/supported-projects/docker) | A volume plugin that provides highly available storage replicated by [DRBD](https://www.drbd.org). Data written to the docker volume is replicated in a cluster of DRBD nodes. |
|
||||
| [Flocker plugin](https://github.com/ScatterHQ/flocker) | A volume plugin that provides multi-host portable volumes for Docker, enabling you to run databases and other stateful containers and move them around across a cluster of machines. |
|
||||
| [Fuxi Volume Plugin](https://github.com/openstack/fuxi) | A volume plugin that is developed as part of the OpenStack Kuryr project and implements the Docker volume plugin API by utilizing Cinder, the OpenStack block storage service. |
|
||||
| [gce-docker plugin](https://github.com/mcuadros/gce-docker) | A volume plugin able to attach, format and mount Google Compute [persistent-disks](https://cloud.google.com/compute/docs/disks/persistent-disks). |
|
||||
| [GlusterFS plugin](https://github.com/calavera/docker-volume-glusterfs) | A volume plugin that provides multi-host volumes management for Docker using GlusterFS. |
|
||||
| [Flocker plugin](https://github.com/ScatterHQ/flocker) (archived) | A volume plugin that provides multi-host portable volumes for Docker, enabling you to run databases and other stateful containers and move them around across a cluster of machines. |
|
||||
| [Fuxi Volume Plugin](https://github.com/openstack-archive/fuxi) (archived) | A volume plugin that is developed as part of the OpenStack Kuryr project and implements the Docker volume plugin API by utilizing Cinder, the OpenStack block storage service. |
|
||||
| [gce-docker plugin](https://github.com/mcuadros/gce-docker) (archived) | A volume plugin able to attach, format and mount Google Compute [persistent-disks](https://cloud.google.com/compute/docs/disks/persistent-disks). |
|
||||
| [GlusterFS plugin](https://github.com/calavera/docker-volume-glusterfs) (archived) | A volume plugin that provides multi-host volumes management for Docker using GlusterFS. |
|
||||
| [Horcrux Volume Plugin](https://github.com/muthu-r/horcrux) | A volume plugin that allows on-demand, version controlled access to your data. Horcrux is an open-source plugin, written in Go, and supports SCP, [Minio](https://www.minio.io) and Amazon S3. |
|
||||
| [HPE 3Par Volume Plugin](https://github.com/hpe-storage/python-hpedockerplugin/) | A volume plugin that supports HPE 3Par and StoreVirtual iSCSI storage arrays. |
|
||||
| [Infinit volume plugin](https://infinit.sh/documentation/docker/volume-plugin) | A volume plugin that makes it easy to mount and manage Infinit volumes using Docker. |
|
||||
| [IPFS Volume Plugin](https://github.com/vdemeester/docker-volume-ipfs) | An open source volume plugin that allows using an [ipfs](https://ipfs.io/) filesystem as a volume. |
|
||||
| [Keywhiz plugin](https://github.com/calavera/docker-volume-keywhiz) | A plugin that provides credentials and secret management using Keywhiz as a central repository. |
|
||||
| [IPFS Volume Plugin](https://github.com/vdemeester/docker-volume-ipfs) (archived) | An open source volume plugin that allows using an [ipfs](https://ipfs.io/) filesystem as a volume. |
|
||||
| [Keywhiz plugin](https://github.com/calavera/docker-volume-keywhiz) (archived) | A plugin that provides credentials and secret management using Keywhiz as a central repository. |
|
||||
| [Linode Volume Plugin](https://github.com/linode/docker-volume-linode) | A plugin that adds the ability to manage Linode Block Storage as Docker Volumes from within a Linode. |
|
||||
| [Local Persist Plugin](https://github.com/CWSpear/local-persist) | A volume plugin that extends the default `local` driver's functionality by allowing you specify a mountpoint anywhere on the host, which enables the files to *always persist*, even if the volume is removed via `docker volume rm`. |
|
||||
| [NetApp Plugin](https://github.com/NetApp/netappdvp) (nDVP) | A volume plugin that provides direct integration with the Docker ecosystem for the NetApp storage portfolio. The nDVP package supports the provisioning and management of storage resources from the storage platform to Docker hosts, with a robust framework for adding additional platforms in the future. |
|
||||
|
||||
@@ -86,6 +86,31 @@ passed to the authorization plugins. For commands that return chunked HTTP
|
||||
response, such as `logs` and `events`, only the HTTP request is sent to the
|
||||
authorization plugins.
|
||||
|
||||
### Response body size and partial buffering
|
||||
|
||||
The internal buffer that holds the response body between the daemon's HTTP
|
||||
handler and the plugin's response authorization callback (`responseModifier`,
|
||||
defined in [`pkg/authorization/response.go`](https://github.com/moby/moby/blob/master/pkg/authorization/response.go))
|
||||
has a fixed capacity of 64 KiB (`maxBufferSize`).
|
||||
|
||||
For most non-streaming endpoints the full response is buffered for plugin
|
||||
inspection regardless of total size, because Go's `encoding/json` encoder
|
||||
serializes the complete payload into a single underlying write. The
|
||||
streaming-response exclusion noted above (for example, `logs` and `events`)
|
||||
is the practical effect of this 64 KiB threshold combined with the
|
||||
`io.WriteFlusher` write pattern used by streaming handlers, where each write
|
||||
is immediately drained to the client and is therefore no longer available
|
||||
for plugin inspection by the time the handler returns.
|
||||
|
||||
> [!NOTE]
|
||||
> Plugins that depend on `ResponseBody` inspection for redaction or
|
||||
> content-filtering should restrict their policies to endpoints whose
|
||||
> response is produced as a single write (typical of REST-style API
|
||||
> responses). For commands whose responses are streamed or are likely to
|
||||
> exceed the buffer through multiple writes, do not rely on `ResponseBody`
|
||||
> for security-relevant decisions; perform the filtering in a separate
|
||||
> layer in front of the daemon.
|
||||
|
||||
During request/response processing, some authorization flows might
|
||||
need to do additional queries to the Docker daemon. To complete such flows,
|
||||
plugins can call the daemon API similar to a regular user. To enable these
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Dockerfile reference
|
||||
|
||||
This file has moved to the BuildKit repository at https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md
|
||||
@@ -82,7 +82,7 @@ The following filter matches only services with the `project` label with the
|
||||
`project-a` value.
|
||||
|
||||
```console
|
||||
$ docker service ls --filter label=project=test
|
||||
$ docker config ls --filter label=project=project-a
|
||||
|
||||
ID NAME CREATED UPDATED
|
||||
mem02h8n73mybpgqjf0kfi1n0 test_config About an hour ago About an hour ago
|
||||
@@ -95,7 +95,7 @@ The `name` filter matches on all or prefix of a config's name.
|
||||
The following filter matches config with a name containing a prefix of `test`.
|
||||
|
||||
```console
|
||||
$ docker config ls --filter name=test_config
|
||||
$ docker config ls --filter name=test
|
||||
|
||||
ID NAME CREATED UPDATED
|
||||
mem02h8n73mybpgqjf0kfi1n0 test_config About an hour ago About an hour ago
|
||||
|
||||
@@ -17,7 +17,7 @@ container source to stdout.
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------------|:-------|:--------|:-------------------------------------------------------------------------------------------------------------|
|
||||
| `-a`, `--archive` | `bool` | | Archive mode (copy all uid/gid information) |
|
||||
| `-L`, `--follow-link` | `bool` | | Always follow symbol link in SRC_PATH |
|
||||
| `-L`, `--follow-link` | `bool` | | Always follow symlinks in SRC_PATH |
|
||||
| `-q`, `--quiet` | `bool` | | Suppress progress output during copy. Progress output is automatically suppressed if no terminal is attached |
|
||||
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ CONTAINER ID IMAGE COMMAND CREATED
|
||||
9b6247364a03 busybox "top" 2 minutes ago Up 2 minutes nostalgic_stallman
|
||||
```
|
||||
|
||||
You can also filter for a substring in a name as this shows:
|
||||
You can filter for a substring in a name as this shows:
|
||||
|
||||
```console
|
||||
$ docker ps --filter "name=nostalgic"
|
||||
@@ -395,22 +395,23 @@ template.
|
||||
|
||||
Valid placeholders for the Go template are listed below:
|
||||
|
||||
| Placeholder | Description |
|
||||
|:--------------|:------------------------------------------------------------------------------------------------|
|
||||
| `.ID` | Container ID |
|
||||
| `.Image` | Image ID |
|
||||
| `.Command` | Quoted command |
|
||||
| `.CreatedAt` | Time when the container was created. |
|
||||
| `.RunningFor` | Elapsed time since the container was started. |
|
||||
| `.Ports` | Exposed ports. |
|
||||
| `.State` | Container status (for example; "created", "running", "exited"). |
|
||||
| `.Status` | Container status with details about duration and health-status. |
|
||||
| `.Size` | Container disk size. |
|
||||
| `.Names` | Container names. |
|
||||
| `.Labels` | All labels assigned to the container. |
|
||||
| `.Label` | Value of a specific label for this container. For example `'{{.Label "com.docker.swarm.cpu"}}'` |
|
||||
| `.Mounts` | Names of the volumes mounted in this container. |
|
||||
| `.Networks` | Names of the networks attached to this container. |
|
||||
| Placeholder | Description |
|
||||
|:----------------|:------------------------------------------------------------------------------------------------|
|
||||
| `.ID` | Container ID |
|
||||
| `.Image` | Image ID |
|
||||
| `.Command` | Quoted command |
|
||||
| `.CreatedAt` | Time when the container was created. |
|
||||
| `.RunningFor` | Elapsed time since the container was started. |
|
||||
| `.Ports` | Exposed ports. |
|
||||
| `.State` | Container status (for example; "created", "running", "exited"). |
|
||||
| `.Status` | Container status with details about duration and health-status. |
|
||||
| `.HealthStatus` | Container health status ("starting", "healthy", "unhealthy"; empty when unavailable). |
|
||||
| `.Size` | Container disk size. |
|
||||
| `.Names` | Container names. |
|
||||
| `.Labels` | All labels assigned to the container. |
|
||||
| `.Label` | Value of a specific label for this container. For example `'{{.Label "com.docker.swarm.cpu"}}'` |
|
||||
| `.Mounts` | Names of the volumes mounted in this container. |
|
||||
| `.Networks` | Names of the networks attached to this container. |
|
||||
|
||||
When using the `--format` option, the `ps` command will either output the data
|
||||
exactly as the template declares or, when using the `table` directive, includes
|
||||
|
||||
@@ -1233,7 +1233,7 @@ the container and remove the file system when the container exits, use the
|
||||
`--rm` flag:
|
||||
|
||||
```text
|
||||
--rm: Automatically remove the container when it exits
|
||||
--rm: Automatically remove the container and its associated anonymous volumes when it exits
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
|
||||
@@ -37,11 +37,6 @@ resources from their Docker host. With a single command, you can place
|
||||
limits on a single container or on many. To specify more than one container,
|
||||
provide space-separated list of container names or IDs.
|
||||
|
||||
With the exception of the `--kernel-memory` option, you can specify these
|
||||
options on a running or a stopped container. On kernel version older than
|
||||
4.6, you can only update `--kernel-memory` on a stopped container or on
|
||||
a running container with kernel memory initialized.
|
||||
|
||||
> [!WARNING]
|
||||
> The `docker update` and `docker container update` commands are not supported
|
||||
> for Windows containers.
|
||||
@@ -69,42 +64,6 @@ To update multiple resource configurations for multiple containers:
|
||||
$ docker update --cpu-shares 512 -m 300M abebf7571666 hopeful_morse
|
||||
```
|
||||
|
||||
### <a name="kernel-memory"></a> Update a container's kernel memory constraints (--kernel-memory)
|
||||
|
||||
You can update a container's kernel memory limit using the `--kernel-memory`
|
||||
option. On kernel version older than 4.6, this option can be updated on a
|
||||
running container only if the container was started with `--kernel-memory`.
|
||||
If the container was started without `--kernel-memory` you need to stop
|
||||
the container before updating kernel memory.
|
||||
|
||||
> [!NOTE]
|
||||
> The `--kernel-memory` option has been deprecated since Docker 20.10.
|
||||
|
||||
For example, if you started a container with this command:
|
||||
|
||||
```console
|
||||
$ docker run -dit --name test --kernel-memory 50M ubuntu bash
|
||||
```
|
||||
|
||||
You can update kernel memory while the container is running:
|
||||
|
||||
```console
|
||||
$ docker update --kernel-memory 80M test
|
||||
```
|
||||
|
||||
If you started a container without kernel memory initialized:
|
||||
|
||||
```console
|
||||
$ docker run -dit --name test2 --memory 300M ubuntu bash
|
||||
```
|
||||
|
||||
Update kernel memory of running container `test2` will fail. You need to stop
|
||||
the container before updating the `--kernel-memory` setting. The next time you
|
||||
start it, the container uses the new value.
|
||||
|
||||
Kernel version newer than (include) 4.6 does not have this limitation, you
|
||||
can use `--kernel-memory` the same way as other options.
|
||||
|
||||
### <a name="restart"></a> Update a container's restart policy (--restart)
|
||||
|
||||
You can change a container's restart policy on a running container. The new
|
||||
|
||||
@@ -15,7 +15,7 @@ Manage contexts
|
||||
| [`rm`](context_rm.md) | Remove one or more contexts |
|
||||
| [`show`](context_show.md) | Print the name of the current context |
|
||||
| [`update`](context_update.md) | Update a context |
|
||||
| [`use`](context_use.md) | Set the current docker context |
|
||||
| [`use`](context_use.md) | Set the default docker context |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,63 @@
|
||||
# context use
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Set the current docker context
|
||||
Set the default docker context
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
## Description
|
||||
|
||||
Set the default context to use, when `DOCKER_HOST`, `DOCKER_CONTEXT` environment
|
||||
variables and `--host`, `--context` global options aren't set.
|
||||
To disable usage of contexts, you can use the special `default` context.
|
||||
The `docker context use` command sets the default context for the Docker CLI.
|
||||
|
||||
The `docker context use` command sets the Docker CLI’s default context by updating
|
||||
your CLI config (`~/.docker/config.json`). This change is persistent, affecting
|
||||
all shells and sessions that share that config, not just the current terminal.
|
||||
|
||||
For one-off commands or per-shell usage, use `--context` or the `DOCKER_CONTEXT`
|
||||
environment variable instead.
|
||||
|
||||
## Examples
|
||||
|
||||
### Set the default (sticky) context
|
||||
|
||||
This updates the CLI configuration and applies to new terminal sessions:
|
||||
|
||||
```bash
|
||||
$ docker context use my-context
|
||||
my-context
|
||||
|
||||
$ docker context show
|
||||
my-context
|
||||
```
|
||||
|
||||
### Use a context for a single command
|
||||
|
||||
Use the global `--context` flag to avoid changing the default:
|
||||
|
||||
```bash
|
||||
$ docker --context my-context ps
|
||||
```
|
||||
|
||||
### Use a context for the current shell session
|
||||
|
||||
Set `DOCKER_CONTEXT` to override the configured default in the current shell:
|
||||
|
||||
```bash
|
||||
$ export DOCKER_CONTEXT=my-context
|
||||
$ docker context show
|
||||
my-context
|
||||
```
|
||||
|
||||
To stop overriding:
|
||||
|
||||
```bash
|
||||
$ unset DOCKER_CONTEXT
|
||||
```
|
||||
|
||||
### Switch back to the default context
|
||||
|
||||
```bash
|
||||
$ docker context use default
|
||||
default
|
||||
```
|
||||
|
||||
@@ -17,7 +17,7 @@ container source to stdout.
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------------|:-------|:--------|:-------------------------------------------------------------------------------------------------------------|
|
||||
| `-a`, `--archive` | `bool` | | Archive mode (copy all uid/gid information) |
|
||||
| `-L`, `--follow-link` | `bool` | | Always follow symbol link in SRC_PATH |
|
||||
| `-L`, `--follow-link` | `bool` | | Always follow symlinks in SRC_PATH |
|
||||
| `-q`, `--quiet` | `bool` | | Suppress progress output during copy. Progress output is automatically suppressed if no terminal is attached |
|
||||
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ COPY failed: forbidden path outside the build context: ../../some-dir ()
|
||||
```
|
||||
|
||||
BuildKit on the other hand strips leading relative paths that traverse outside
|
||||
of the build context. Re-using the previous example, the path `COPY
|
||||
of the build context. Reusing the previous example, the path `COPY
|
||||
../../some-dir .` evaluates to `COPY some-dir .` with BuildKit.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -32,13 +32,13 @@ use `docker pull`.
|
||||
### Proxy configuration
|
||||
|
||||
If you are behind an HTTP proxy server, for example in corporate settings,
|
||||
before open a connect to registry, you may need to configure the Docker
|
||||
daemon's proxy settings, refer to the [dockerd command-line reference](https://docs.docker.com/reference/cli/dockerd/#proxy-configuration)
|
||||
for details.
|
||||
you may have to configure the Docker daemon to use the proxy server for
|
||||
operations such as pulling and pushing images. Refer to the
|
||||
[dockerd command-line reference](https://docs.docker.com/reference/cli/dockerd/#proxy-configuration) for details.
|
||||
|
||||
### Concurrent downloads
|
||||
|
||||
By default the Docker daemon will pull three layers of an image at a time.
|
||||
By default the Docker daemon downloads three layers of an image at a time.
|
||||
If you are on a low bandwidth connection this may cause timeout issues and you may want to lower
|
||||
this via the `--max-concurrent-downloads` daemon option. See the
|
||||
[daemon documentation](https://docs.docker.com/reference/cli/dockerd/) for more details.
|
||||
|
||||
@@ -107,7 +107,7 @@ $ docker inspect --format='{{.Config.Image}}' $INSTANCE_ID
|
||||
You can loop over arrays and maps in the results to produce simple text output:
|
||||
|
||||
```console
|
||||
$ docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}} {{$p}} -> {{(index $conf 0).HostPort}} {{end}}' $INSTANCE_ID
|
||||
$ docker inspect --format='{{range $p, $conf := .NetworkSettings.Ports}} {{$p}} -> {{with $conf}}{{(index . 0).HostPort}}{{else}}none{{end}} {{end}}' $INSTANCE_ID
|
||||
```
|
||||
|
||||
### Find a specific port mapping
|
||||
|
||||
@@ -82,7 +82,7 @@ The following filter matches only services with the `project` label with the
|
||||
`project-a` value.
|
||||
|
||||
```console
|
||||
$ docker service ls --filter label=project=test
|
||||
$ docker secret ls --filter label=project=project-a
|
||||
|
||||
ID NAME CREATED UPDATED
|
||||
mem02h8n73mybpgqjf0kfi1n0 test_secret About an hour ago About an hour ago
|
||||
@@ -95,7 +95,7 @@ The `name` filter matches on all or prefix of a secret's name.
|
||||
The following filter matches secret with a name containing a prefix of `test`.
|
||||
|
||||
```console
|
||||
$ docker secret ls --filter name=test_secret
|
||||
$ docker secret ls --filter name=test
|
||||
|
||||
ID NAME CREATED UPDATED
|
||||
mem02h8n73mybpgqjf0kfi1n0 test_secret About an hour ago About an hour ago
|
||||
|
||||
@@ -58,7 +58,7 @@ desired root digest: sha256:05da740cf2577a25224c53019e2cce99bcc5ba09664ad6bb2a94
|
||||
rotated CA certificates: [> ] 0/2 nodes
|
||||
```
|
||||
|
||||
Once the rotation os finished (all the progress bars have completed) the now-current
|
||||
Once the rotation is finished (all the progress bars have completed) the now-current
|
||||
CA certificate will be printed:
|
||||
|
||||
```console
|
||||
|
||||
@@ -958,8 +958,6 @@ the `--cgroup-parent` option on the daemon.
|
||||
#### Daemon metrics
|
||||
|
||||
The `--metrics-addr` option takes a TCP address to serve the metrics API.
|
||||
This feature is still experimental, therefore, the daemon must be running in experimental
|
||||
mode for this feature to work.
|
||||
|
||||
To serve the metrics API on `localhost:9323` you would specify `--metrics-addr 127.0.0.1:9323`,
|
||||
allowing you to make requests on the API at `127.0.0.1:9323/metrics` to receive metrics in the
|
||||
|
||||
@@ -334,7 +334,6 @@ container:
|
||||
| `-m`, `--memory=""` | Memory limit (format: `<number>[<unit>]`). Number is a positive integer. Unit can be one of `b`, `k`, `m`, or `g`. Minimum is 6M. |
|
||||
| `--memory-swap=""` | Total memory limit (memory + swap, format: `<number>[<unit>]`). Number is a positive integer. Unit can be one of `b`, `k`, `m`, or `g`. |
|
||||
| `--memory-reservation=""` | Memory soft limit (format: `<number>[<unit>]`). Number is a positive integer. Unit can be one of `b`, `k`, `m`, or `g`. |
|
||||
| `--kernel-memory=""` | Kernel memory limit (format: `<number>[<unit>]`). Number is a positive integer. Unit can be one of `b`, `k`, `m`, or `g`. Minimum is 4M. |
|
||||
| `-c`, `--cpu-shares=0` | CPU shares (relative weight) |
|
||||
| `--cpus=0.000` | Number of CPUs. Number is a fractional number. 0.000 means no limit. |
|
||||
| `--cpu-period=0` | Limit the CPU CFS (Completely Fair Scheduler) period |
|
||||
@@ -500,80 +499,6 @@ parameter can be changed to select the priority of which containers will
|
||||
be killed when the system is out of memory, with negative scores making them
|
||||
less likely to be killed, and positive scores more likely.
|
||||
|
||||
### Kernel memory constraints
|
||||
|
||||
Kernel memory is fundamentally different than user memory as kernel memory can't
|
||||
be swapped out. The inability to swap makes it possible for the container to
|
||||
block system services by consuming too much kernel memory. Kernel memory includes:
|
||||
|
||||
- stack pages
|
||||
- slab pages
|
||||
- sockets memory pressure
|
||||
- tcp memory pressure
|
||||
|
||||
You can setup kernel memory limit to constrain these kinds of memory. For example,
|
||||
every process consumes some stack pages. By limiting kernel memory, you can
|
||||
prevent new processes from being created when the kernel memory usage is too high.
|
||||
|
||||
Kernel memory is never completely independent of user memory. Instead, you limit
|
||||
kernel memory in the context of the user memory limit. Assume "U" is the user memory
|
||||
limit and "K" the kernel limit. There are three possible ways to set limits:
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Result</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="no-wrap"><strong>U != 0, K = inf</strong> (default)</td>
|
||||
<td>
|
||||
This is the standard memory limitation mechanism already present before using
|
||||
kernel memory. Kernel memory is completely ignored.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="no-wrap"><strong>U != 0, K < U</strong></td>
|
||||
<td>
|
||||
Kernel memory is a subset of the user memory. This setup is useful in
|
||||
deployments where the total amount of memory per-cgroup is overcommitted.
|
||||
Overcommitting kernel memory limits is definitely not recommended, since the
|
||||
box can still run out of non-reclaimable memory.
|
||||
In this case, you can configure K so that the sum of all groups is
|
||||
never greater than the total memory. Then, freely set U at the expense of
|
||||
the system's service quality.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="no-wrap"><strong>U != 0, K > U</strong></td>
|
||||
<td>
|
||||
Since kernel memory charges are also fed to the user counter and reclamation
|
||||
is triggered for the container for both kinds of memory. This configuration
|
||||
gives the admin a unified view of memory. It is also useful for people
|
||||
who just want to track kernel memory usage.
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
Examples:
|
||||
|
||||
```console
|
||||
$ docker run -it -m 500M --kernel-memory 50M ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
We set memory and kernel memory, so the processes in the container can use
|
||||
500M memory in total, in this 500M memory, it can be 50M kernel memory tops.
|
||||
|
||||
```console
|
||||
$ docker run -it --kernel-memory 50M ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
We set kernel memory without **-m**, so the processes in the container can
|
||||
use as much memory as they want, but they can only use 50M kernel memory.
|
||||
|
||||
### Swappiness constraint
|
||||
|
||||
By default, a container's kernel can swap out a percentage of anonymous pages.
|
||||
|
||||
@@ -31,10 +31,6 @@ true
|
||||
|
||||
## Current experimental features
|
||||
|
||||
Docker service logs command to view logs for a Docker service. This is needed in Swarm mode.
|
||||
Option to squash image layers to the base image after successful builds.
|
||||
Checkpoint and restore support for Containers.
|
||||
Metrics (Prometheus) output for basic container, image, and daemon operations.
|
||||
|
||||
* [Checkpoint & Restore](../docs/reference/commandline/checkpoint.md)
|
||||
* [Docker build with --squash argument](../docs/reference/commandline/build.md#squash-an-images-layers---squash-experimental)
|
||||
* [Support for plugins on Swarm](https://github.com/moby/moby/pull/33575)
|
||||
|
||||
@@ -4,6 +4,7 @@ package registry
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -74,7 +75,7 @@ func loadTLSConfig(ctx context.Context, directory string, tlsConfig *tls.Config)
|
||||
switch filepath.Ext(f.Name()) {
|
||||
case ".crt":
|
||||
if tlsConfig.RootCAs == nil {
|
||||
systemPool, err := tlsconfig.SystemCertPool()
|
||||
systemPool, err := x509.SystemCertPool()
|
||||
if err != nil {
|
||||
return invalidParam(fmt.Errorf("unable to get system cert pool: %w", err))
|
||||
}
|
||||
|
||||
+2
-12
@@ -50,7 +50,6 @@ docker-run - Create and run a new container from an image
|
||||
[**--ip6**[=*IPv6-ADDRESS*]]
|
||||
[**--ipc**[=*IPC*]]
|
||||
[**--isolation**[=*default*]]
|
||||
[**--kernel-memory**[=*KERNEL-MEMORY*]]
|
||||
[**-l**|**--label**[=*[]*]]
|
||||
[**--label-file**[=*[]*]]
|
||||
[**--link**[=*[]*]]
|
||||
@@ -410,15 +409,6 @@ is `hyperv`. Linux only supports `default`.
|
||||
**-l**, **--label** *key*=*value*
|
||||
Set metadata on the container (for example, **--label com.example.key=value**).
|
||||
|
||||
**--kernel-memory**=*number*[*S*]
|
||||
Kernel memory limit; *S* is an optional suffix which can be one of **b**, **k**, **m**, or **g**.
|
||||
|
||||
Constrains the kernel memory available to a container. If a limit of 0
|
||||
is specified (not using **--kernel-memory**), the container's kernel memory
|
||||
is not limited. If you specify a limit, it may be rounded up to a multiple
|
||||
of the operating system's page size and the value can be very large,
|
||||
millions of trillions.
|
||||
|
||||
**--label-file**=[]
|
||||
Read in a line delimited file of labels
|
||||
|
||||
@@ -628,7 +618,7 @@ its root filesystem mounted as read only prohibiting any writes.
|
||||
Default is **no**.
|
||||
|
||||
**--rm** **true**|**false**
|
||||
Automatically remove the container when it exits. The default is **false**.
|
||||
Automatically remove the container and its associated anonymous volumes when it exits. The default is **false**.
|
||||
`--rm` flag can work together with `-d`, and auto-removal will be done on
|
||||
daemon side. Note that it's incompatible with any restart policy other than
|
||||
`none`.
|
||||
@@ -744,7 +734,7 @@ any options, the systems uses the following options:
|
||||
**-u**, **--user**=""
|
||||
Sets the username or UID used and optionally the groupname or GID for the specified command.
|
||||
|
||||
The followings examples are all valid:
|
||||
The following examples are all valid:
|
||||
--user [user | user:group | uid | uid:gid | user:gid | uid:group ]
|
||||
|
||||
Without this argument the command will be run as root in the container.
|
||||
|
||||
@@ -19,7 +19,7 @@ the same capabilities as the container, which may be limited. Set
|
||||
# USER
|
||||
`user` sets the username or UID used and optionally the groupname or GID for the specified command.
|
||||
|
||||
The followings examples are all valid:
|
||||
The following examples are all valid:
|
||||
--user [user | user:group | uid | uid:gid | user:gid | uid:group ]
|
||||
|
||||
Without this argument the command will be run as root in the container.
|
||||
|
||||
@@ -4,25 +4,11 @@ resources from their Docker host. With a single command, you can place
|
||||
limits on a single container or on many. To specify more than one container,
|
||||
provide space-separated list of container names or IDs.
|
||||
|
||||
With the exception of the **--kernel-memory** option, you can specify these
|
||||
options on a running or a stopped container. On kernel version older than
|
||||
4.6, You can only update **--kernel-memory** on a stopped container or on
|
||||
a running container with kernel memory initialized.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
## kernel-memory
|
||||
|
||||
Kernel memory limit (format: `<number>[<unit>]`, where unit = b, k, m or g)
|
||||
|
||||
Note that on kernel version older than 4.6, you can not update kernel memory on
|
||||
a running container if the container is started without kernel memory initialized,
|
||||
in this case, it can only be updated after it's stopped. The new setting takes
|
||||
effect when the container is started.
|
||||
|
||||
## memory
|
||||
|
||||
Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
|
||||
Memory limit (format: `<number>[<unit>]`, where unit = b, k, m or g)
|
||||
|
||||
Note that the memory should be smaller than the already set swap memory limit.
|
||||
If you want update a memory limit bigger than the already set swap memory limit,
|
||||
@@ -52,41 +38,6 @@ To update multiple resource configurations for multiple containers:
|
||||
$ docker container update --cpu-shares 512 -m 300M abebf7571666 hopeful_morse
|
||||
```
|
||||
|
||||
### Update a container's kernel memory constraints
|
||||
|
||||
You can update a container's kernel memory limit using the **--kernel-memory**
|
||||
option. On kernel version older than 4.6, this option can be updated on a
|
||||
running container only if the container was started with **--kernel-memory**.
|
||||
If the container was started *without* **--kernel-memory** you need to stop
|
||||
the container before updating kernel memory.
|
||||
|
||||
NOTE: The **--kernel-memory** option has been deprecated since Docker 20.10.
|
||||
|
||||
For example, if you started a container with this command:
|
||||
|
||||
```console
|
||||
$ docker run -dit --name test --kernel-memory 50M ubuntu bash
|
||||
```
|
||||
|
||||
You can update kernel memory while the container is running:
|
||||
|
||||
```console
|
||||
$ docker container update --kernel-memory 80M test
|
||||
```
|
||||
|
||||
If you started a container *without* kernel memory initialized:
|
||||
|
||||
```console
|
||||
$ docker run -dit --name test2 --memory 300M ubuntu bash
|
||||
```
|
||||
|
||||
Update kernel memory of running container `test2` will fail. You need to stop
|
||||
the container before updating the **--kernel-memory** setting. The next time you
|
||||
start it, the container uses the new value.
|
||||
|
||||
Kernel version newer than (include) 4.6 does not have this limitation, you
|
||||
can use `--kernel-memory` the same way as other options.
|
||||
|
||||
### Update a container's restart policy
|
||||
|
||||
You can change a container's restart policy on a running container. The new
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
The `docker volume` command has subcommands for managing data volumes. A data
|
||||
volume is a specially-designated directory that by-passes storage driver
|
||||
volume is a specially-designated directory that bypasses storage driver
|
||||
management.
|
||||
|
||||
Data volumes persist data independent of a container's life cycle. When you
|
||||
|
||||
+20
-12
@@ -70,28 +70,36 @@ func ConvertKVStringsToMapWithNil(values []string) map[string]*string {
|
||||
return result
|
||||
}
|
||||
|
||||
// ParseRestartPolicy returns the parsed policy or an error indicating what is incorrect
|
||||
// ParseRestartPolicy parses a restart policy string ("name[:max-retries]")
|
||||
// into a [container.RestartPolicy].
|
||||
//
|
||||
// Parsing is syntactic only; semantic validation is deferred to the daemon/API.
|
||||
// An empty input returns a zero-value policy for backward compatibility. The
|
||||
// retry count, if set, must be an integer (negative values are allowed here
|
||||
// but may be rejected by the daemon).
|
||||
func ParseRestartPolicy(policy string) (container.RestartPolicy, error) {
|
||||
if policy == "" {
|
||||
// for backward-compatibility, we don't set the default ("no")
|
||||
// policy here, because older versions of the engine may not
|
||||
// support it.
|
||||
// For backward compatibility, do not set an explicit default ("no"),
|
||||
// as older daemons may not support it.
|
||||
return container.RestartPolicy{}, nil
|
||||
}
|
||||
|
||||
p := container.RestartPolicy{}
|
||||
k, v, ok := strings.Cut(policy, ":")
|
||||
if ok && k == "" {
|
||||
name, count, ok := strings.Cut(policy, ":")
|
||||
if ok && name == "" {
|
||||
return container.RestartPolicy{}, errors.New("invalid restart policy format: no policy provided before colon")
|
||||
}
|
||||
if v != "" {
|
||||
count, err := strconv.Atoi(v)
|
||||
|
||||
var retryCount int
|
||||
if count != "" {
|
||||
c, err := strconv.Atoi(count)
|
||||
if err != nil {
|
||||
return container.RestartPolicy{}, errors.New("invalid restart policy format: maximum retry count must be an integer")
|
||||
}
|
||||
p.MaximumRetryCount = count
|
||||
retryCount = c
|
||||
}
|
||||
|
||||
p.Name = container.RestartPolicyMode(k)
|
||||
return p, nil
|
||||
return container.RestartPolicy{
|
||||
Name: container.RestartPolicyMode(name),
|
||||
MaximumRetryCount: retryCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -318,8 +318,7 @@ func TestPortOptInvalidSimpleSyntax(t *testing.T) {
|
||||
},
|
||||
{
|
||||
value: "",
|
||||
expectedError: "invalid proto: ",
|
||||
// expectedError: "no port specified: <empty>", // FIXME(thaJeztah): re-enable once https://github.com/docker/go-connections/pull/143 is in a go-connections release.
|
||||
expectedError: "no port specified: <empty>",
|
||||
},
|
||||
{
|
||||
value: "1.1.1.1:80:80",
|
||||
|
||||
+26
-26
@@ -12,14 +12,14 @@ require (
|
||||
dario.cat/mergo v1.0.2
|
||||
github.com/containerd/errdefs v1.0.0
|
||||
github.com/containerd/log v0.1.0
|
||||
github.com/containerd/platforms v1.0.0-rc.2
|
||||
github.com/containerd/platforms v1.0.0-rc.4
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7
|
||||
github.com/creack/pty v1.1.24
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/docker/cli-docs-tool v0.11.0
|
||||
github.com/docker/distribution v2.8.3+incompatible
|
||||
github.com/docker/docker-credential-helpers v0.9.5
|
||||
github.com/docker/go-connections v0.6.0
|
||||
github.com/docker/docker-credential-helpers v0.9.7
|
||||
github.com/docker/go-connections v0.7.0
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/fvbommel/sortorder v1.1.0
|
||||
github.com/go-jose/go-jose/v4 v4.1.4
|
||||
@@ -28,12 +28,12 @@ require (
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/mattn/go-runewidth v0.0.22
|
||||
github.com/mattn/go-runewidth v0.0.23
|
||||
github.com/moby/go-archive v0.2.0
|
||||
github.com/moby/moby/api v1.54.1
|
||||
github.com/moby/moby/client v0.4.0
|
||||
github.com/moby/moby/api v1.54.2
|
||||
github.com/moby/moby/client v0.4.1
|
||||
github.com/moby/patternmatcher v0.6.1
|
||||
github.com/moby/swarmkit/v2 v2.1.1
|
||||
github.com/moby/swarmkit/v2 v2.1.2
|
||||
github.com/moby/sys/atomicwriter v0.1.0
|
||||
github.com/moby/sys/capability v0.4.0
|
||||
github.com/moby/sys/sequential v0.6.0
|
||||
@@ -49,19 +49,19 @@ 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.67.0
|
||||
go.opentelemetry.io/otel v1.42.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0
|
||||
go.opentelemetry.io/otel/metric v1.42.0
|
||||
go.opentelemetry.io/otel/sdk v1.42.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0
|
||||
go.opentelemetry.io/otel/trace v1.42.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0
|
||||
go.opentelemetry.io/otel v1.43.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0
|
||||
go.opentelemetry.io/otel/metric v1.43.0
|
||||
go.opentelemetry.io/otel/sdk v1.43.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0
|
||||
go.opentelemetry.io/otel/trace v1.43.0
|
||||
go.yaml.in/yaml/v3 v3.0.4
|
||||
golang.org/x/sync v0.20.0
|
||||
golang.org/x/sys v0.42.0
|
||||
golang.org/x/term v0.41.0
|
||||
golang.org/x/text v0.35.0
|
||||
golang.org/x/sys v0.44.0
|
||||
golang.org/x/term v0.43.0
|
||||
golang.org/x/text v0.37.0
|
||||
gotest.tools/v3 v3.5.2
|
||||
tags.cncf.io/container-device-interface v1.1.0
|
||||
)
|
||||
@@ -96,15 +96,15 @@ require (
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
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.etcd.io/raft/v3 v3.6.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
|
||||
golang.org/x/mod v0.34.0 // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||
golang.org/x/mod v0.36.0 // indirect
|
||||
golang.org/x/net v0.54.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||
google.golang.org/grpc v1.79.3 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/grpc v1.80.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
)
|
||||
|
||||
+54
-54
@@ -24,8 +24,8 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4=
|
||||
github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
|
||||
github.com/containerd/platforms v1.0.0-rc.4 h1:M42JrUT4zfZTqtkUwkr0GzmUWbfyO5VO0Q5b3op97T4=
|
||||
github.com/containerd/platforms v1.0.0-rc.4/go.mod h1:lKlMXyLybmBedS/JJm11uDofzI8L2v0J2ZbYvNsbq1A=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
@@ -40,10 +40,10 @@ github.com/docker/cli-docs-tool v0.11.0 h1:7d8QARFb7QEobizqxmEM7fOteZEHwH/zWgHQt
|
||||
github.com/docker/cli-docs-tool v0.11.0/go.mod h1:ma8BKiisUo8D6W05XEYIh3oa1UbgrZhi1nowyKFJa8Q=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
|
||||
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/docker-credential-helpers v0.9.7 h1:jaPIxEIDz5bQeghNAdzz0ETwMMnM4vzjZlxz3pWP4JA=
|
||||
github.com/docker/docker-credential-helpers v0.9.7/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||
github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c=
|
||||
github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
|
||||
github.com/docker/go-events v0.0.0-20250808211157-605354379745 h1:yOn6Ze6IbYI/KAw2lw/83ELYvZh6hvsygTVkD0dzMC4=
|
||||
github.com/docker/go-events v0.0.0-20250808211157-605354379745/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
|
||||
@@ -106,21 +106,21 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mattn/go-runewidth v0.0.22 h1:76lXsPn6FyHtTY+jt2fTTvsMUCZq1k0qwRsAMuxzKAk=
|
||||
github.com/mattn/go-runewidth v0.0.22/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw=
|
||||
github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
|
||||
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
|
||||
github.com/moby/moby/api v1.54.1 h1:TqVzuJkOLsgLDDwNLmYqACUuTehOHRGKiPhvH8V3Nn4=
|
||||
github.com/moby/moby/api v1.54.1/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
|
||||
github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw=
|
||||
github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g=
|
||||
github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg=
|
||||
github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
|
||||
github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY=
|
||||
github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ=
|
||||
github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U=
|
||||
github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/swarmkit/v2 v2.1.1 h1:yvTJ8MMCc3f0qTA44J6R59EZ5yZawdYopkpuLk4+ICU=
|
||||
github.com/moby/swarmkit/v2 v2.1.1/go.mod h1:mTTGIAz/59OGZR5Qe+QByIe3Nxc+sSuJkrsStFhr6Lg=
|
||||
github.com/moby/swarmkit/v2 v2.1.2 h1:1WDZAI6HVYNKdCG4zlXnTAPyLsLwuhRGWlHoOUf5Z6I=
|
||||
github.com/moby/swarmkit/v2 v2.1.2/go.mod h1:GQ6T0ij2oBbWX10OHwpvK449xfXkQMZ8J+B+eQ4mgp4=
|
||||
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/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
|
||||
@@ -202,30 +202,30 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
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.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ=
|
||||
go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo=
|
||||
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.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
|
||||
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
|
||||
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0 h1:MdKucPl/HbzckWWEisiNqMPhRrAOQX8r4jTuGr636gk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0/go.mod h1:RolT8tWtfHcjajEH5wFIZ4Dgh5jpPdFXYV9pTAk/qjc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 h1:THuZiwpQZuHPul65w4WcwEnkX2QIuMT+UFoOrygtoJw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0/go.mod h1:J2pvYM5NGHofZ2/Ru6zw/TNWnEQp5crgyDeSrYpXkAw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 h1:zWWrB1U6nqhS/k6zYB74CjRpuiitRtLLi68VcgmOEto=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0/go.mod h1:2qXPNBX1OVRC0IwOnfo1ljoid+RD0QK3443EaqVlsOU=
|
||||
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
|
||||
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
|
||||
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
|
||||
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
|
||||
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo=
|
||||
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk=
|
||||
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
@@ -236,16 +236,16 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
||||
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
||||
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
||||
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
||||
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.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.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||
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=
|
||||
@@ -261,14 +261,14 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
|
||||
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
||||
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
||||
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
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.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -279,14 +279,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=
|
||||
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-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
|
||||
+15
-22
@@ -1,32 +1,25 @@
|
||||
version: "2"
|
||||
linters:
|
||||
enable:
|
||||
- copyloopvar
|
||||
- gofmt
|
||||
- goimports
|
||||
- dupword
|
||||
- gosec
|
||||
- ineffassign
|
||||
- misspell
|
||||
- nolintlint
|
||||
- revive
|
||||
- staticcheck
|
||||
- tenv # Detects using os.Setenv instead of t.Setenv since Go 1.17
|
||||
- unconvert
|
||||
- unused
|
||||
- govet
|
||||
- dupword # Checks for duplicate words in the source code
|
||||
disable:
|
||||
- errcheck
|
||||
|
||||
run:
|
||||
timeout: 5m
|
||||
|
||||
issues:
|
||||
exclude-dirs:
|
||||
- api
|
||||
- cluster
|
||||
- design
|
||||
- docs
|
||||
- docs/man
|
||||
- releases
|
||||
- reports
|
||||
- test # e2e scripts
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
exclusions:
|
||||
generated: lax
|
||||
|
||||
+105
-1
@@ -152,6 +152,88 @@ func Only(platform specs.Platform) MatchComparer {
|
||||
return Ordered(platformVector(Normalize(platform))...)
|
||||
}
|
||||
|
||||
// OnlyOS returns a match comparer that matches only platforms with the same
|
||||
// OS, OS version, and OS features, regardless of architecture. When comparing,
|
||||
// it always ranks the best architecture match highest using the default
|
||||
// platform resolution logic.
|
||||
func OnlyOS(platform specs.Platform) MatchComparer {
|
||||
normalized := Normalize(platform)
|
||||
return onlyOSComparer{
|
||||
platform: normalized,
|
||||
osvM: newOSVersionMatcher(normalized),
|
||||
archOrder: orderedPlatformComparer{
|
||||
matchers: []Matcher{NewMatcher(normalized)},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newOSVersionMatcher(platform specs.Platform) osVerMatcher {
|
||||
if platform.OS == "windows" {
|
||||
return &windowsVersionMatcher{
|
||||
windowsOSVersion: getWindowsOSVersion(platform.OSVersion),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type onlyOSComparer struct {
|
||||
platform specs.Platform
|
||||
osvM osVerMatcher
|
||||
archOrder orderedPlatformComparer
|
||||
}
|
||||
|
||||
func (c onlyOSComparer) matchOS(platform specs.Platform) bool {
|
||||
normalized := Normalize(platform)
|
||||
if c.platform.OS != normalized.OS {
|
||||
return false
|
||||
}
|
||||
if c.osvM != nil {
|
||||
if !c.osvM.Match(platform.OSVersion) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(normalized.OSFeatures) > 0 {
|
||||
if len(c.platform.OSFeatures) < len(normalized.OSFeatures) {
|
||||
return false
|
||||
}
|
||||
j := 0
|
||||
for _, feature := range normalized.OSFeatures {
|
||||
found := false
|
||||
for ; j < len(c.platform.OSFeatures); j++ {
|
||||
if feature == c.platform.OSFeatures[j] {
|
||||
found = true
|
||||
j++
|
||||
break
|
||||
}
|
||||
if feature < c.platform.OSFeatures[j] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c onlyOSComparer) Match(platform specs.Platform) bool {
|
||||
return c.matchOS(platform)
|
||||
}
|
||||
|
||||
func (c onlyOSComparer) Less(p1, p2 specs.Platform) bool {
|
||||
p1m := c.matchOS(p1)
|
||||
p2m := c.matchOS(p2)
|
||||
if p1m && !p2m {
|
||||
return true
|
||||
}
|
||||
if !p1m {
|
||||
return false
|
||||
}
|
||||
// Both match — rank by architecture preference
|
||||
return c.archOrder.Less(p1, p2)
|
||||
}
|
||||
|
||||
// OnlyStrict returns a match comparer for a single platform.
|
||||
//
|
||||
// Unlike Only, OnlyStrict does not match sub platforms.
|
||||
@@ -213,9 +295,20 @@ func (c orderedPlatformComparer) Less(p1 specs.Platform, p2 specs.Platform) bool
|
||||
return true
|
||||
}
|
||||
if p1m || p2m {
|
||||
if p1m && p2m {
|
||||
// Prefer one with most matching features
|
||||
if len(p1.OSFeatures) != len(p2.OSFeatures) {
|
||||
return len(p1.OSFeatures) > len(p2.OSFeatures)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(p1.OSFeatures) > 0 || len(p2.OSFeatures) > 0 {
|
||||
p1.OSFeatures = nil
|
||||
p2.OSFeatures = nil
|
||||
return c.Less(p1, p2)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -242,9 +335,20 @@ func (c anyPlatformComparer) Less(p1, p2 specs.Platform) bool {
|
||||
p2m = true
|
||||
}
|
||||
if p1m && p2m {
|
||||
return false
|
||||
if len(p1.OSFeatures) != len(p2.OSFeatures) {
|
||||
return len(p1.OSFeatures) > len(p2.OSFeatures)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If neither match and has features, strip features and compare
|
||||
if !p1m && !p2m && (len(p1.OSFeatures) > 0 || len(p2.OSFeatures) > 0) {
|
||||
p1.OSFeatures = nil
|
||||
p2.OSFeatures = nil
|
||||
return c.Less(p1, p2)
|
||||
}
|
||||
|
||||
// If one matches, and the other does, sort match first
|
||||
return p1m && !p2m
|
||||
}
|
||||
|
||||
-2
@@ -45,7 +45,6 @@ func getMachineArch() (string, error) {
|
||||
// So we don't need to access the ARM registers to detect platform information
|
||||
// by ourselves. We can just parse these information from /proc/cpuinfo
|
||||
func getCPUInfo(pattern string) (info string, err error) {
|
||||
|
||||
cpuinfo, err := os.Open("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -75,7 +74,6 @@ func getCPUInfo(pattern string) (info string, err error) {
|
||||
|
||||
// getCPUVariantFromArch get CPU variant from arch through a system call
|
||||
func getCPUVariantFromArch(arch string) (string, error) {
|
||||
|
||||
var variant string
|
||||
|
||||
arch = strings.ToLower(arch)
|
||||
|
||||
+4
-4
@@ -24,10 +24,10 @@ import (
|
||||
)
|
||||
|
||||
func getCPUVariant() (string, error) {
|
||||
|
||||
var variant string
|
||||
|
||||
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
|
||||
switch runtime.GOOS {
|
||||
case "windows", "darwin":
|
||||
// Windows/Darwin only supports v7 for ARM32 and v8 for ARM64 and so we can use
|
||||
// runtime.GOARCH to determine the variants
|
||||
switch runtime.GOARCH {
|
||||
@@ -38,7 +38,7 @@ func getCPUVariant() (string, error) {
|
||||
default:
|
||||
variant = "unknown"
|
||||
}
|
||||
} else if runtime.GOOS == "freebsd" {
|
||||
case "freebsd":
|
||||
// FreeBSD supports ARMv6 and ARMv7 as well as ARMv4 and ARMv5 (though deprecated)
|
||||
// detecting those variants is currently unimplemented
|
||||
switch runtime.GOARCH {
|
||||
@@ -47,7 +47,7 @@ func getCPUVariant() (string, error) {
|
||||
default:
|
||||
variant = "unknown"
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
return "", fmt.Errorf("getCPUVariant for OS %s: %v", runtime.GOOS, errNotImplemented)
|
||||
}
|
||||
|
||||
|
||||
+12
@@ -17,6 +17,7 @@
|
||||
package platforms
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -162,3 +163,14 @@ func (c *windowsMatchComparer) Less(p1, p2 specs.Platform) bool {
|
||||
}
|
||||
return m1 && !m2
|
||||
}
|
||||
|
||||
type windowsStripFeaturesMatcher struct {
|
||||
Matcher
|
||||
}
|
||||
|
||||
func (m windowsStripFeaturesMatcher) Match(p specs.Platform) bool {
|
||||
if i := slices.Index(p.OSFeatures, "win32k"); i >= 0 {
|
||||
p.OSFeatures = slices.Delete(slices.Clone(p.OSFeatures), i, i+1)
|
||||
}
|
||||
return m.Matcher.Match(p)
|
||||
}
|
||||
|
||||
+168
-22
@@ -111,9 +111,11 @@ package platforms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -121,12 +123,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
specifierRe = regexp.MustCompile(`^[A-Za-z0-9_.-]+$`)
|
||||
osAndVersionRe = regexp.MustCompile(`^([A-Za-z0-9_-]+)(?:\(([A-Za-z0-9_.-]*)\))?$`)
|
||||
specifierRe = regexp.MustCompile(`^[A-Za-z0-9_.-]+$`)
|
||||
osRe = regexp.MustCompile(`^([A-Za-z0-9_-]+)(?:\(([A-Za-z0-9_.%-]*)((?:\+[A-Za-z0-9_.%-]+)*)\))?$`)
|
||||
)
|
||||
|
||||
const osAndVersionFormat = "%s(%s)"
|
||||
|
||||
// Platform is a type alias for convenience, so there is no need to import image-spec package everywhere.
|
||||
type Platform = specs.Platform
|
||||
|
||||
@@ -143,6 +143,10 @@ type Matcher interface {
|
||||
// functionality.
|
||||
//
|
||||
// Applications should opt to use `Match` over directly parsing specifiers.
|
||||
//
|
||||
// For OSFeatures, this matcher will match if the platform to match has
|
||||
// OSFeatures which are a subset of the OSFeatures of the platform
|
||||
// provided to NewMatcher.
|
||||
func NewMatcher(platform specs.Platform) Matcher {
|
||||
m := &matcher{
|
||||
Platform: Normalize(platform),
|
||||
@@ -152,6 +156,11 @@ func NewMatcher(platform specs.Platform) Matcher {
|
||||
m.osvM = &windowsVersionMatcher{
|
||||
windowsOSVersion: getWindowsOSVersion(platform.OSVersion),
|
||||
}
|
||||
|
||||
// In prior versions, the win32k os feature was not considered for matching,
|
||||
// strip out the win32k feature for comparison
|
||||
var stripped Matcher = windowsStripFeaturesMatcher{m}
|
||||
|
||||
// In prior versions, on windows, the returned matcher implements a
|
||||
// MatchComprarer interface.
|
||||
// This preserves that behavior for backwards compatibility.
|
||||
@@ -161,8 +170,9 @@ func NewMatcher(platform specs.Platform) Matcher {
|
||||
// It was likely intended to be used in `Ordered` but it is not since
|
||||
// `Less` that is implemented here ends up getting masked due to wrapping.
|
||||
if runtime.GOOS == "windows" {
|
||||
return &windowsMatchComparer{m}
|
||||
return &windowsMatchComparer{stripped}
|
||||
}
|
||||
return stripped
|
||||
}
|
||||
return m
|
||||
}
|
||||
@@ -178,10 +188,39 @@ type matcher struct {
|
||||
|
||||
func (m *matcher) Match(platform specs.Platform) bool {
|
||||
normalized := Normalize(platform)
|
||||
return m.OS == normalized.OS &&
|
||||
if m.OS == normalized.OS &&
|
||||
m.Architecture == normalized.Architecture &&
|
||||
m.Variant == normalized.Variant &&
|
||||
m.matchOSVersion(platform)
|
||||
m.matchOSVersion(platform) {
|
||||
if len(normalized.OSFeatures) == 0 {
|
||||
return true
|
||||
}
|
||||
if len(m.OSFeatures) >= len(normalized.OSFeatures) {
|
||||
// Ensure that normalized.OSFeatures is a subset of
|
||||
// m.OSFeatures
|
||||
j := 0
|
||||
for _, feature := range normalized.OSFeatures {
|
||||
found := false
|
||||
for ; j < len(m.OSFeatures); j++ {
|
||||
if feature == m.OSFeatures[j] {
|
||||
found = true
|
||||
j++
|
||||
break
|
||||
}
|
||||
// Since both lists are ordered, if the feature is less
|
||||
// than what is seen, it is not in the list
|
||||
if feature < m.OSFeatures[j] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *matcher) matchOSVersion(platform specs.Platform) bool {
|
||||
@@ -210,11 +249,14 @@ func ParseAll(specifiers []string) ([]specs.Platform, error) {
|
||||
|
||||
// Parse parses the platform specifier syntax into a platform declaration.
|
||||
//
|
||||
// Platform specifiers are in the format `<os>[(<OSVersion>)]|<arch>|<os>[(<OSVersion>)]/<arch>[/<variant>]`.
|
||||
// Platform specifiers are in the format `<os>[(<os options>)]|<arch>|<os>[(<os options>)]/<arch>[/<variant>]`.
|
||||
// The minimum required information for a platform specifier is the operating
|
||||
// system or architecture. The OSVersion can be part of the OS like `windows(10.0.17763)`
|
||||
// When an OSVersion is specified, then specs.Platform.OSVersion is populated with that value,
|
||||
// and an empty string otherwise.
|
||||
// system or architecture. The "os options" may be OSVersion which can be part of the OS
|
||||
// like `windows(10.0.17763)`. When an OSVersion is specified, then specs.Platform.OSVersion is
|
||||
// populated with that value, and an empty string otherwise. The "os options" may also include an
|
||||
// array of OSFeatures, each feature prefixed with '+', without any other separator, and provided
|
||||
// after the OSVersion when the OSVersion is specified. An "os options" with version and features
|
||||
// is like `windows(10.0.17763+win32k)`.
|
||||
// If there is only a single string (no slashes), the
|
||||
// value will be matched against the known set of operating systems, then fall
|
||||
// back to the known set of architectures. The missing component will be
|
||||
@@ -231,14 +273,24 @@ func Parse(specifier string) (specs.Platform, error) {
|
||||
var p specs.Platform
|
||||
for i, part := range parts {
|
||||
if i == 0 {
|
||||
// First element is <os>[(<OSVersion>)]
|
||||
osVer := osAndVersionRe.FindStringSubmatch(part)
|
||||
if osVer == nil {
|
||||
return specs.Platform{}, fmt.Errorf("%q is an invalid OS component of %q: OSAndVersion specifier component must match %q: %w", part, specifier, osAndVersionRe.String(), errInvalidArgument)
|
||||
// First element is <os>[(<OSVersion>[+<OSFeature>]*)]
|
||||
osOptions := osRe.FindStringSubmatch(part)
|
||||
if osOptions == nil {
|
||||
return specs.Platform{}, fmt.Errorf("%q is an invalid OS component of %q: OSAndVersion specifier component must match %q: %w", part, specifier, osRe.String(), errInvalidArgument)
|
||||
}
|
||||
|
||||
p.OS = normalizeOS(osVer[1])
|
||||
p.OSVersion = osVer[2]
|
||||
p.OS = normalizeOS(osOptions[1])
|
||||
osVersion, err := decodeOSOption(osOptions[2])
|
||||
if err != nil {
|
||||
return specs.Platform{}, fmt.Errorf("%q has an invalid OS version %q: %w", specifier, osOptions[2], err)
|
||||
}
|
||||
p.OSVersion = osVersion
|
||||
if osOptions[3] != "" {
|
||||
p.OSFeatures, err = parseOSFeatures(osOptions[3][1:])
|
||||
if err != nil {
|
||||
return specs.Platform{}, fmt.Errorf("%q has invalid OS features: %w", specifier, err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !specifierRe.MatchString(part) {
|
||||
return specs.Platform{}, fmt.Errorf("%q is an invalid component of %q: platform specifier component must match %q: %w", part, specifier, specifierRe.String(), errInvalidArgument)
|
||||
@@ -296,6 +348,30 @@ func Parse(specifier string) (specs.Platform, error) {
|
||||
return specs.Platform{}, fmt.Errorf("%q: cannot parse platform specifier: %w", specifier, errInvalidArgument)
|
||||
}
|
||||
|
||||
func parseOSFeatures(s string) ([]string, error) {
|
||||
if s == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var features []string
|
||||
for raw := range strings.SplitSeq(s, "+") {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return nil, fmt.Errorf("empty os feature: %w", errInvalidArgument)
|
||||
}
|
||||
feature, err := decodeOSOption(raw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid os feature %q: %w", raw, err)
|
||||
}
|
||||
if feature == "" {
|
||||
continue
|
||||
}
|
||||
features = append(features, feature)
|
||||
}
|
||||
|
||||
return features, nil
|
||||
}
|
||||
|
||||
// MustParse is like Parses but panics if the specifier cannot be parsed.
|
||||
// Simplifies initialization of global variables.
|
||||
func MustParse(specifier string) specs.Platform {
|
||||
@@ -321,12 +397,77 @@ func FormatAll(platform specs.Platform) string {
|
||||
if platform.OS == "" {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
if platform.OSVersion != "" {
|
||||
OSAndVersion := fmt.Sprintf(osAndVersionFormat, platform.OS, platform.OSVersion)
|
||||
return path.Join(OSAndVersion, platform.Architecture, platform.Variant)
|
||||
if platform.OSVersion == "" && len(platform.OSFeatures) == 0 {
|
||||
return path.Join(platform.OS, platform.Architecture, platform.Variant)
|
||||
}
|
||||
return path.Join(platform.OS, platform.Architecture, platform.Variant)
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString(platform.OS)
|
||||
osv := encodeOSOption(platform.OSVersion)
|
||||
formatted := formatOSFeatures(platform.OSFeatures)
|
||||
if osv != "" || formatted != "" {
|
||||
b.Grow(len(osv) + len(formatted) + 3) // parens + maybe '+'
|
||||
b.WriteByte('(')
|
||||
if osv != "" {
|
||||
b.WriteString(osv)
|
||||
}
|
||||
if formatted != "" {
|
||||
b.WriteByte('+')
|
||||
b.WriteString(formatted)
|
||||
}
|
||||
b.WriteByte(')')
|
||||
}
|
||||
|
||||
return path.Join(b.String(), platform.Architecture, platform.Variant)
|
||||
}
|
||||
|
||||
func formatOSFeatures(features []string) string {
|
||||
if len(features) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
if !slices.IsSorted(features) {
|
||||
features = slices.Clone(features)
|
||||
slices.Sort(features)
|
||||
}
|
||||
var b strings.Builder
|
||||
var wrote bool
|
||||
var prev string
|
||||
for _, f := range features {
|
||||
if f == "" || f == prev {
|
||||
// skip empty and duplicate values
|
||||
continue
|
||||
}
|
||||
prev = f
|
||||
if wrote {
|
||||
b.WriteByte('+')
|
||||
}
|
||||
b.WriteString(encodeOSOption(f))
|
||||
wrote = true
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// osOptionReplacer encodes characters in OS option values (version and
|
||||
// features) that are ambiguous with the format syntax. The percent sign
|
||||
// must be replaced first to avoid double-encoding.
|
||||
var osOptionReplacer = strings.NewReplacer(
|
||||
"%", "%25",
|
||||
"+", "%2B",
|
||||
"(", "%28",
|
||||
")", "%29",
|
||||
"/", "%2F",
|
||||
)
|
||||
|
||||
func encodeOSOption(v string) string {
|
||||
return osOptionReplacer.Replace(v)
|
||||
}
|
||||
|
||||
func decodeOSOption(v string) (string, error) {
|
||||
if strings.Contains(v, "%") {
|
||||
return url.PathUnescape(v)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Normalize validates and translate the platform to the canonical value.
|
||||
@@ -336,6 +477,11 @@ func FormatAll(platform specs.Platform) string {
|
||||
func Normalize(platform specs.Platform) specs.Platform {
|
||||
platform.OS = normalizeOS(platform.OS)
|
||||
platform.Architecture, platform.Variant = normalizeArch(platform.Architecture, platform.Variant)
|
||||
if len(platform.OSFeatures) > 0 {
|
||||
platform.OSFeatures = slices.Clone(platform.OSFeatures)
|
||||
slices.Sort(platform.OSFeatures)
|
||||
platform.OSFeatures = slices.Compact(platform.OSFeatures)
|
||||
}
|
||||
|
||||
return platform
|
||||
}
|
||||
|
||||
+36
-36
@@ -27,19 +27,15 @@ type PortSet map[Port]struct{}
|
||||
type Port string
|
||||
|
||||
// NewPort creates a new instance of a Port given a protocol and port number or port range
|
||||
func NewPort(proto, port string) (Port, error) {
|
||||
// Check for parsing issues on "port" now so we can avoid having
|
||||
// to check it later on.
|
||||
|
||||
portStartInt, portEndInt, err := ParsePortRangeToInt(port)
|
||||
func NewPort(proto, portOrRange string) (Port, error) {
|
||||
start, end, err := parsePortRange(portOrRange)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if portStartInt == portEndInt {
|
||||
return Port(fmt.Sprintf("%d/%s", portStartInt, proto)), nil
|
||||
if start == end {
|
||||
return Port(fmt.Sprintf("%d/%s", start, proto)), nil
|
||||
}
|
||||
return Port(fmt.Sprintf("%d-%d/%s", portStartInt, portEndInt, proto)), nil
|
||||
return Port(fmt.Sprintf("%d-%d/%s", start, end, proto)), nil
|
||||
}
|
||||
|
||||
// ParsePort parses the port number string and returns an int
|
||||
@@ -47,49 +43,53 @@ func ParsePort(rawPort string) (int, error) {
|
||||
if rawPort == "" {
|
||||
return 0, nil
|
||||
}
|
||||
port, err := strconv.ParseUint(rawPort, 10, 16)
|
||||
port, err := parsePortNumber(rawPort)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("invalid port '%s': %w", rawPort, errors.Unwrap(err))
|
||||
return 0, fmt.Errorf("invalid port '%s': %w", rawPort, err)
|
||||
}
|
||||
return int(port), nil
|
||||
return port, nil
|
||||
}
|
||||
|
||||
// ParsePortRangeToInt parses the port range string and returns start/end ints
|
||||
func ParsePortRangeToInt(rawPort string) (int, int, error) {
|
||||
func ParsePortRangeToInt(rawPort string) (startPort, endPort int, _ error) {
|
||||
if rawPort == "" {
|
||||
// TODO(thaJeztah): consider making this an error; this was kept to keep existing behavior.
|
||||
return 0, 0, nil
|
||||
}
|
||||
start, end, err := ParsePortRange(rawPort)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(start), int(end), nil
|
||||
return parsePortRange(rawPort)
|
||||
}
|
||||
|
||||
// Proto returns the protocol of a Port
|
||||
func (p Port) Proto() string {
|
||||
proto, _ := SplitProtoPort(string(p))
|
||||
_, proto, _ := strings.Cut(string(p), "/")
|
||||
if proto == "" {
|
||||
proto = "tcp"
|
||||
}
|
||||
return proto
|
||||
}
|
||||
|
||||
// Port returns the port number of a Port
|
||||
func (p Port) Port() string {
|
||||
_, port := SplitProtoPort(string(p))
|
||||
port, _, _ := strings.Cut(string(p), "/")
|
||||
return port
|
||||
}
|
||||
|
||||
// Int returns the port number of a Port as an int
|
||||
// Int returns the port number of a Port as an int. It assumes [Port]
|
||||
// is valid, and returns 0 otherwise.
|
||||
func (p Port) Int() int {
|
||||
portStr := p.Port()
|
||||
// We don't need to check for an error because we're going to
|
||||
// assume that any error would have been found, and reported, in NewPort()
|
||||
port, _ := ParsePort(portStr)
|
||||
// assume that any error would have been found, and reported, in [NewPort]
|
||||
port, _ := parsePortNumber(p.Port())
|
||||
return port
|
||||
}
|
||||
|
||||
// Range returns the start/end port numbers of a Port range as ints
|
||||
func (p Port) Range() (int, int, error) {
|
||||
return ParsePortRangeToInt(p.Port())
|
||||
portRange := p.Port()
|
||||
if portRange == "" {
|
||||
return 0, 0, nil
|
||||
}
|
||||
return parsePortRange(portRange)
|
||||
}
|
||||
|
||||
// SplitProtoPort splits a port(range) and protocol, formatted as "<portnum>/[<proto>]"
|
||||
@@ -173,6 +173,10 @@ func splitParts(rawport string) (hostIP, hostPort, containerPort string) {
|
||||
func ParsePortSpec(rawPort string) ([]PortMapping, error) {
|
||||
ip, hostPort, containerPort := splitParts(rawPort)
|
||||
proto, containerPort := SplitProtoPort(containerPort)
|
||||
if containerPort == "" {
|
||||
return nil, fmt.Errorf("no port specified: %s<empty>", rawPort)
|
||||
}
|
||||
|
||||
proto = strings.ToLower(proto)
|
||||
if err := validateProto(proto); err != nil {
|
||||
return nil, err
|
||||
@@ -189,18 +193,15 @@ func ParsePortSpec(rawPort string) ([]PortMapping, error) {
|
||||
if ip != "" && net.ParseIP(ip) == nil {
|
||||
return nil, errors.New("invalid IP address: " + ip)
|
||||
}
|
||||
if containerPort == "" {
|
||||
return nil, fmt.Errorf("no port specified: %s<empty>", rawPort)
|
||||
}
|
||||
|
||||
startPort, endPort, err := ParsePortRange(containerPort)
|
||||
startPort, endPort, err := parsePortRange(containerPort)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid containerPort: " + containerPort)
|
||||
}
|
||||
|
||||
var startHostPort, endHostPort uint64
|
||||
var startHostPort, endHostPort int
|
||||
if hostPort != "" {
|
||||
startHostPort, endHostPort, err = ParsePortRange(hostPort)
|
||||
startHostPort, endHostPort, err = parsePortRange(hostPort)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid hostPort: " + hostPort)
|
||||
}
|
||||
@@ -217,19 +218,18 @@ func ParsePortSpec(rawPort string) ([]PortMapping, error) {
|
||||
count := endPort - startPort + 1
|
||||
ports := make([]PortMapping, 0, count)
|
||||
|
||||
for i := uint64(0); i < count; i++ {
|
||||
cPort := Port(strconv.FormatUint(startPort+i, 10) + "/" + proto)
|
||||
for i := range count {
|
||||
hPort := ""
|
||||
if hostPort != "" {
|
||||
hPort = strconv.FormatUint(startHostPort+i, 10)
|
||||
hPort = strconv.Itoa(startHostPort + i)
|
||||
// Set hostPort to a range only if there is a single container port
|
||||
// and a dynamic host port.
|
||||
if count == 1 && startHostPort != endHostPort {
|
||||
hPort += "-" + strconv.FormatUint(endHostPort, 10)
|
||||
hPort += "-" + strconv.Itoa(endHostPort)
|
||||
}
|
||||
}
|
||||
ports = append(ports, PortMapping{
|
||||
Port: cPort,
|
||||
Port: Port(strconv.Itoa(startPort+i) + "/" + proto),
|
||||
Binding: PortBinding{HostIP: ip, HostPort: hPort},
|
||||
})
|
||||
}
|
||||
|
||||
+42
-15
@@ -2,32 +2,59 @@ package nat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParsePortRange parses and validates the specified string as a port-range (8000-9000)
|
||||
func ParsePortRange(ports string) (uint64, uint64, error) {
|
||||
// ParsePortRange parses and validates the specified string as a port range (e.g., "8000-9000").
|
||||
func ParsePortRange(ports string) (startPort, endPort uint64, _ error) {
|
||||
start, end, err := parsePortRange(ports)
|
||||
return uint64(start), uint64(end), err
|
||||
}
|
||||
|
||||
// parsePortRange parses and validates the specified string as a port range (e.g., "8000-9000").
|
||||
func parsePortRange(ports string) (startPort, endPort int, _ error) {
|
||||
if ports == "" {
|
||||
return 0, 0, errors.New("empty string specified for ports")
|
||||
}
|
||||
if !strings.Contains(ports, "-") {
|
||||
start, err := strconv.ParseUint(ports, 10, 16)
|
||||
end := start
|
||||
return start, end, err
|
||||
start, end, ok := strings.Cut(ports, "-")
|
||||
|
||||
startPort, err := parsePortNumber(start)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("invalid start port '%s': %w", start, err)
|
||||
}
|
||||
if !ok || start == end {
|
||||
return startPort, startPort, nil
|
||||
}
|
||||
|
||||
parts := strings.Split(ports, "-")
|
||||
start, err := strconv.ParseUint(parts[0], 10, 16)
|
||||
endPort, err = parsePortNumber(end)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
return 0, 0, fmt.Errorf("invalid end port '%s': %w", end, err)
|
||||
}
|
||||
end, err := strconv.ParseUint(parts[1], 10, 16)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
if endPort < startPort {
|
||||
return 0, 0, errors.New("invalid port range: " + ports)
|
||||
}
|
||||
if end < start {
|
||||
return 0, 0, errors.New("invalid range specified for port: " + ports)
|
||||
return startPort, endPort, nil
|
||||
}
|
||||
|
||||
// parsePortNumber parses rawPort into an int, unwrapping strconv errors
|
||||
// and returning a single "out of range" error for any value outside 0–65535.
|
||||
func parsePortNumber(rawPort string) (int, error) {
|
||||
if rawPort == "" {
|
||||
return 0, errors.New("value is empty")
|
||||
}
|
||||
return start, end, nil
|
||||
port, err := strconv.ParseInt(rawPort, 10, 0)
|
||||
if err != nil {
|
||||
var numErr *strconv.NumError
|
||||
if errors.As(err, &numErr) {
|
||||
err = numErr.Err
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
if port < 0 || port > 65535 {
|
||||
return 0, errors.New("value out of range (0–65535)")
|
||||
}
|
||||
|
||||
return int(port), nil
|
||||
}
|
||||
|
||||
+27
-15
@@ -34,8 +34,10 @@ func Sort(ports []Port, predicate func(i, j Port) bool) {
|
||||
}
|
||||
|
||||
type portMapEntry struct {
|
||||
port Port
|
||||
binding PortBinding
|
||||
port Port
|
||||
binding *PortBinding
|
||||
portInt int
|
||||
portProto string
|
||||
}
|
||||
|
||||
type portMapSorter []portMapEntry
|
||||
@@ -48,23 +50,36 @@ func (s portMapSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
// 2. larger port
|
||||
// 3. port with tcp protocol
|
||||
func (s portMapSorter) Less(i, j int) bool {
|
||||
pi, pj := s[i].port, s[j].port
|
||||
hpi, hpj := toInt(s[i].binding.HostPort), toInt(s[j].binding.HostPort)
|
||||
return hpi > hpj || pi.Int() > pj.Int() || (pi.Int() == pj.Int() && strings.ToLower(pi.Proto()) == "tcp")
|
||||
pi, pj := s[i].portInt, s[j].portInt
|
||||
var hpi, hpj int
|
||||
if s[i].binding != nil {
|
||||
hpi = toInt(s[i].binding.HostPort)
|
||||
}
|
||||
if s[j].binding != nil {
|
||||
hpj = toInt(s[j].binding.HostPort)
|
||||
}
|
||||
return hpi > hpj || pi > pj || (pi == pj && strings.EqualFold(s[i].portProto, "tcp"))
|
||||
}
|
||||
|
||||
// SortPortMap sorts the list of ports and their respected mapping. The ports
|
||||
// will explicit HostPort will be placed first.
|
||||
func SortPortMap(ports []Port, bindings PortMap) {
|
||||
func SortPortMap(ports []Port, bindings map[Port][]PortBinding) {
|
||||
s := portMapSorter{}
|
||||
for _, p := range ports {
|
||||
portInt, portProto := p.Int(), p.Proto()
|
||||
if binding, ok := bindings[p]; ok && len(binding) > 0 {
|
||||
for _, b := range binding {
|
||||
s = append(s, portMapEntry{port: p, binding: b})
|
||||
s = append(s, portMapEntry{
|
||||
port: p, binding: &b,
|
||||
portInt: portInt, portProto: portProto,
|
||||
})
|
||||
}
|
||||
bindings[p] = []PortBinding{}
|
||||
} else {
|
||||
s = append(s, portMapEntry{port: p})
|
||||
s = append(s, portMapEntry{
|
||||
port: p,
|
||||
portInt: portInt, portProto: portProto,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,16 +96,13 @@ func SortPortMap(ports []Port, bindings PortMap) {
|
||||
i++
|
||||
}
|
||||
// reorder bindings for this port
|
||||
if _, ok := bindings[entry.port]; ok {
|
||||
bindings[entry.port] = append(bindings[entry.port], entry.binding)
|
||||
if entry.binding != nil {
|
||||
bindings[entry.port] = append(bindings[entry.port], *entry.binding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toInt(s string) uint64 {
|
||||
i, _, err := ParsePortRange(s)
|
||||
if err != nil {
|
||||
i = 0
|
||||
}
|
||||
func toInt(s string) int {
|
||||
i, _, _ := parsePortRange(s)
|
||||
return i
|
||||
}
|
||||
|
||||
+30
-30
@@ -1,48 +1,57 @@
|
||||
package sockets
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var errClosed = errors.New("use of closed network connection")
|
||||
|
||||
// InmemSocket implements net.Listener using in-memory only connections.
|
||||
type InmemSocket struct {
|
||||
chConn chan net.Conn
|
||||
chClose chan struct{}
|
||||
addr string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// dummyAddr is used to satisfy net.Addr for the in-mem socket
|
||||
// it is just stored as a string and returns the string for all calls
|
||||
type dummyAddr string
|
||||
|
||||
// NewInmemSocket creates an in-memory only net.Listener
|
||||
// The addr argument can be any string, but is used to satisfy the `Addr()` part
|
||||
// of the net.Listener interface
|
||||
// Network returns the addr string, satisfies net.Addr
|
||||
func (a dummyAddr) Network() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
// String returns the string form
|
||||
func (a dummyAddr) String() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
// InmemSocket implements [net.Listener] using in-memory only connections.
|
||||
type InmemSocket struct {
|
||||
chConn chan net.Conn
|
||||
chClose chan struct{}
|
||||
addr dummyAddr
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewInmemSocket creates an in-memory only [net.Listener]. The addr argument
|
||||
// can be any string, but is used to satisfy the [net.Listener.Addr] part
|
||||
// of the [net.Listener] interface
|
||||
func NewInmemSocket(addr string, bufSize int) *InmemSocket {
|
||||
return &InmemSocket{
|
||||
chConn: make(chan net.Conn, bufSize),
|
||||
chClose: make(chan struct{}),
|
||||
addr: addr,
|
||||
addr: dummyAddr(addr),
|
||||
}
|
||||
}
|
||||
|
||||
// Addr returns the socket's addr string to satisfy net.Listener
|
||||
func (s *InmemSocket) Addr() net.Addr {
|
||||
return dummyAddr(s.addr)
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// Accept implements the Accept method in the Listener interface; it waits for the next call and returns a generic Conn.
|
||||
// Accept implements the Accept method in the Listener interface; it waits
|
||||
// for the next call and returns a generic Conn. It returns a [net.ErrClosed]
|
||||
// if the connection is already closed.
|
||||
func (s *InmemSocket) Accept() (net.Conn, error) {
|
||||
select {
|
||||
case conn := <-s.chConn:
|
||||
return conn, nil
|
||||
case <-s.chClose:
|
||||
return nil, errClosed
|
||||
return nil, net.ErrClosed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,24 +67,15 @@ func (s *InmemSocket) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Dial is used to establish a connection with the in-mem server
|
||||
// Dial is used to establish a connection with the in-mem server.
|
||||
// It returns a [net.ErrClosed] if the connection is already closed.
|
||||
func (s *InmemSocket) Dial(network, addr string) (net.Conn, error) {
|
||||
srvConn, clientConn := net.Pipe()
|
||||
select {
|
||||
case s.chConn <- srvConn:
|
||||
case <-s.chClose:
|
||||
return nil, errClosed
|
||||
return nil, net.ErrClosed
|
||||
}
|
||||
|
||||
return clientConn, nil
|
||||
}
|
||||
|
||||
// Network returns the addr string, satisfies net.Addr
|
||||
func (a dummyAddr) Network() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
// String returns the string form
|
||||
func (a dummyAddr) String() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
package sockets
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetProxyEnv allows access to the uppercase and the lowercase forms of
|
||||
// proxy-related variables. See the Go specification for details on these
|
||||
// variables. https://golang.org/pkg/net/http/
|
||||
//
|
||||
// Deprecated: this function was used as helper for [DialerFromEnvironment] and is no longer used. It will be removed in the next release.
|
||||
func GetProxyEnv(key string) string {
|
||||
proxyValue := os.Getenv(strings.ToUpper(key))
|
||||
if proxyValue == "" {
|
||||
return os.Getenv(strings.ToLower(key))
|
||||
}
|
||||
return proxyValue
|
||||
}
|
||||
|
||||
// DialerFromEnvironment was previously used to configure a net.Dialer to route
|
||||
// connections through a SOCKS proxy.
|
||||
//
|
||||
// Deprecated: SOCKS proxies are now supported by configuring only
|
||||
// http.Transport.Proxy, and no longer require changing http.Transport.Dial.
|
||||
// Therefore, only [sockets.ConfigureTransport] needs to be called, and any
|
||||
// [sockets.DialerFromEnvironment] calls can be dropped.
|
||||
func DialerFromEnvironment(direct *net.Dialer) (*net.Dialer, error) {
|
||||
return direct, nil
|
||||
}
|
||||
+12
-12
@@ -27,11 +27,19 @@ var ErrProtocolNotAvailable = errors.New("protocol not available")
|
||||
// make sure you do it _after_ any subsequent calls to ConfigureTransport is made against the same
|
||||
// [http.Transport].
|
||||
func ConfigureTransport(tr *http.Transport, proto, addr string) error {
|
||||
if tr.MaxIdleConns == 0 {
|
||||
// prevent long-lived processes from leaking connections
|
||||
// due to idle connections not being released.
|
||||
//
|
||||
// TODO: see if we can also address this from the server side; see: https://github.com/moby/moby/issues/45539
|
||||
tr.MaxIdleConns = 6
|
||||
tr.IdleConnTimeout = 30 * time.Second
|
||||
}
|
||||
switch proto {
|
||||
case "unix":
|
||||
return configureUnixTransport(tr, proto, addr)
|
||||
return configureUnixTransport(tr, addr)
|
||||
case "npipe":
|
||||
return configureNpipeTransport(tr, proto, addr)
|
||||
return configureNpipeTransport(tr, addr)
|
||||
default:
|
||||
tr.Proxy = http.ProxyFromEnvironment
|
||||
tr.DisableCompression = false
|
||||
@@ -42,15 +50,7 @@ func ConfigureTransport(tr *http.Transport, proto, addr string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DialPipe connects to a Windows named pipe. It is not supported on
|
||||
// non-Windows platforms.
|
||||
//
|
||||
// Deprecated: use [github.com/Microsoft/go-winio.DialPipe] or [github.com/Microsoft/go-winio.DialPipeContext].
|
||||
func DialPipe(addr string, timeout time.Duration) (net.Conn, error) {
|
||||
return dialPipe(addr, timeout)
|
||||
}
|
||||
|
||||
func configureUnixTransport(tr *http.Transport, proto, addr string) error {
|
||||
func configureUnixTransport(tr *http.Transport, addr string) error {
|
||||
if len(addr) > maxUnixSocketPathSize {
|
||||
return fmt.Errorf("unix socket path %q is too long", addr)
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func configureUnixTransport(tr *http.Transport, proto, addr string) error {
|
||||
Timeout: defaultTimeout,
|
||||
}
|
||||
tr.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||
return dialer.DialContext(ctx, proto, addr)
|
||||
return dialer.DialContext(ctx, "unix", addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+1
-12
@@ -2,17 +2,6 @@
|
||||
|
||||
package sockets
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func configureNpipeTransport(tr *http.Transport, proto, addr string) error {
|
||||
func configureNpipeTransport(any, string) error {
|
||||
return ErrProtocolNotAvailable
|
||||
}
|
||||
|
||||
func dialPipe(_ string, _ time.Duration) (net.Conn, error) {
|
||||
return nil, syscall.EAFNOSUPPORT
|
||||
}
|
||||
|
||||
+1
-6
@@ -4,12 +4,11 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
)
|
||||
|
||||
func configureNpipeTransport(tr *http.Transport, proto, addr string) error {
|
||||
func configureNpipeTransport(tr *http.Transport, addr string) error {
|
||||
// No need for compression in local communications.
|
||||
tr.DisableCompression = true
|
||||
tr.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||
@@ -17,7 +16,3 @@ func configureNpipeTransport(tr *http.Transport, proto, addr string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dialPipe(addr string, timeout time.Duration) (net.Conn, error) {
|
||||
return winio.DialPipe(addr, &timeout)
|
||||
}
|
||||
|
||||
+123
-1
@@ -1,6 +1,128 @@
|
||||
package sockets
|
||||
|
||||
import "net"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// BasePermissions defines the default DACL, which allows Administrators
|
||||
// and LocalSystem full access (similar to defaults used in [moby]);
|
||||
//
|
||||
// - D:P: DACL without inheritance (protected, (P)).
|
||||
// - (A;;GA;;;BA): Allow full access (GA) for built-in Administrators (BA).
|
||||
// - (A;;GA;;;SY); Allow full access (GA) for LocalSystem (SY).
|
||||
// - Any other user is denied access.
|
||||
//
|
||||
// [moby]: https://github.com/moby/moby/blob/6b45c76a233b1b8b56465f76c21c09fd7920e82d/daemon/listeners/listeners_windows.go#L53-L59
|
||||
const BasePermissions = "D:P(A;;GA;;;BA)(A;;GA;;;SY)"
|
||||
|
||||
// WithBasePermissions sets a default DACL, which allows Administrators
|
||||
// and LocalSystem full access (similar to defaults used in [moby]);
|
||||
//
|
||||
// - D:P: DACL without inheritance (protected, (P)).
|
||||
// - (A;;GA;;;BA): Allow full access (GA) for built-in Administrators (BA).
|
||||
// - (A;;GA;;;SY); Allow full access (GA) for LocalSystem (SY).
|
||||
// - Any other user is denied access.
|
||||
//
|
||||
// [moby]: https://github.com/moby/moby/blob/6b45c76a233b1b8b56465f76c21c09fd7920e82d/daemon/listeners/listeners_windows.go#L53-L59
|
||||
func WithBasePermissions() SockOption {
|
||||
return withSDDL(BasePermissions)
|
||||
}
|
||||
|
||||
// WithAdditionalUsersAndGroups modifies the socket file's DACL to grant
|
||||
// access to additional users and groups.
|
||||
//
|
||||
// It sets [BasePermissions] on the socket path and grants the given additional
|
||||
// users and groups to generic read (GR) and write (GW) access. It returns
|
||||
// an error if no groups were given, when failing to resolve any of the
|
||||
// additional users and groups, or when failing to apply the ACL.
|
||||
func WithAdditionalUsersAndGroups(additionalUsersAndGroups []string) SockOption {
|
||||
return func(path string) error {
|
||||
if len(additionalUsersAndGroups) == 0 {
|
||||
return errors.New("no additional users specified")
|
||||
}
|
||||
sd, err := getSecurityDescriptor(additionalUsersAndGroups...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("looking up SID: %w", err)
|
||||
}
|
||||
return withSDDL(sd)(path)
|
||||
}
|
||||
}
|
||||
|
||||
// withSDDL applies the given SDDL to the socket. It returns an error
|
||||
// when failing parse the SDDL, or if the DACL was defaulted.
|
||||
//
|
||||
// TODO(thaJeztah); this is not exported yet, as some of the checks may need review if they're not too opinionated.
|
||||
func withSDDL(sddl string) SockOption {
|
||||
return func(path string) error {
|
||||
sd, err := windows.SecurityDescriptorFromString(sddl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing SDDL: %w", err)
|
||||
}
|
||||
dacl, defaulted, err := sd.DACL()
|
||||
if err != nil {
|
||||
return fmt.Errorf("extracting DACL: %w", err)
|
||||
}
|
||||
if dacl == nil || defaulted {
|
||||
// should never be hit with our [DefaultPermissions],
|
||||
// as it contains "D:" and "P" (protected, don't inherit).
|
||||
return errors.New("no DACL found in security descriptor or defaulted")
|
||||
}
|
||||
return windows.SetNamedSecurityInfo(
|
||||
path,
|
||||
windows.SE_FILE_OBJECT,
|
||||
windows.DACL_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION,
|
||||
nil, // do not change the owner
|
||||
nil, // do not change the owner
|
||||
dacl,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// NewUnixSocket creates a new unix socket.
|
||||
//
|
||||
// It sets [BasePermissions] on the socket path and grants the given additional
|
||||
// users and groups to generic read (GR) and write (GW) access. It returns
|
||||
// an error when failing to resolve any of the additional users and groups,
|
||||
// or when failing to apply the ACL.
|
||||
func NewUnixSocket(path string, additionalUsersAndGroups []string) (net.Listener, error) {
|
||||
var opts []SockOption
|
||||
if len(additionalUsersAndGroups) > 0 {
|
||||
opts = append(opts, WithAdditionalUsersAndGroups(additionalUsersAndGroups))
|
||||
} else {
|
||||
opts = append(opts, WithBasePermissions())
|
||||
}
|
||||
return NewUnixSocketWithOpts(path, opts...)
|
||||
}
|
||||
|
||||
// getSecurityDescriptor returns the DACL for the Unix socket.
|
||||
//
|
||||
// By default, it grants [BasePermissions], but allows for additional
|
||||
// users and groups to get generic read (GR) and write (GW) access. It
|
||||
// returns an error when failing to resolve any of the additional users
|
||||
// and groups.
|
||||
func getSecurityDescriptor(additionalUsersAndGroups ...string) (string, error) {
|
||||
sddl := BasePermissions
|
||||
|
||||
// Grant generic read (GR) and write (GW) access to whatever
|
||||
// additional users or groups were specified.
|
||||
//
|
||||
// TODO(thaJeztah): should we fail on, or remove duplicates?
|
||||
for _, g := range additionalUsersAndGroups {
|
||||
sid, err := winio.LookupSidByName(strings.TrimSpace(g))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("looking up SID: %w", err)
|
||||
}
|
||||
sddl += fmt.Sprintf("(A;;GRGW;;;%s)", sid)
|
||||
}
|
||||
return sddl, nil
|
||||
}
|
||||
|
||||
func listenUnix(path string) (net.Listener, error) {
|
||||
return net.Listen("unix", path)
|
||||
|
||||
+7
-11
@@ -1,16 +1,12 @@
|
||||
package tlsconfig
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"runtime"
|
||||
)
|
||||
import "crypto/x509"
|
||||
|
||||
// SystemCertPool returns a copy of the system cert pool,
|
||||
// returns an error if failed to load or empty pool on windows.
|
||||
// SystemCertPool returns a copy of the system cert pool.
|
||||
//
|
||||
// Deprecated: use [x509.SystemCertPool] instead.
|
||||
//
|
||||
//go:fix inline
|
||||
func SystemCertPool() (*x509.CertPool, error) {
|
||||
certpool, err := x509.SystemCertPool()
|
||||
if err != nil && runtime.GOOS == "windows" {
|
||||
return x509.NewCertPool(), nil
|
||||
}
|
||||
return certpool, err
|
||||
return x509.SystemCertPool()
|
||||
}
|
||||
|
||||
+20
-8
@@ -34,6 +34,9 @@ type Options struct {
|
||||
// the system pool will be used.
|
||||
ExclusiveRootPools bool
|
||||
MinVersion uint16
|
||||
|
||||
// systemCertPool allows mocking the system cert-pool for testing.
|
||||
systemCertPool func() (*x509.CertPool, error)
|
||||
}
|
||||
|
||||
// DefaultServerAcceptedCiphers should be uses by code which already has a crypto/tls
|
||||
@@ -47,6 +50,8 @@ var defaultCipherSuites = []uint16{
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
}
|
||||
|
||||
// ServerDefault returns a secure-enough TLS configuration for the server TLS configuration.
|
||||
@@ -75,26 +80,33 @@ func defaultConfig(ops ...func(*tls.Config)) *tls.Config {
|
||||
}
|
||||
|
||||
// certPool returns an X.509 certificate pool from `caFile`, the certificate file.
|
||||
func certPool(caFile string, exclusivePool bool) (*x509.CertPool, error) {
|
||||
func certPool(opts Options) (*x509.CertPool, error) {
|
||||
// If we should verify the server, we need to load a trusted ca
|
||||
var (
|
||||
pool *x509.CertPool
|
||||
err error
|
||||
)
|
||||
if exclusivePool {
|
||||
if opts.ExclusiveRootPools {
|
||||
pool = x509.NewCertPool()
|
||||
} else {
|
||||
pool, err = SystemCertPool()
|
||||
if opts.systemCertPool != nil {
|
||||
pool, err = opts.systemCertPool()
|
||||
} else {
|
||||
pool, err = x509.SystemCertPool()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read system certificates: %v", err)
|
||||
}
|
||||
}
|
||||
pemData, err := os.ReadFile(caFile)
|
||||
if opts.CAFile == "" {
|
||||
return pool, nil
|
||||
}
|
||||
pemData, err := os.ReadFile(opts.CAFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read CA certificate %q: %v", caFile, err)
|
||||
return nil, fmt.Errorf("could not read CA certificate %q: %v", opts.CAFile, err)
|
||||
}
|
||||
if !pool.AppendCertsFromPEM(pemData) {
|
||||
return nil, fmt.Errorf("failed to append certificates from PEM file: %q", caFile)
|
||||
return nil, fmt.Errorf("failed to append certificates from PEM file: %q", opts.CAFile)
|
||||
}
|
||||
return pool, nil
|
||||
}
|
||||
@@ -197,7 +209,7 @@ func Client(options Options) (*tls.Config, error) {
|
||||
tlsConfig := defaultConfig()
|
||||
tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify
|
||||
if !options.InsecureSkipVerify && options.CAFile != "" {
|
||||
CAs, err := certPool(options.CAFile, options.ExclusiveRootPools)
|
||||
CAs, err := certPool(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -230,7 +242,7 @@ func Server(options Options) (*tls.Config, error) {
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{tlsCert}
|
||||
if options.ClientAuth >= tls.VerifyClientCertIfGiven && options.CAFile != "" {
|
||||
CAs, err := certPool(options.CAFile, options.ExclusiveRootPools)
|
||||
CAs, err := certPool(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+69
-22
@@ -24,10 +24,48 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
zerowidth table // combining + nonprint merged for faster zero-width lookup
|
||||
widewidth table // ambiguous + doublewidth merged for EA path
|
||||
)
|
||||
|
||||
func init() {
|
||||
zerowidth = mergeIntervals(combining, nonprint)
|
||||
widewidth = mergeIntervals(ambiguous, doublewidth)
|
||||
handleEnv()
|
||||
}
|
||||
|
||||
func mergeIntervals(t1, t2 table) table {
|
||||
merged := make(table, 0, len(t1)+len(t2))
|
||||
i, j := 0, 0
|
||||
for i < len(t1) && j < len(t2) {
|
||||
if t1[i].first <= t2[j].first {
|
||||
merged = append(merged, t1[i])
|
||||
i++
|
||||
} else {
|
||||
merged = append(merged, t2[j])
|
||||
j++
|
||||
}
|
||||
}
|
||||
merged = append(merged, t1[i:]...)
|
||||
merged = append(merged, t2[j:]...)
|
||||
if len(merged) == 0 {
|
||||
return merged
|
||||
}
|
||||
result := merged[:1]
|
||||
for _, iv := range merged[1:] {
|
||||
last := &result[len(result)-1]
|
||||
if iv.first <= last.last+1 {
|
||||
if iv.last > last.last {
|
||||
last.last = iv.last
|
||||
}
|
||||
} else {
|
||||
result = append(result, iv)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func handleEnv() {
|
||||
env := os.Getenv("RUNEWIDTH_EASTASIAN")
|
||||
if env == "" {
|
||||
@@ -52,15 +90,6 @@ type interval struct {
|
||||
|
||||
type table []interval
|
||||
|
||||
func inTables(r rune, ts ...table) bool {
|
||||
for _, t := range ts {
|
||||
if inTable(r, t) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func inTable(r rune, t table) bool {
|
||||
if r < t[0].first {
|
||||
return false
|
||||
@@ -131,9 +160,7 @@ func (c *Condition) RuneWidth(r rune) int {
|
||||
return 0
|
||||
case r < 0x300:
|
||||
return 1
|
||||
case inTable(r, narrow):
|
||||
return 1
|
||||
case inTables(r, nonprint, combining):
|
||||
case inTable(r, zerowidth):
|
||||
return 0
|
||||
case inTable(r, doublewidth):
|
||||
return 2
|
||||
@@ -142,13 +169,13 @@ func (c *Condition) RuneWidth(r rune) int {
|
||||
}
|
||||
} else {
|
||||
switch {
|
||||
case inTables(r, nonprint, combining):
|
||||
case inTable(r, zerowidth):
|
||||
return 0
|
||||
case inTable(r, narrow):
|
||||
return 1
|
||||
case inTables(r, ambiguous, doublewidth):
|
||||
case inTable(r, widewidth):
|
||||
return 2
|
||||
case !c.StrictEmojiNeutral && inTables(r, ambiguous, emoji, narrow):
|
||||
case !c.StrictEmojiNeutral && inTable(r, emoji):
|
||||
return 2
|
||||
default:
|
||||
return 1
|
||||
@@ -185,6 +212,16 @@ func (c *Condition) StringWidth(s string) (width int) {
|
||||
return c.RuneWidth(r)
|
||||
}
|
||||
}
|
||||
// ASCII fast path: no grapheme clustering needed for pure ASCII
|
||||
if isAllASCII(s) {
|
||||
for i := 0; i < len(s); i++ {
|
||||
b := s[i]
|
||||
if b >= 0x20 && b != 0x7F {
|
||||
width++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
g := graphemes.FromString(s)
|
||||
for g.Next() {
|
||||
var chWidth int
|
||||
@@ -199,6 +236,15 @@ func (c *Condition) StringWidth(s string) (width int) {
|
||||
return
|
||||
}
|
||||
|
||||
func isAllASCII(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] >= 0x80 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Truncate return string truncated with w cells
|
||||
func (c *Condition) Truncate(s string, w int, tail string) string {
|
||||
if c.StringWidth(s) <= w {
|
||||
@@ -264,24 +310,25 @@ func (c *Condition) TruncateLeft(s string, w int, prefix string) string {
|
||||
// Wrap return string wrapped with w cells
|
||||
func (c *Condition) Wrap(s string, w int) string {
|
||||
width := 0
|
||||
out := ""
|
||||
var out strings.Builder
|
||||
out.Grow(len(s) + len(s)/w + 1)
|
||||
for _, r := range s {
|
||||
cw := c.RuneWidth(r)
|
||||
if r == '\n' {
|
||||
out += string(r)
|
||||
out.WriteRune(r)
|
||||
width = 0
|
||||
continue
|
||||
} else if width+cw > w {
|
||||
out += "\n"
|
||||
out.WriteByte('\n')
|
||||
width = 0
|
||||
out += string(r)
|
||||
out.WriteRune(r)
|
||||
width += cw
|
||||
continue
|
||||
}
|
||||
out += string(r)
|
||||
out.WriteRune(r)
|
||||
width += cw
|
||||
}
|
||||
return out
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// FillLeft return string filled in left by spaces in w cells
|
||||
@@ -320,7 +367,7 @@ func RuneWidth(r rune) int {
|
||||
|
||||
// IsAmbiguousWidth returns whether is ambiguous width or not.
|
||||
func IsAmbiguousWidth(r rune) bool {
|
||||
return inTables(r, private, ambiguous)
|
||||
return inTable(r, private) || inTable(r, ambiguous)
|
||||
}
|
||||
|
||||
// IsCombiningWidth returns whether is combining width or not.
|
||||
|
||||
+28
-16
@@ -12,8 +12,9 @@ const rs = 0x1E
|
||||
|
||||
type DecoderFn func(v any) error
|
||||
|
||||
// NewJSONStreamDecoder builds adequate DecoderFn to read json records formatted with specified content-type
|
||||
func NewJSONStreamDecoder(r io.Reader, contentType string) DecoderFn {
|
||||
// NewJSONStreamDecoder builds a DecoderFn to read a stream of JSON records
|
||||
// formatted with the specified content-type.
|
||||
func NewJSONStreamDecoder(r io.Reader, contentType types.MediaType) DecoderFn {
|
||||
switch contentType {
|
||||
case types.MediaTypeJSONSequence:
|
||||
return json.NewDecoder(NewRSFilterReader(r)).Decode
|
||||
@@ -24,27 +25,38 @@ func NewJSONStreamDecoder(r io.Reader, contentType string) DecoderFn {
|
||||
}
|
||||
}
|
||||
|
||||
// RSFilterReader wraps an io.Reader and filters out ASCII RS characters
|
||||
type RSFilterReader struct {
|
||||
type rsFilterReader struct {
|
||||
reader io.Reader
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
// NewRSFilterReader creates a new RSFilterReader that filters out RS characters
|
||||
func NewRSFilterReader(r io.Reader) *RSFilterReader {
|
||||
return &RSFilterReader{
|
||||
reader: r,
|
||||
buffer: make([]byte, 4096), // Internal buffer for reading chunks
|
||||
}
|
||||
// NewRSFilterReader creates an [io.Reader] that filters out ASCII Record Separators (RS).
|
||||
func NewRSFilterReader(r io.Reader) io.Reader {
|
||||
return &rsFilterReader{reader: r}
|
||||
}
|
||||
|
||||
// Read implements the io.Reader interface, filtering out RS characters
|
||||
func (r *RSFilterReader) Read(p []byte) (n int, err error) {
|
||||
func (r *rsFilterReader) Read(p []byte) (int, error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
n, err = r.reader.Read(p)
|
||||
filtered := slices.DeleteFunc(p[:n], func(b byte) bool { return b == rs })
|
||||
return len(filtered), err
|
||||
for {
|
||||
n, err := r.reader.Read(p)
|
||||
if n == 0 {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
filtered := slices.DeleteFunc(p[:n], func(b byte) bool { return b == rs })
|
||||
n = len(filtered)
|
||||
if err != nil {
|
||||
if err == io.EOF && n > 0 {
|
||||
return n, nil
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
if n == 0 {
|
||||
// Avoid returning (0, nil) after consuming input; keep reading until data or an error (e.g., EOF).
|
||||
continue
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
|
||||
+64
-13
@@ -8,6 +8,8 @@ import (
|
||||
"iter"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/errdefs/pkg/errhttp"
|
||||
|
||||
"github.com/moby/moby/api/types/jsonstream"
|
||||
)
|
||||
|
||||
@@ -44,41 +46,90 @@ func (r Stream) Close() error {
|
||||
|
||||
var _ io.ReadCloser = Stream{}
|
||||
|
||||
// JSONMessages decodes the response stream as a sequence of JSONMessages.
|
||||
// if stream ends or context is cancelled, the underlying [io.Reader] is closed.
|
||||
// JSONMessages decodes the response stream as a sequence of [jsonstream.Message].
|
||||
// The underlying [io.Reader] is closed when the stream ends or if the context
|
||||
// is cancelled.
|
||||
func (r Stream) JSONMessages(ctx context.Context) iter.Seq2[jsonstream.Message, error] {
|
||||
stop := context.AfterFunc(ctx, func() {
|
||||
_ = r.Close()
|
||||
})
|
||||
dec := json.NewDecoder(r)
|
||||
return func(yield func(jsonstream.Message, error) bool) {
|
||||
defer func() {
|
||||
stop() // unregister AfterFunc
|
||||
r.Close()
|
||||
_ = r.Close()
|
||||
}()
|
||||
|
||||
dec := json.NewDecoder(r)
|
||||
for {
|
||||
var jm jsonstream.Message
|
||||
err := dec.Decode(&jm)
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if ctx.Err() != nil {
|
||||
yield(jm, ctx.Err())
|
||||
if err := dec.Decode(&jm); err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
return
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
// Do not return decoding errors if the context was
|
||||
// cancelled, because the decoding errors may be due
|
||||
// to the context being cancelled.
|
||||
yield(jsonstream.Message{}, err)
|
||||
return
|
||||
}
|
||||
yield(jsonstream.Message{}, err)
|
||||
return
|
||||
}
|
||||
if !yield(jm, err) {
|
||||
if !yield(jm, nil) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait waits for operation to complete and detects errors reported as JSONMessage
|
||||
// Wait consumes the stream until completion.
|
||||
//
|
||||
// It returns nil if the operation completes successfully. Errors are
|
||||
// returned if the context is canceled, a decoding/transport failure
|
||||
// occurs, or a JSON message reports an error ([jsonstream.Message.Error]).
|
||||
func (r Stream) Wait(ctx context.Context) error {
|
||||
for _, err := range r.JSONMessages(ctx) {
|
||||
for jm, err := range r.JSONMessages(ctx) {
|
||||
if err != nil {
|
||||
// decode, transport and context cancellation errors.
|
||||
return err
|
||||
}
|
||||
if jm.Error != nil {
|
||||
// push/pull failures.
|
||||
return httpErrorFromStatusCode(jm.Error, jm.Error.Code)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpError struct {
|
||||
err error
|
||||
errdef error
|
||||
}
|
||||
|
||||
func (e *httpError) Error() string {
|
||||
return e.err.Error()
|
||||
}
|
||||
|
||||
func (e *httpError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func (e *httpError) Is(target error) bool {
|
||||
return errors.Is(e.errdef, target)
|
||||
}
|
||||
|
||||
// httpErrorFromStatusCode creates an errdef error, based on the provided HTTP status-code
|
||||
//
|
||||
// TODO(thaJeztah): unify with the implementation in client and move to an internal package
|
||||
// see https://github.com/moby/moby/blob/client/v0.4.0/client/errors.go#L76-L114
|
||||
func httpErrorFromStatusCode(err error, statusCode int) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &httpError{
|
||||
err: err,
|
||||
errdef: errhttp.ToNative(statusCode),
|
||||
}
|
||||
}
|
||||
|
||||
+8
-5
@@ -244,12 +244,15 @@ func imageDiskUsageFromLegacyAPI(du *legacyDiskUsage) ImagesDiskUsage {
|
||||
Items: du.Images,
|
||||
}
|
||||
|
||||
for _, i := range idu.Items {
|
||||
if i.Containers > 0 {
|
||||
for _, img := range idu.Items {
|
||||
switch {
|
||||
case img.Containers < 0:
|
||||
// No container-count information available; skip (assume it's in use).
|
||||
case img.Containers > 0:
|
||||
idu.ActiveCount++
|
||||
} else if i.Size != -1 && i.SharedSize != -1 {
|
||||
// Only count reclaimable size if we have size information
|
||||
idu.Reclaimable += (i.Size - i.SharedSize)
|
||||
case img.Containers == 0 && img.Size != -1 && img.SharedSize != -1:
|
||||
reclaimable := img.Size - img.SharedSize
|
||||
idu.Reclaimable += reclaimable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+60
-60
@@ -9,7 +9,7 @@ import (
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
github_com_moby_swarmkit_v2_api_deepcopy "github.com/moby/swarmkit/v2/api/deepcopy"
|
||||
raftselector "github.com/moby/swarmkit/v2/manager/raftselector"
|
||||
raftpb "go.etcd.io/etcd/raft/v3/raftpb"
|
||||
raftpb "go.etcd.io/raft/v3/raftpb"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
metadata "google.golang.org/grpc/metadata"
|
||||
@@ -731,73 +731,73 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_d2c32e1e3c930c15 = []byte{
|
||||
// 1046 bytes of a gzipped FileDescriptorProto
|
||||
// 1044 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x96, 0x41, 0x73, 0xdb, 0x44,
|
||||
0x14, 0xc7, 0x25, 0x5b, 0x75, 0x92, 0xe7, 0x36, 0x09, 0x5b, 0x12, 0x14, 0xb5, 0x28, 0xae, 0xdb,
|
||||
0x14, 0xc7, 0x25, 0x5b, 0x75, 0x9a, 0xe7, 0x36, 0x09, 0x5b, 0x12, 0x14, 0xb5, 0x28, 0xae, 0x9b,
|
||||
0x19, 0x9c, 0x90, 0xc8, 0x83, 0xcb, 0x4c, 0x99, 0x42, 0x0f, 0x71, 0xe2, 0x99, 0x98, 0xb4, 0x4e,
|
||||
0x47, 0x49, 0x4a, 0x6f, 0x41, 0x96, 0x36, 0xae, 0xb0, 0xad, 0x35, 0xbb, 0x6b, 0x07, 0x2e, 0x4c,
|
||||
0x8f, 0x90, 0x13, 0x37, 0x18, 0x66, 0x3a, 0x1c, 0xe0, 0xdc, 0x0f, 0xc0, 0x27, 0xc8, 0x70, 0xea,
|
||||
0x8d, 0x9e, 0x32, 0xd4, 0xb9, 0xc3, 0x17, 0xe0, 0xc0, 0xec, 0x4a, 0x4a, 0x42, 0x22, 0x3b, 0x3e,
|
||||
0x70, 0xd2, 0x8e, 0xf7, 0xf7, 0x7f, 0xff, 0xb7, 0xbb, 0x6f, 0xdf, 0x1a, 0x16, 0x1a, 0x3e, 0x7f,
|
||||
0x70, 0xf2, 0x8e, 0xf7, 0xf7, 0x7f, 0xff, 0xb7, 0xbb, 0x6f, 0xdf, 0x0a, 0x16, 0x1a, 0x3e, 0x7f,
|
||||
0xd6, 0xad, 0x5b, 0x2e, 0x69, 0x17, 0x3d, 0xe2, 0x36, 0x31, 0x2d, 0xb2, 0x7d, 0x87, 0xb6, 0x9b,
|
||||
0x3e, 0x2f, 0x3a, 0x1d, 0xbf, 0x48, 0x9d, 0x3d, 0x6e, 0x75, 0x28, 0xe1, 0x04, 0xa1, 0x70, 0xde,
|
||||
0x8a, 0xe7, 0xad, 0xde, 0x07, 0xc6, 0xd2, 0x25, 0x72, 0x52, 0xff, 0x02, 0xbb, 0x9c, 0x85, 0x11,
|
||||
0x8c, 0xc5, 0x4b, 0x68, 0xfe, 0x75, 0x07, 0xc7, 0xec, 0x42, 0x83, 0x58, 0x98, 0xbb, 0x9e, 0xe5,
|
||||
0x93, 0xa2, 0xf8, 0xca, 0x4c, 0x8a, 0xbd, 0xbb, 0xf2, 0xdb, 0xa9, 0x9f, 0x49, 0xcc, 0x78, 0xbb,
|
||||
0x41, 0x1a, 0x44, 0x0e, 0x8b, 0x62, 0x14, 0xfd, 0x7a, 0x6f, 0x88, 0x99, 0x24, 0xea, 0xdd, 0xbd,
|
||||
0x62, 0xa7, 0xd5, 0x6d, 0xf8, 0x41, 0xf4, 0x09, 0x85, 0xf9, 0x97, 0x2a, 0x80, 0xed, 0xec, 0xf1,
|
||||
0x47, 0xb8, 0x5d, 0xc7, 0x14, 0xdd, 0x86, 0x31, 0xe1, 0xb5, 0xeb, 0x7b, 0xba, 0x9a, 0x53, 0x0b,
|
||||
0x5a, 0x19, 0xfa, 0x47, 0xf3, 0x19, 0x01, 0x54, 0xd7, 0xec, 0x8c, 0x98, 0xaa, 0x7a, 0x02, 0x0a,
|
||||
0x88, 0x87, 0x05, 0x94, 0xca, 0xa9, 0x85, 0x89, 0x10, 0xaa, 0x11, 0x0f, 0x0b, 0x48, 0x4c, 0x55,
|
||||
0x3d, 0x84, 0x40, 0x73, 0x3c, 0x8f, 0xea, 0x69, 0x41, 0xd8, 0x72, 0x8c, 0xca, 0x90, 0x61, 0xdc,
|
||||
0xe1, 0x5d, 0xa6, 0x6b, 0x39, 0xb5, 0x90, 0x2d, 0xdd, 0xb1, 0x2e, 0xee, 0xb2, 0x75, 0x9a, 0xcd,
|
||||
0x96, 0x64, 0xcb, 0xda, 0xe1, 0xd1, 0xbc, 0x62, 0x47, 0xca, 0xfc, 0x2d, 0xc8, 0x7e, 0x4a, 0xfc,
|
||||
0xc0, 0xc6, 0x5f, 0x76, 0x31, 0xe3, 0x27, 0x36, 0xea, 0xa9, 0x4d, 0xfe, 0x27, 0x15, 0xae, 0x86,
|
||||
0x0c, 0xeb, 0x90, 0x80, 0xe1, 0xd1, 0x56, 0xf5, 0x11, 0x8c, 0xb5, 0xa5, 0x2d, 0xd3, 0x53, 0xb9,
|
||||
0x74, 0x21, 0x5b, 0x32, 0x87, 0x67, 0x67, 0xc7, 0x38, 0x7a, 0x1f, 0xa6, 0x28, 0x6e, 0x93, 0x1e,
|
||||
0xf6, 0x76, 0xe3, 0x08, 0xe9, 0x5c, 0xba, 0xa0, 0x95, 0x53, 0xd3, 0x8a, 0x3d, 0x19, 0x4d, 0x85,
|
||||
0x22, 0x96, 0x2f, 0xc3, 0xd5, 0x87, 0xd8, 0xe9, 0xe1, 0x78, 0x01, 0x25, 0xd0, 0xc4, 0x8e, 0xc9,
|
||||
0xc4, 0x2e, 0xf7, 0x94, 0x6c, 0x7e, 0x0a, 0xae, 0x45, 0x31, 0xc2, 0x05, 0xe6, 0x1f, 0xc2, 0xdc,
|
||||
0x63, 0x4a, 0x5c, 0xcc, 0x58, 0xc8, 0x32, 0xe6, 0x34, 0x4e, 0x1c, 0x16, 0xc4, 0xc2, 0xe4, 0x2f,
|
||||
0x91, 0xc9, 0x94, 0x15, 0x96, 0x95, 0x15, 0x83, 0xf1, 0xfc, 0x7d, 0xed, 0xf9, 0x0f, 0x79, 0x25,
|
||||
0x7f, 0x13, 0x8c, 0xa4, 0x68, 0x91, 0xd7, 0x06, 0xe8, 0x5b, 0x9c, 0x62, 0xa7, 0xfd, 0x7f, 0x58,
|
||||
0xdd, 0x80, 0xb9, 0x84, 0x60, 0x91, 0xd3, 0x27, 0x30, 0x63, 0x63, 0x46, 0x5a, 0x3d, 0xbc, 0xe2,
|
||||
0x79, 0x54, 0xa4, 0x13, 0xd9, 0x8c, 0x72, 0x9e, 0xf9, 0x25, 0x98, 0x3d, 0xaf, 0x8e, 0xca, 0x21,
|
||||
0xa9, 0x66, 0x5a, 0x70, 0xbd, 0x1a, 0x70, 0x4c, 0x03, 0xa7, 0x25, 0xe2, 0xc4, 0x4e, 0xb3, 0x90,
|
||||
0x3a, 0x31, 0xc9, 0xf4, 0x8f, 0xe6, 0x53, 0xd5, 0x35, 0x3b, 0xe5, 0x7b, 0xe8, 0x01, 0x64, 0x1c,
|
||||
0x97, 0xfb, 0x24, 0x88, 0x6a, 0x65, 0x3e, 0xe9, 0xdc, 0xb6, 0x38, 0xa1, 0x78, 0x45, 0x62, 0x71,
|
||||
0x11, 0x87, 0xa2, 0xfc, 0x3f, 0x1a, 0x64, 0xcf, 0xcc, 0xa2, 0x8f, 0x4f, 0xc2, 0x09, 0xab, 0xc9,
|
||||
0xd2, 0xed, 0x4b, 0xc2, 0x6d, 0xf8, 0x81, 0x17, 0x07, 0x43, 0x56, 0x54, 0x41, 0x29, 0xb9, 0xe3,
|
||||
0x7a, 0x92, 0x54, 0xdc, 0xcd, 0x75, 0x25, 0xac, 0x1e, 0x74, 0x0f, 0xc6, 0x18, 0xa6, 0x3d, 0xdf,
|
||||
0xc5, 0xf2, 0x72, 0x66, 0x4b, 0x37, 0x12, 0xdd, 0x42, 0x64, 0x5d, 0xb1, 0x63, 0x5a, 0x18, 0x71,
|
||||
0x87, 0x35, 0xa3, 0xcb, 0x9b, 0x68, 0xb4, 0xed, 0xb0, 0xa6, 0x30, 0x12, 0x9c, 0x30, 0x0a, 0x30,
|
||||
0xdf, 0x27, 0xb4, 0xa9, 0x5f, 0x19, 0x6c, 0x54, 0x0b, 0x11, 0x61, 0x14, 0xd1, 0x42, 0xe8, 0xb6,
|
||||
0xba, 0x8c, 0x63, 0xaa, 0x67, 0x06, 0x0b, 0x57, 0x43, 0x44, 0x08, 0x23, 0x1a, 0x7d, 0x08, 0x19,
|
||||
0x86, 0x5d, 0x8a, 0xb9, 0x3e, 0x26, 0x75, 0x46, 0xf2, 0xca, 0x04, 0xb1, 0x2e, 0x5a, 0x8a, 0x1c,
|
||||
0xa1, 0xfb, 0x30, 0x4e, 0x31, 0x23, 0x5d, 0xea, 0x62, 0x7d, 0x5c, 0xea, 0x6e, 0x26, 0x5e, 0xc3,
|
||||
0x88, 0x59, 0x57, 0xec, 0x13, 0x1e, 0x3d, 0x80, 0x09, 0xfc, 0x15, 0xc7, 0x01, 0x13, 0x87, 0x37,
|
||||
0x21, 0xc5, 0xef, 0x26, 0x89, 0x2b, 0x31, 0xb4, 0xae, 0xd8, 0xa7, 0x0a, 0x91, 0xb0, 0x4b, 0x82,
|
||||
0x3d, 0xbf, 0xa1, 0xc3, 0xe0, 0x84, 0x57, 0x25, 0x21, 0x12, 0x0e, 0x59, 0xa1, 0xea, 0x91, 0x56,
|
||||
0xb7, 0x8d, 0xf5, 0xec, 0x60, 0xd5, 0x13, 0x49, 0x08, 0x55, 0xc8, 0x96, 0xc7, 0x21, 0xc3, 0x1d,
|
||||
0xda, 0xc0, 0x7c, 0xf1, 0x6f, 0x15, 0xa6, 0xce, 0x55, 0x13, 0x7a, 0x0f, 0xc6, 0x76, 0x6a, 0x1b,
|
||||
0xb5, 0xcd, 0xcf, 0x6a, 0xd3, 0x8a, 0x61, 0x1c, 0xbc, 0xc8, 0xcd, 0x9e, 0x23, 0x76, 0x82, 0x66,
|
||||
0x40, 0xf6, 0x03, 0x54, 0x82, 0xeb, 0x5b, 0xdb, 0x9b, 0x76, 0x65, 0x77, 0x65, 0x75, 0xbb, 0xba,
|
||||
0x59, 0xdb, 0x5d, 0xb5, 0x2b, 0x2b, 0xdb, 0x95, 0x69, 0xd5, 0x98, 0x3b, 0x78, 0x91, 0x9b, 0x39,
|
||||
0x27, 0x5a, 0xa5, 0xd8, 0xe1, 0xf8, 0x82, 0x66, 0xe7, 0xf1, 0x9a, 0xd0, 0xa4, 0x12, 0x35, 0x3b,
|
||||
0x1d, 0x2f, 0x49, 0x63, 0x57, 0x1e, 0x6d, 0x3e, 0xa9, 0x4c, 0xa7, 0x13, 0x35, 0xb6, 0x6c, 0xb2,
|
||||
0xc6, 0x3b, 0xdf, 0xfe, 0x62, 0x2a, 0xbf, 0xfd, 0x6a, 0x9e, 0x5f, 0x5d, 0xe9, 0xe7, 0x34, 0x68,
|
||||
0xe2, 0x5e, 0xa3, 0x03, 0x15, 0xd0, 0xc5, 0xe6, 0x86, 0x96, 0x93, 0x76, 0x70, 0x60, 0x4b, 0x35,
|
||||
0xac, 0x51, 0xf1, 0xa8, 0x93, 0xcd, 0xfc, 0xfe, 0xf2, 0xaf, 0x1f, 0x53, 0x53, 0x70, 0x4d, 0xf2,
|
||||
0xcb, 0x6d, 0x27, 0x70, 0x1a, 0x98, 0xa2, 0xef, 0x54, 0x78, 0xeb, 0x42, 0xfb, 0x43, 0x4b, 0xc9,
|
||||
0x97, 0x3f, 0xb9, 0xe5, 0x1a, 0xcb, 0x23, 0xd2, 0x43, 0x33, 0x29, 0xa8, 0xe8, 0x1b, 0x98, 0xfc,
|
||||
0x6f, 0xbb, 0x44, 0x0b, 0x83, 0x2e, 0xc1, 0x85, 0x86, 0x6c, 0x2c, 0x8e, 0x82, 0x0e, 0xcd, 0xa0,
|
||||
0xf4, 0x87, 0x0a, 0x93, 0xa7, 0x0f, 0x1d, 0x7b, 0xe6, 0x77, 0xd0, 0xe7, 0xa0, 0x89, 0x67, 0x1c,
|
||||
0x25, 0x36, 0xd7, 0x33, 0x7f, 0x02, 0x8c, 0xdc, 0x60, 0x60, 0xf8, 0x01, 0xb8, 0x70, 0x45, 0x3e,
|
||||
0xa4, 0x28, 0x31, 0xc2, 0xd9, 0x77, 0xda, 0xb8, 0x35, 0x84, 0x18, 0x6a, 0x52, 0xbe, 0x73, 0xf8,
|
||||
0xc6, 0x54, 0x5e, 0xbf, 0x31, 0x95, 0xe7, 0x7d, 0x53, 0x3d, 0xec, 0x9b, 0xea, 0xab, 0xbe, 0xa9,
|
||||
0xfe, 0xd9, 0x37, 0xd5, 0xef, 0x8f, 0x4d, 0xe5, 0xd5, 0xb1, 0xa9, 0xbc, 0x3e, 0x36, 0x95, 0xa7,
|
||||
0xe9, 0xa7, 0x5a, 0x3d, 0x23, 0xff, 0x93, 0xdd, 0xfd, 0x37, 0x00, 0x00, 0xff, 0xff, 0x4a, 0x56,
|
||||
0x23, 0xf6, 0xa8, 0x0a, 0x00, 0x00,
|
||||
0x8c, 0xc5, 0x4b, 0x68, 0xfe, 0x75, 0x07, 0xc7, 0xec, 0x7c, 0x83, 0x58, 0x98, 0xbb, 0x9e, 0xe5,
|
||||
0x13, 0x99, 0x44, 0xb1, 0x77, 0x57, 0xfe, 0x76, 0xea, 0x67, 0x72, 0x32, 0xde, 0x6e, 0x90, 0x06,
|
||||
0x91, 0xc3, 0xa2, 0x18, 0x45, 0xff, 0xde, 0x1b, 0xe2, 0x23, 0x89, 0x7a, 0x77, 0xaf, 0xd8, 0x69,
|
||||
0x75, 0x1b, 0x7e, 0x10, 0xfd, 0x84, 0xc2, 0xfc, 0x4b, 0x15, 0xc0, 0x76, 0xf6, 0xf8, 0x23, 0xdc,
|
||||
0xae, 0x63, 0x8a, 0xee, 0xc0, 0x98, 0xf0, 0xda, 0xf5, 0x3d, 0x5d, 0xcd, 0xa9, 0x05, 0xad, 0x0c,
|
||||
0xfd, 0xa3, 0xb9, 0x8c, 0x00, 0xaa, 0x6b, 0x76, 0x46, 0x4c, 0x55, 0x3d, 0x01, 0x05, 0xc4, 0xc3,
|
||||
0x02, 0x4a, 0xe5, 0xd4, 0xc2, 0x78, 0x08, 0xd5, 0x88, 0x87, 0x05, 0x24, 0xa6, 0xaa, 0x1e, 0x42,
|
||||
0xa0, 0x39, 0x9e, 0x47, 0xf5, 0xb4, 0x20, 0x6c, 0x39, 0x46, 0x65, 0xc8, 0x30, 0xee, 0xf0, 0x2e,
|
||||
0xd3, 0xb5, 0x9c, 0x5a, 0xc8, 0x96, 0xe6, 0xad, 0x8b, 0x1b, 0x6c, 0x9d, 0x66, 0xb3, 0x25, 0xd9,
|
||||
0xb2, 0x76, 0x78, 0x34, 0xa7, 0xd8, 0x91, 0x32, 0x7f, 0x1b, 0xb2, 0x9f, 0x12, 0x3f, 0xb0, 0xf1,
|
||||
0x97, 0x5d, 0xcc, 0xf8, 0x89, 0x8d, 0x7a, 0x6a, 0x93, 0xff, 0x49, 0x85, 0x6b, 0x21, 0xc3, 0x3a,
|
||||
0x24, 0x60, 0x78, 0xb4, 0x55, 0x7d, 0x04, 0x63, 0x6d, 0x69, 0xcb, 0xf4, 0x54, 0x2e, 0x5d, 0xc8,
|
||||
0x96, 0xcc, 0xe1, 0xd9, 0xd9, 0x31, 0x8e, 0xde, 0x87, 0x49, 0x8a, 0xdb, 0xa4, 0x87, 0xbd, 0xdd,
|
||||
0x38, 0x42, 0x3a, 0x97, 0x2e, 0x68, 0xe5, 0xd4, 0x94, 0x62, 0x4f, 0x44, 0x53, 0xa1, 0x88, 0xe5,
|
||||
0xcb, 0x70, 0xed, 0x21, 0x76, 0x7a, 0x38, 0x5e, 0x40, 0x09, 0x34, 0xb1, 0x63, 0x32, 0xb1, 0xcb,
|
||||
0x3d, 0x25, 0x9b, 0x9f, 0x84, 0xeb, 0x51, 0x8c, 0x70, 0x81, 0xf9, 0x87, 0x30, 0xfb, 0x98, 0x12,
|
||||
0x17, 0x33, 0x16, 0xb2, 0x8c, 0x39, 0x8d, 0x13, 0x87, 0x05, 0xb1, 0x30, 0xf9, 0x4f, 0x64, 0x32,
|
||||
0x69, 0x85, 0x65, 0x65, 0xc5, 0x60, 0x3c, 0x7f, 0x5f, 0x7b, 0xfe, 0x43, 0x5e, 0xc9, 0xdf, 0x02,
|
||||
0x23, 0x29, 0x5a, 0xe4, 0xb5, 0x01, 0xfa, 0x16, 0xa7, 0xd8, 0x69, 0xff, 0x1f, 0x56, 0x37, 0x61,
|
||||
0x36, 0x21, 0x58, 0xe4, 0xf4, 0x09, 0x4c, 0xdb, 0x98, 0x91, 0x56, 0x0f, 0xaf, 0x78, 0x1e, 0x15,
|
||||
0xe9, 0x44, 0x36, 0xa3, 0x9c, 0x67, 0x7e, 0x09, 0x66, 0xce, 0xab, 0xa3, 0x72, 0x48, 0xaa, 0x99,
|
||||
0x16, 0xdc, 0xa8, 0x06, 0x1c, 0xd3, 0xc0, 0x69, 0x89, 0x38, 0xb1, 0xd3, 0x0c, 0xa4, 0x4e, 0x4c,
|
||||
0x32, 0xfd, 0xa3, 0xb9, 0x54, 0x75, 0xcd, 0x4e, 0xf9, 0x1e, 0x7a, 0x00, 0x19, 0xc7, 0xe5, 0x3e,
|
||||
0x09, 0xa2, 0x5a, 0x99, 0x4b, 0x3a, 0xb7, 0x2d, 0x4e, 0x28, 0x5e, 0x91, 0x58, 0x5c, 0xc4, 0xa1,
|
||||
0x28, 0xff, 0x8f, 0x06, 0xd9, 0x33, 0xb3, 0xe8, 0xe3, 0x93, 0x70, 0xc2, 0x6a, 0xa2, 0x74, 0xe7,
|
||||
0x92, 0x70, 0x1b, 0x7e, 0xe0, 0xc5, 0xc1, 0x90, 0x15, 0x55, 0x50, 0x4a, 0xee, 0xb8, 0x9e, 0x24,
|
||||
0x15, 0x77, 0x73, 0x5d, 0x09, 0xab, 0x07, 0xdd, 0x83, 0x31, 0x86, 0x69, 0xcf, 0x77, 0xb1, 0xbc,
|
||||
0x9c, 0xd9, 0xd2, 0xcd, 0x44, 0xb7, 0x10, 0x59, 0x57, 0xec, 0x98, 0x16, 0x46, 0xdc, 0x61, 0xcd,
|
||||
0xe8, 0xf2, 0x26, 0x1a, 0x6d, 0x3b, 0xac, 0x29, 0x8c, 0x04, 0x27, 0x8c, 0x02, 0xcc, 0xf7, 0x09,
|
||||
0x6d, 0xea, 0x57, 0x06, 0x1b, 0xd5, 0x42, 0x44, 0x18, 0x45, 0xb4, 0x10, 0xba, 0xad, 0x2e, 0xe3,
|
||||
0x98, 0xea, 0x99, 0xc1, 0xc2, 0xd5, 0x10, 0x11, 0xc2, 0x88, 0x46, 0x1f, 0x42, 0x86, 0x61, 0x97,
|
||||
0x62, 0xae, 0x8f, 0x49, 0x9d, 0x91, 0xbc, 0x32, 0x41, 0xac, 0x8b, 0x96, 0x22, 0x47, 0xe8, 0x3e,
|
||||
0x5c, 0xa5, 0x98, 0x91, 0x2e, 0x75, 0xb1, 0x7e, 0x55, 0xea, 0x6e, 0x25, 0x5e, 0xc3, 0x88, 0x59,
|
||||
0x57, 0xec, 0x13, 0x1e, 0x3d, 0x80, 0x71, 0xfc, 0x15, 0xc7, 0x01, 0x13, 0x87, 0x37, 0x2e, 0xc5,
|
||||
0xef, 0x26, 0x89, 0x2b, 0x31, 0xb4, 0xae, 0xd8, 0xa7, 0x0a, 0x91, 0xb0, 0x4b, 0x82, 0x3d, 0xbf,
|
||||
0xa1, 0xc3, 0xe0, 0x84, 0x57, 0x25, 0x21, 0x12, 0x0e, 0x59, 0xa1, 0xea, 0x91, 0x56, 0xb7, 0x8d,
|
||||
0xf5, 0xec, 0x60, 0xd5, 0x13, 0x49, 0x08, 0x55, 0xc8, 0x96, 0xaf, 0x42, 0x86, 0x3b, 0xb4, 0x81,
|
||||
0xf9, 0xe2, 0xdf, 0x2a, 0x4c, 0x9e, 0xab, 0x26, 0xf4, 0x1e, 0x8c, 0xed, 0xd4, 0x36, 0x6a, 0x9b,
|
||||
0x9f, 0xd5, 0xa6, 0x14, 0xc3, 0x38, 0x78, 0x91, 0x9b, 0x39, 0x47, 0xec, 0x04, 0xcd, 0x80, 0xec,
|
||||
0x07, 0xa8, 0x04, 0x37, 0xb6, 0xb6, 0x37, 0xed, 0xca, 0xee, 0xca, 0xea, 0x76, 0x75, 0xb3, 0xb6,
|
||||
0xbb, 0x6a, 0x57, 0x56, 0xb6, 0x2b, 0x53, 0xaa, 0x31, 0x7b, 0xf0, 0x22, 0x37, 0x7d, 0x4e, 0xb4,
|
||||
0x4a, 0xb1, 0xc3, 0xf1, 0x05, 0xcd, 0xce, 0xe3, 0x35, 0xa1, 0x49, 0x25, 0x6a, 0x76, 0x3a, 0x5e,
|
||||
0x92, 0xc6, 0xae, 0x3c, 0xda, 0x7c, 0x52, 0x99, 0x4a, 0x27, 0x6a, 0x6c, 0xd9, 0x64, 0x8d, 0x77,
|
||||
0xbe, 0xfd, 0xc5, 0x54, 0x7e, 0xfb, 0xd5, 0x3c, 0xbf, 0xba, 0xd2, 0xcf, 0x69, 0xd0, 0xc4, 0xbd,
|
||||
0x46, 0x07, 0x2a, 0xa0, 0x8b, 0xcd, 0x0d, 0x2d, 0x27, 0xed, 0xe0, 0xc0, 0x96, 0x6a, 0x58, 0xa3,
|
||||
0xe2, 0x51, 0x27, 0x9b, 0xfe, 0xfd, 0xe5, 0x5f, 0x3f, 0xa6, 0x26, 0xe1, 0xba, 0xe4, 0x97, 0xdb,
|
||||
0x4e, 0xe0, 0x34, 0x30, 0x45, 0xdf, 0xa9, 0xf0, 0xd6, 0x85, 0xf6, 0x87, 0x96, 0x92, 0x2f, 0x7f,
|
||||
0x72, 0xcb, 0x35, 0x96, 0x47, 0xa4, 0x87, 0x66, 0x52, 0x50, 0xd1, 0x37, 0x30, 0xf1, 0xdf, 0x76,
|
||||
0x89, 0x16, 0x06, 0x5d, 0x82, 0x0b, 0x0d, 0xd9, 0x58, 0x1c, 0x05, 0x1d, 0x9a, 0x41, 0xe9, 0x0f,
|
||||
0x15, 0x26, 0x4e, 0x1f, 0x3a, 0xf6, 0xcc, 0xef, 0xa0, 0xcf, 0x41, 0x13, 0xcf, 0x38, 0x4a, 0x6c,
|
||||
0xae, 0x67, 0x3e, 0x02, 0x8c, 0xdc, 0x60, 0x60, 0xf8, 0x01, 0xb8, 0x70, 0x45, 0x3e, 0xa4, 0x28,
|
||||
0x31, 0xc2, 0xd9, 0x77, 0xda, 0xb8, 0x3d, 0x84, 0x18, 0x6a, 0x52, 0x9e, 0x3f, 0x7c, 0x63, 0x2a,
|
||||
0xaf, 0xdf, 0x98, 0xca, 0xf3, 0xbe, 0xa9, 0x1e, 0xf6, 0x4d, 0xf5, 0x55, 0xdf, 0x54, 0xff, 0xec,
|
||||
0x9b, 0xea, 0xf7, 0xc7, 0xa6, 0xf2, 0xea, 0xd8, 0x54, 0x5e, 0x1f, 0x9b, 0xca, 0xd3, 0xf4, 0x53,
|
||||
0xad, 0x9e, 0x91, 0xdf, 0x64, 0x77, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x32, 0xf6, 0xf1, 0xe7,
|
||||
0xa3, 0x0a, 0x00, 0x00,
|
||||
}
|
||||
|
||||
type authenticatedWrapperRaftServer struct {
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ package docker.swarmkit.v1;
|
||||
|
||||
import "github.com/docker/swarmkit/api/objects.proto";
|
||||
import "github.com/docker/swarmkit/api/types.proto";
|
||||
import "go.etcd.io/etcd/raft/v3/raftpb/raft.proto";
|
||||
import "go.etcd.io/raft/v3/raftpb/raft.proto";
|
||||
import weak "gogoproto/gogo.proto";
|
||||
import weak "github.com/docker/swarmkit/protobuf/plugin/plugin.proto";
|
||||
|
||||
|
||||
+70
-35
@@ -67,8 +67,9 @@ func (ResourceType) EnumDescriptor() ([]byte, []int) {
|
||||
// Only the manager create a NEW task, and move the task to PENDING and ASSIGNED.
|
||||
// Afterward, the manager must rely on the agent to update the task status
|
||||
// (pre-run: preparing, ready, starting;
|
||||
// running;
|
||||
// end-state: complete, shutdown, failed, rejected)
|
||||
//
|
||||
// running;
|
||||
// end-state: complete, shutdown, failed, rejected)
|
||||
type TaskState int32
|
||||
|
||||
const (
|
||||
@@ -1085,6 +1086,7 @@ var xxx_messageInfo_DiscreteGenericResource proto.InternalMessageInfo
|
||||
// be either an integer (e.g: SSD=3) or a string (e.g: SSD=sda1)
|
||||
type GenericResource struct {
|
||||
// Types that are valid to be assigned to Resource:
|
||||
//
|
||||
// *GenericResource_NamedResourceSpec
|
||||
// *GenericResource_DiscreteResourceSpec
|
||||
Resource isGenericResource_Resource `protobuf_oneof:"resource"`
|
||||
@@ -2114,6 +2116,7 @@ type TaskStatus struct {
|
||||
// Container status contains container specific status information.
|
||||
//
|
||||
// Types that are valid to be assigned to RuntimeStatus:
|
||||
//
|
||||
// *TaskStatus_Container
|
||||
RuntimeStatus isTaskStatus_RuntimeStatus `protobuf_oneof:"runtime_status"`
|
||||
// HostPorts provides a list of ports allocated at the host
|
||||
@@ -2506,6 +2509,7 @@ type IssuanceStatus struct {
|
||||
State IssuanceStatus_State `protobuf:"varint,1,opt,name=state,proto3,enum=docker.swarmkit.v1.IssuanceStatus_State" json:"state,omitempty"`
|
||||
// Err is set if the Certificate Issuance is in an error state.
|
||||
// The following states should report a companion error:
|
||||
//
|
||||
// FAILED
|
||||
Err string `protobuf:"bytes,2,opt,name=err,proto3" json:"err,omitempty"`
|
||||
}
|
||||
@@ -3006,6 +3010,7 @@ var xxx_messageInfo_SpreadOver proto.InternalMessageInfo
|
||||
|
||||
type PlacementPreference struct {
|
||||
// Types that are valid to be assigned to Preference:
|
||||
//
|
||||
// *PlacementPreference_Spread
|
||||
Preference isPlacementPreference_Preference `protobuf_oneof:"Preference"`
|
||||
}
|
||||
@@ -3437,6 +3442,7 @@ type SecretReference struct {
|
||||
// Target specifies how this secret should be exposed to the task.
|
||||
//
|
||||
// Types that are valid to be assigned to Target:
|
||||
//
|
||||
// *SecretReference_File
|
||||
Target isSecretReference_Target `protobuf_oneof:"target"`
|
||||
}
|
||||
@@ -3517,6 +3523,7 @@ type ConfigReference struct {
|
||||
// Target specifies how this config should be exposed to the task.
|
||||
//
|
||||
// Types that are valid to be assigned to Target:
|
||||
//
|
||||
// *ConfigReference_File
|
||||
// *ConfigReference_Runtime
|
||||
Target isConfigReference_Target `protobuf_oneof:"target"`
|
||||
@@ -3826,6 +3833,7 @@ var xxx_messageInfo_Privileges proto.InternalMessageInfo
|
||||
// CredentialSpec for managed service account (Windows only).
|
||||
type Privileges_CredentialSpec struct {
|
||||
// Types that are valid to be assigned to Source:
|
||||
//
|
||||
// *Privileges_CredentialSpec_File
|
||||
// *Privileges_CredentialSpec_Registry
|
||||
// *Privileges_CredentialSpec_Config
|
||||
@@ -4113,6 +4121,7 @@ type VolumeAccessMode struct {
|
||||
// but the upstream is free to do so. However, one of these MUST be set.
|
||||
//
|
||||
// Types that are valid to be assigned to AccessType:
|
||||
//
|
||||
// *VolumeAccessMode_Block
|
||||
// *VolumeAccessMode_Mount
|
||||
AccessType isVolumeAccessMode_AccessType `protobuf_oneof:"access_type"`
|
||||
@@ -4338,12 +4347,12 @@ var xxx_messageInfo_VolumeSecret proto.InternalMessageInfo
|
||||
//
|
||||
// Without this two-step process, the following could happen:
|
||||
//
|
||||
// 1. ControllerPublishVolume is called and the Volume is successfully
|
||||
// published.
|
||||
// 2. A crash or leadership change disrupts the cluster before
|
||||
// the Volume with the updated VolumePublishStatus can be added to the
|
||||
// store.
|
||||
// 3. The Task that required the Volume to be published is deleted.
|
||||
// 1. ControllerPublishVolume is called and the Volume is successfully
|
||||
// published.
|
||||
// 2. A crash or leadership change disrupts the cluster before
|
||||
// the Volume with the updated VolumePublishStatus can be added to the
|
||||
// store.
|
||||
// 3. The Task that required the Volume to be published is deleted.
|
||||
//
|
||||
// In this case, the Volume would be published to the Node, but Swarm would be
|
||||
// unaware of this, and would additionally be unaware that the Volume _should_
|
||||
@@ -4608,21 +4617,27 @@ type TopologyRequirement struct {
|
||||
// accessible from at least one of the requisite topologies.
|
||||
//
|
||||
// Given
|
||||
// x = number of topologies provisioned volume is accessible from
|
||||
// n = number of requisite topologies
|
||||
//
|
||||
// x = number of topologies provisioned volume is accessible from
|
||||
// n = number of requisite topologies
|
||||
//
|
||||
// The CO MUST ensure n >= 1. The SP MUST ensure x >= 1
|
||||
// If x==n, then the SP MUST make the provisioned volume available to
|
||||
// all topologies from the list of requisite topologies. If it is
|
||||
// unable to do so, the SP MUST fail the CreateVolume call.
|
||||
// For example, if a volume should be accessible from a single zone,
|
||||
// and requisite =
|
||||
// {"region": "R1", "zone": "Z2"}
|
||||
//
|
||||
// {"region": "R1", "zone": "Z2"}
|
||||
//
|
||||
// then the provisioned volume MUST be accessible from the "region"
|
||||
// "R1" and the "zone" "Z2".
|
||||
// Similarly, if a volume should be accessible from two zones, and
|
||||
// requisite =
|
||||
// {"region": "R1", "zone": "Z2"},
|
||||
// {"region": "R1", "zone": "Z3"}
|
||||
//
|
||||
// {"region": "R1", "zone": "Z2"},
|
||||
// {"region": "R1", "zone": "Z3"}
|
||||
//
|
||||
// then the provisioned volume MUST be accessible from the "region"
|
||||
// "R1" and both "zone" "Z2" and "zone" "Z3".
|
||||
//
|
||||
@@ -4631,18 +4646,23 @@ type TopologyRequirement struct {
|
||||
// the CreateVolume call.
|
||||
// For example, if a volume should be accessible from a single zone,
|
||||
// and requisite =
|
||||
// {"region": "R1", "zone": "Z2"},
|
||||
// {"region": "R1", "zone": "Z3"}
|
||||
//
|
||||
// {"region": "R1", "zone": "Z2"},
|
||||
// {"region": "R1", "zone": "Z3"}
|
||||
//
|
||||
// then the SP may choose to make the provisioned volume available in
|
||||
// either the "zone" "Z2" or the "zone" "Z3" in the "region" "R1".
|
||||
// Similarly, if a volume should be accessible from two zones, and
|
||||
// requisite =
|
||||
// {"region": "R1", "zone": "Z2"},
|
||||
// {"region": "R1", "zone": "Z3"},
|
||||
// {"region": "R1", "zone": "Z4"}
|
||||
//
|
||||
// {"region": "R1", "zone": "Z2"},
|
||||
// {"region": "R1", "zone": "Z3"},
|
||||
// {"region": "R1", "zone": "Z4"}
|
||||
//
|
||||
// then the provisioned volume MUST be accessible from any combination
|
||||
// of two unique topologies: e.g. "R1/Z2" and "R1/Z3", or "R1/Z2" and
|
||||
// "R1/Z4", or "R1/Z3" and "R1/Z4".
|
||||
//
|
||||
// "R1/Z4", or "R1/Z3" and "R1/Z4".
|
||||
//
|
||||
// If x>n, then the SP MUST make the provisioned volume available from
|
||||
// all topologies from the list of requisite topologies and MAY choose
|
||||
@@ -4651,7 +4671,9 @@ type TopologyRequirement struct {
|
||||
// CreateVolume call.
|
||||
// For example, if a volume should be accessible from two zones, and
|
||||
// requisite =
|
||||
// {"region": "R1", "zone": "Z2"}
|
||||
//
|
||||
// {"region": "R1", "zone": "Z2"}
|
||||
//
|
||||
// then the provisioned volume MUST be accessible from the "region"
|
||||
// "R1" and the "zone" "Z2" and the SP may select the second zone
|
||||
// independently, e.g. "R1/Z4".
|
||||
@@ -4680,10 +4702,14 @@ type TopologyRequirement struct {
|
||||
// Example 1:
|
||||
// Given a volume should be accessible from a single zone, and
|
||||
// requisite =
|
||||
// {"region": "R1", "zone": "Z2"},
|
||||
// {"region": "R1", "zone": "Z3"}
|
||||
//
|
||||
// {"region": "R1", "zone": "Z2"},
|
||||
// {"region": "R1", "zone": "Z3"}
|
||||
//
|
||||
// preferred =
|
||||
// {"region": "R1", "zone": "Z3"}
|
||||
//
|
||||
// {"region": "R1", "zone": "Z3"}
|
||||
//
|
||||
// then the the SP SHOULD first attempt to make the provisioned volume
|
||||
// available from "zone" "Z3" in the "region" "R1" and fall back to
|
||||
// "zone" "Z2" in the "region" "R1" if that is not possible.
|
||||
@@ -4691,13 +4717,17 @@ type TopologyRequirement struct {
|
||||
// Example 2:
|
||||
// Given a volume should be accessible from a single zone, and
|
||||
// requisite =
|
||||
// {"region": "R1", "zone": "Z2"},
|
||||
// {"region": "R1", "zone": "Z3"},
|
||||
// {"region": "R1", "zone": "Z4"},
|
||||
// {"region": "R1", "zone": "Z5"}
|
||||
//
|
||||
// {"region": "R1", "zone": "Z2"},
|
||||
// {"region": "R1", "zone": "Z3"},
|
||||
// {"region": "R1", "zone": "Z4"},
|
||||
// {"region": "R1", "zone": "Z5"}
|
||||
//
|
||||
// preferred =
|
||||
// {"region": "R1", "zone": "Z4"},
|
||||
// {"region": "R1", "zone": "Z2"}
|
||||
//
|
||||
// {"region": "R1", "zone": "Z4"},
|
||||
// {"region": "R1", "zone": "Z2"}
|
||||
//
|
||||
// then the the SP SHOULD first attempt to make the provisioned volume
|
||||
// accessible from "zone" "Z4" in the "region" "R1" and fall back to
|
||||
// "zone" "Z2" in the "region" "R1" if that is not possible. If that
|
||||
@@ -4710,13 +4740,17 @@ type TopologyRequirement struct {
|
||||
// the volume is accessible from two zones, aka synchronously
|
||||
// replicated), and
|
||||
// requisite =
|
||||
// {"region": "R1", "zone": "Z2"},
|
||||
// {"region": "R1", "zone": "Z3"},
|
||||
// {"region": "R1", "zone": "Z4"},
|
||||
// {"region": "R1", "zone": "Z5"}
|
||||
//
|
||||
// {"region": "R1", "zone": "Z2"},
|
||||
// {"region": "R1", "zone": "Z3"},
|
||||
// {"region": "R1", "zone": "Z4"},
|
||||
// {"region": "R1", "zone": "Z5"}
|
||||
//
|
||||
// preferred =
|
||||
// {"region": "R1", "zone": "Z5"},
|
||||
// {"region": "R1", "zone": "Z3"}
|
||||
//
|
||||
// {"region": "R1", "zone": "Z5"},
|
||||
// {"region": "R1", "zone": "Z3"}
|
||||
//
|
||||
// then the the SP SHOULD first attempt to make the provisioned volume
|
||||
// accessible from the combination of the two "zones" "Z5" and "Z3" in
|
||||
// the "region" "R1". If that's not possible, it should fall back to
|
||||
@@ -4831,6 +4865,7 @@ type VolumeCapability struct {
|
||||
// following fields MUST be specified.
|
||||
//
|
||||
// Types that are valid to be assigned to AccessType:
|
||||
//
|
||||
// *VolumeCapability_Block
|
||||
// *VolumeCapability_Mount
|
||||
AccessType isVolumeCapability_AccessType `protobuf_oneof:"access_type"`
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user