mirror of
https://github.com/docker/cli.git
synced 2026-11-06 21:18:28 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5457199aa5 | ||
|
|
cac50c5e62 | ||
|
|
00d843b496 | ||
|
|
64fe50bbea | ||
|
|
999ab16f09 | ||
|
|
bb485bc900 | ||
|
|
86e51b44de | ||
|
|
d9bbcfcb4d | ||
|
|
2e886262b1 | ||
|
|
a4971d1f60 | ||
|
|
bfb5814dd5 | ||
|
|
68e452624a | ||
|
|
160531c55d | ||
|
|
a82e528901 | ||
|
|
e35bc1b6ae | ||
|
|
baf6c13889 | ||
|
|
4051f75f69 | ||
|
|
76321a699d | ||
|
|
b82456c62f | ||
|
|
a98becc3e3 | ||
|
|
eabc65846b | ||
|
|
b873f57cd0 | ||
|
|
c8a849c2f9 | ||
|
|
f4efc1b596 | ||
|
|
2747e2e3bf | ||
|
|
329cdf1a9d | ||
|
|
0c7e2e7203 | ||
|
|
dfc4efb1e2 | ||
|
|
dba867594c | ||
|
|
d9aefe565a | ||
|
|
9182b351c1 | ||
|
|
0ae55a3e6c | ||
|
|
029a4b21b3 | ||
|
|
e596fea454 | ||
|
|
0c72f456aa | ||
|
|
26f2736a2b | ||
|
|
ac4c4dfabd | ||
|
|
1245054535 | ||
|
|
f72fb0caf0 | ||
|
|
e4af2bf0d3 | ||
|
|
49a1af6aaa | ||
|
|
c2be9ccfc3 | ||
|
|
0da6a5121f | ||
|
|
6b3ca8fc04 | ||
|
|
f47603c290 | ||
|
|
9709c8fe08 | ||
|
|
af45a23fa9 | ||
|
|
bc97f5ac19 | ||
|
|
6d71967120 | ||
|
|
9b51892b80 |
@@ -68,7 +68,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@59cb1203cd42bcefd40a1b6d73b589e48c86040e # v7.5.0
|
||||
uses: docker/bake-action@018cb6412ab401ebaa809aa5f85966b74628600f # v7.4.0
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
set: |
|
||||
@@ -99,7 +99,7 @@ jobs:
|
||||
|
||||
bin-image:
|
||||
if: ${{ github.event_name != 'pull_request' && github.repository == 'docker/cli' }}
|
||||
uses: docker/github-builder/.github/workflows/bake.yml@cbc9d42f8c89a55623392aadd03600965b80e963 # v1.18.1
|
||||
uses: docker/github-builder/.github/workflows/bake.yml@58cb9f5b71b1836d6f690c1e95effdeb9b98cb8a # v1.17.0
|
||||
permissions:
|
||||
contents: read # same as global permission
|
||||
id-token: write # for signing attestation(s) and authenticating to Docker Hub with GitHub OIDC Token
|
||||
@@ -159,7 +159,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@59cb1203cd42bcefd40a1b6d73b589e48c86040e # v7.5.0
|
||||
uses: docker/bake-action@018cb6412ab401ebaa809aa5f85966b74628600f # v7.4.0
|
||||
with:
|
||||
targets: plugins-cross
|
||||
set: |
|
||||
|
||||
@@ -64,18 +64,18 @@ jobs:
|
||||
name: Update Go
|
||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
||||
with:
|
||||
go-version: "1.27.3"
|
||||
go-version: "1.26.10"
|
||||
cache: false
|
||||
-
|
||||
name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
|
||||
uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
|
||||
with:
|
||||
languages: go
|
||||
-
|
||||
name: Autobuild
|
||||
uses: github/codeql-action/autobuild@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
|
||||
uses: github/codeql-action/autobuild@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
|
||||
-
|
||||
name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
|
||||
uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
|
||||
with:
|
||||
category: "/language:go"
|
||||
|
||||
@@ -26,9 +26,6 @@ on:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # needed for CodeCov; https://github.com/codecov/codecov-action/blob/v7.1.1/README.md#using-oidc
|
||||
runs-on: ubuntu-26.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -83,4 +80,4 @@ jobs:
|
||||
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
|
||||
with:
|
||||
files: ./build/coverage/coverage.txt
|
||||
use_oidc: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
@@ -11,7 +11,7 @@ permissions:
|
||||
|
||||
jobs:
|
||||
review:
|
||||
uses: docker/docker-agent-action/.github/workflows/review-pr.yml@aaa657c53cc69ed15683cd8a985d86663a0e1594 # v2.1.0
|
||||
uses: docker/docker-agent-action/.github/workflows/review-pr.yml@3654ec391b070e247de2e2e3c1ff2519b6378b3a # v2.0.9
|
||||
permissions:
|
||||
contents: read # Read repository files and PR diffs
|
||||
pull-requests: write # Post review comments
|
||||
|
||||
@@ -26,9 +26,6 @@ on:
|
||||
|
||||
jobs:
|
||||
ctn:
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # needed for CodeCov; https://github.com/codecov/codecov-action/blob/v7.1.1/README.md#using-oidc
|
||||
runs-on: ubuntu-26.04
|
||||
steps:
|
||||
-
|
||||
@@ -36,7 +33,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
|
||||
-
|
||||
name: Test
|
||||
uses: docker/bake-action@59cb1203cd42bcefd40a1b6d73b589e48c86040e # v7.5.0
|
||||
uses: docker/bake-action@018cb6412ab401ebaa809aa5f85966b74628600f # v7.4.0
|
||||
with:
|
||||
targets: test-coverage
|
||||
-
|
||||
@@ -44,12 +41,9 @@ jobs:
|
||||
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
|
||||
with:
|
||||
files: ./build/coverage/coverage.txt
|
||||
use_oidc: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
host:
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # needed for CodeCov; https://github.com/codecov/codecov-action/blob/v7.1.1/README.md#using-oidc
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
GOPATH: ${{ github.workspace }}
|
||||
@@ -59,9 +53,9 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- macos-14 # macOS 14 on arm64 (Apple Silicon M1)
|
||||
- macos-15-intel # macOS 15 on Intel
|
||||
- macos-15 # macOS 15 on arm64 (Apple Silicon M1)
|
||||
- macos-26 # macOS 26 on arm64 (Apple Silicon M1)
|
||||
- macos-26-intel # macOS 26 on Intel
|
||||
# - windows-2022 # FIXME: some tests are failing on the Windows runner, as well as on Appveyor since June 24, 2018: https://ci.appveyor.com/project/docker/cli/history
|
||||
steps:
|
||||
-
|
||||
@@ -74,7 +68,7 @@ jobs:
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
||||
with:
|
||||
go-version: "1.27.3"
|
||||
go-version: "1.26.10"
|
||||
cache: false
|
||||
-
|
||||
name: Test
|
||||
@@ -92,4 +86,4 @@ jobs:
|
||||
with:
|
||||
files: /tmp/coverage.txt
|
||||
working-directory: ${{ env.GOPATH }}/src/github.com/docker/cli
|
||||
use_oidc: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Run
|
||||
uses: docker/bake-action@59cb1203cd42bcefd40a1b6d73b589e48c86040e # v7.5.0
|
||||
uses: docker/bake-action@018cb6412ab401ebaa809aa5f85966b74628600f # v7.4.0
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
@@ -101,7 +101,7 @@ jobs:
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
||||
with:
|
||||
go-version: "1.27.3"
|
||||
go-version: "1.26.10"
|
||||
cache: false
|
||||
-
|
||||
name: Run gocompat check
|
||||
|
||||
+11
-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.27.3"
|
||||
go: "1.26.10"
|
||||
|
||||
timeout: 5m
|
||||
|
||||
@@ -201,6 +201,11 @@ linters:
|
||||
linters:
|
||||
- gosec
|
||||
|
||||
# TODO: make sure all packages have a description. Currently, there's 67 packages without.
|
||||
- text: "package-comments: should have a package comment"
|
||||
linters:
|
||||
- revive
|
||||
|
||||
# Exclude some linters from running on tests files.
|
||||
- path: _test\.go
|
||||
linters:
|
||||
@@ -222,6 +227,11 @@ linters:
|
||||
linters:
|
||||
- staticcheck
|
||||
|
||||
# TODO(thaJeztah): remove once https://github.com/leighmcculloch/gocheckcompilerdirectives/issues/7 is fixed.
|
||||
- text: "compiler directive unrecognized: //go:fix"
|
||||
linters:
|
||||
- gocheckcompilerdirectives
|
||||
|
||||
# Log a warning if an exclusion rule is unused.
|
||||
# Default: false
|
||||
warn-unused: true
|
||||
|
||||
+6
-8
@@ -5,12 +5,10 @@ ARG BASE_VARIANT=alpine
|
||||
# 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.24
|
||||
ARG ALPINE_VERSION=3.23
|
||||
ARG BASE_DEBIAN_DISTRO=bookworm
|
||||
|
||||
# BASE_DEBIAN_DISTRO sets the version of the debian base image to use,
|
||||
ARG BASE_DEBIAN_DISTRO=trixie
|
||||
|
||||
ARG GO_VERSION=1.27.3
|
||||
ARG GO_VERSION=1.26.10
|
||||
|
||||
# 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.
|
||||
@@ -27,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.38.1
|
||||
ARG BUILDX_VERSION=0.38.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.7.0
|
||||
ARG COMPOSE_VERSION=v5.6.0
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
||||
@@ -55,7 +53,7 @@ WORKDIR /go/src/github.com/docker/cli
|
||||
|
||||
FROM build-base-debian AS build-debian
|
||||
ARG TARGETPLATFORM
|
||||
RUN xx-apt-get install --no-install-recommends -y libc6-dev libgcc-14-dev pkgconf
|
||||
RUN xx-apt-get install --no-install-recommends -y libc6-dev libgcc-12-dev pkgconf
|
||||
|
||||
FROM build-base-${BASE_VARIANT} AS goversioninfo
|
||||
ARG GOVERSIONINFO_VERSION
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
@@ -33,11 +32,6 @@ import (
|
||||
// called.
|
||||
var PersistentPreRunE func(*cobra.Command, []string) error
|
||||
|
||||
// finalizeHookTimeout is the timeout given for running the finalize hook.
|
||||
// It contains the timeout for exporting metrics and a little bit extra in case
|
||||
// shutdown does not respond to the export timeout promptly.
|
||||
const finalizeHookTimeout = command.ExportTimeout + 10*time.Millisecond
|
||||
|
||||
// RunPlugin executes the specified plugin command
|
||||
func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta metadata.Metadata) error {
|
||||
tcmd := newPluginCommand(dockerCli, plugin, meta)
|
||||
@@ -75,7 +69,6 @@ func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta metadat
|
||||
stopInstrumentation(err)
|
||||
return err
|
||||
}
|
||||
cobra.OnFinalize(func() { finalizeHook(dockerCli) })
|
||||
})
|
||||
return retErr
|
||||
}
|
||||
@@ -90,59 +83,6 @@ func RunPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta metadat
|
||||
return cmd.Execute()
|
||||
}
|
||||
|
||||
// finalizeHook is a hook to be run when the CLI has finished executing the run command.
|
||||
// This happens after any post run methods but also always runs even when an error happens.
|
||||
func finalizeHook(dockerCli *command.DockerCli) {
|
||||
// Use a new context for the export so that the command being cancelled
|
||||
// doesn't affect the metrics, and we get metrics for cancelled commands.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), command.ExportTimeout)
|
||||
defer cancel()
|
||||
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
wait bool
|
||||
)
|
||||
if tp, ok := dockerCli.TracerProvider().(command.TracerProvider); ok {
|
||||
wg.Go(func() {
|
||||
if err := tp.Shutdown(ctx); err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
})
|
||||
wait = true
|
||||
}
|
||||
|
||||
if mp, ok := dockerCli.MeterProvider().(command.MeterProvider); ok {
|
||||
wg.Go(func() {
|
||||
if err := mp.Shutdown(ctx); err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
})
|
||||
wait = true
|
||||
}
|
||||
|
||||
// Wait for the wait group in another goroutine if any were created.
|
||||
if !wait {
|
||||
return
|
||||
}
|
||||
|
||||
// Spawn a goroutine to wait for the wait group.
|
||||
// If the shutdown does not promptly hang up with the
|
||||
// cancellation of the context, we leave this goroutine
|
||||
// orphaned so the process can exit.
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
wg.Wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(finalizeHookTimeout):
|
||||
// Ensure we still exit even if the shutdown doesn't work properly.
|
||||
// We give 10 extra milliseconds for the cleanup to happen.
|
||||
}
|
||||
}
|
||||
|
||||
// Run is the top-level entry point to the CLI plugin framework. It should
|
||||
// be called from the plugin's "main()" function. It initializes a new
|
||||
// [command.DockerCli] instance with the given options before calling
|
||||
@@ -272,8 +212,8 @@ func newMetadataSubcommand(plugin *cobra.Command, meta metadata.Metadata) *cobra
|
||||
Use: metadata.MetadataSubcommandName,
|
||||
Hidden: true,
|
||||
// Suppress the global/parent PersistentPreRunE, which
|
||||
// needlessly initializes the client and tries to connect to the
|
||||
// daemon.
|
||||
// needlessly initializes the client and tries to
|
||||
// connect to the daemon.
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
|
||||
+1
-19
@@ -160,25 +160,7 @@ func (tcmd *TopLevelCommand) HandleGlobalFlags() (*cobra.Command, []string, erro
|
||||
return nil, nil, cmd.FlagErrorFunc()(cmd, err)
|
||||
}
|
||||
|
||||
args := flags.Args()
|
||||
|
||||
// A leading "--help"/"-h" is consumed above as the root's persistent
|
||||
// help flag and stripped from args, so the target subcommand never sees
|
||||
// it. That's fine for most subcommands, which inherit the same help
|
||||
// flag instance, but "run" and "create" register their own local
|
||||
// "help" flag (to avoid a shorthand clash with "-h"/"--hostname"),
|
||||
// which shadows the persistent one and is left false. Propagate the
|
||||
// value so "docker --help run <image>" shows help like "docker run
|
||||
// --help" does, instead of running the container.
|
||||
if help, _ := flags.GetBool("help"); help {
|
||||
if sub, _, err := cmd.Find(args); err == nil && sub != cmd {
|
||||
if sub.Flags().Lookup("help") != nil {
|
||||
_ = sub.Flags().Set("help", "true")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cmd, args, nil
|
||||
return cmd, flags.Args(), nil
|
||||
}
|
||||
|
||||
// Initialize finalises global option parsing and initializes the docker client.
|
||||
|
||||
@@ -108,45 +108,3 @@ func TestDecoratedName(t *testing.T) {
|
||||
topLevelCommand.Annotations = map[string]string{metadata.CommandAnnotationPlugin: "true"}
|
||||
assert.Equal(t, decoratedName(topLevelCommand), "pluginTopLevelCommand*")
|
||||
}
|
||||
|
||||
// TestHandleGlobalFlagsHelpPropagation covers https://github.com/docker/cli/issues/7473:
|
||||
// a leading "--help" is consumed as the root's persistent help flag and
|
||||
// stripped from args, so a subcommand that registers its own local "help"
|
||||
// flag (as "run" and "create" do, to avoid a shorthand clash with "-h") never
|
||||
// sees it and runs instead of showing help.
|
||||
func TestHandleGlobalFlagsHelpPropagation(t *testing.T) {
|
||||
newRootCmd := func() (root, sub *cobra.Command) {
|
||||
root = &cobra.Command{Use: "docker"}
|
||||
root.PersistentFlags().BoolP("help", "h", false, "Print usage")
|
||||
|
||||
sub = &cobra.Command{Use: "run", RunE: func(cmd *cobra.Command, args []string) error { return nil }}
|
||||
sub.Flags().Bool("help", false, "Print usage")
|
||||
root.AddCommand(sub)
|
||||
return root, sub
|
||||
}
|
||||
|
||||
t.Run("propagates to the shadowing local help flag", func(t *testing.T) {
|
||||
root, sub := newRootCmd()
|
||||
tcmd := &TopLevelCommand{cmd: root, args: []string{"--help", "run", "hello-world"}}
|
||||
|
||||
_, args, err := tcmd.HandleGlobalFlags()
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, args, []string{"run", "hello-world"})
|
||||
|
||||
help, err := sub.Flags().GetBool("help")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, help, true)
|
||||
})
|
||||
|
||||
t.Run("leaves the local help flag alone without --help", func(t *testing.T) {
|
||||
root, sub := newRootCmd()
|
||||
tcmd := &TopLevelCommand{cmd: root, args: []string{"run", "hello-world"}}
|
||||
|
||||
_, _, err := tcmd.HandleGlobalFlags()
|
||||
assert.NilError(t, err)
|
||||
|
||||
help, err := sub.Flags().GetBool("help")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, help, false)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -146,27 +146,24 @@ func (c *ContainerContext) ID() string {
|
||||
// slash (/) prefix stripped. Additional names for the container (related to the
|
||||
// legacy `--link` feature) are omitted when formatting "truncated".
|
||||
func (c *ContainerContext) Names() string {
|
||||
if c.trunc {
|
||||
// When printing truncated, we only print a single name.
|
||||
//
|
||||
// Pick the first name that's not a legacy link (does not have
|
||||
// slashes inside the name itself (e.g., "/other-container/link")).
|
||||
// Normally this would be the first name found.
|
||||
for _, n := range c.c.Names {
|
||||
if name := strings.TrimPrefix(n, "/"); strings.IndexByte(name, '/') == -1 {
|
||||
return name
|
||||
}
|
||||
}
|
||||
// No name without slashes was found; this can happen when connected
|
||||
// to a classic Swarm API, which prefixes names with the node-name
|
||||
// ("/<node-name>/<container-name>"). Fall back to printing all names.
|
||||
}
|
||||
var b strings.Builder
|
||||
for i, n := range c.c.Names {
|
||||
name := strings.TrimPrefix(n, "/")
|
||||
if c.trunc {
|
||||
// When printing truncated, we only print a single name.
|
||||
//
|
||||
// Pick the first name that's not a legacy link (does not have
|
||||
// slashes inside the name itself (e.g., "/other-container/link")).
|
||||
// Normally this would be the first name found.
|
||||
if strings.IndexByte(name, '/') == -1 {
|
||||
return name
|
||||
}
|
||||
continue
|
||||
}
|
||||
if i > 0 {
|
||||
b.WriteByte(',')
|
||||
}
|
||||
b.WriteString(strings.TrimPrefix(n, "/"))
|
||||
b.WriteString(name)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
@@ -48,25 +48,6 @@ func TestContainerPsContext(t *testing.T) {
|
||||
expValue: "foobar_baz",
|
||||
call: ctx.Names,
|
||||
},
|
||||
{
|
||||
container: container.Summary{Names: []string{"/other/link", "/foobar_baz"}},
|
||||
trunc: true,
|
||||
expValue: "foobar_baz",
|
||||
call: ctx.Names,
|
||||
},
|
||||
{
|
||||
container: container.Summary{Names: []string{"/foobar_baz", "/other/link"}},
|
||||
expValue: "foobar_baz,other/link",
|
||||
call: ctx.Names,
|
||||
},
|
||||
{
|
||||
// Names returned by a classic Swarm API are prefixed with the
|
||||
// node-name; see https://github.com/docker/cli/pull/6875
|
||||
container: container.Summary{Names: []string{"/node-1/foobar_baz"}},
|
||||
trunc: true,
|
||||
expValue: "node-1/foobar_baz",
|
||||
call: ctx.Names,
|
||||
},
|
||||
{
|
||||
container: container.Summary{Image: "ubuntu"},
|
||||
trunc: true,
|
||||
@@ -276,7 +257,6 @@ func TestContainerPsContext(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:dupword // ignore duplicate "ubuntu" in test fixtures
|
||||
func TestContainerContextWrite(t *testing.T) {
|
||||
unixTime := time.Now().AddDate(0, 0, -1).Unix()
|
||||
expectedTime := time.Unix(unixTime, 0).String()
|
||||
|
||||
@@ -10,10 +10,8 @@ import (
|
||||
"slices"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/manifest/store"
|
||||
"github.com/docker/cli/internal/registryclient"
|
||||
@@ -22,23 +20,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
// similar to https://github.com/containerd/platforms/blob/v1.0.0-rc.5/database.go#L29-L38
|
||||
knownOSes = []string{
|
||||
"aix", "android", "darwin", "dragonfly", "freebsd", "hurd", "illumos",
|
||||
"ios", "js", "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris",
|
||||
"windows", "zos",
|
||||
}
|
||||
|
||||
// similar to https://github.com/containerd/platforms/blob/v1.0.0-rc.5/database.go#L51-L60
|
||||
knownArches = []string{
|
||||
"386", "amd64", "amd64p32", "arm", "armbe", "arm64", "arm64be", "ppc64",
|
||||
"ppc64le", "loong64", "mips", "mipsle", "mips64", "mips64le", "mips64p32",
|
||||
"mips64p32le", "ppc", "riscv", "riscv64", "s390", "s390x", "sparc",
|
||||
"sparc64", "wasm",
|
||||
}
|
||||
)
|
||||
|
||||
type annotateOptions struct {
|
||||
target string // the target manifest list name (also transaction ID)
|
||||
image string // the manifest to annotate within the list
|
||||
@@ -116,8 +97,6 @@ func newAnnotateCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
flags.StringSliceVar(&opts.osFeatures, "os-features", []string{}, "Set operating system feature")
|
||||
flags.StringVar(&opts.variant, "variant", "", "Set architecture variant")
|
||||
|
||||
_ = cmd.RegisterFlagCompletionFunc("os", completion.FromList(knownOSes...))
|
||||
_ = cmd.RegisterFlagCompletionFunc("arch", completion.FromList(knownArches...))
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -160,15 +139,8 @@ func runManifestAnnotate(dockerCLI command.Cli, opts annotateOptions) error {
|
||||
imageManifest.Descriptor.Platform.OSVersion = opts.osVersion
|
||||
}
|
||||
|
||||
// Validate platform syntax, without restricting OS/architecture combinations.
|
||||
//
|
||||
// TODO(thaJeztah): Implement a platforms.Validate in containerd/platforms and replace this code.
|
||||
p := imageManifest.Descriptor.Platform
|
||||
if p.OS == "" || p.Architecture == "" {
|
||||
return fmt.Errorf("manifest entry for image has missing os/architecture: %q/%q", p.OS, p.Architecture)
|
||||
}
|
||||
if _, err := platforms.Parse(platforms.FormatAll(*p)); err != nil {
|
||||
return fmt.Errorf("manifest entry for image has invalid platform: %w", err)
|
||||
if !isValidOSArch(imageManifest.Descriptor.Platform.OS, imageManifest.Descriptor.Platform.Architecture) {
|
||||
return fmt.Errorf("manifest entry for image has unsupported os/arch combination: %s/%s", opts.os, opts.arch)
|
||||
}
|
||||
return manifestStore.Save(targetRef, imgRef, imageManifest)
|
||||
}
|
||||
|
||||
@@ -58,16 +58,15 @@ func TestManifestAnnotate(t *testing.T) {
|
||||
assert.ErrorContains(t, cmd.Execute(), expectedError)
|
||||
|
||||
cmd.SetArgs([]string{"example.com/list:v1", "example.com/alpine:3.0"})
|
||||
assert.NilError(t, cmd.Flags().Set("os", "freebsd"))
|
||||
assert.NilError(t, cmd.Flags().Set("arch", "<invalid>"))
|
||||
assert.NilError(t, cmd.Flags().Set("os-version", "1"))
|
||||
assert.NilError(t, cmd.Flags().Set("os-features", "feature1"))
|
||||
assert.NilError(t, cmd.Flags().Set("variant", "v7"))
|
||||
|
||||
expectedError = "manifest entry for image has invalid platform"
|
||||
cmd.Flags().Set("os", "freebsd")
|
||||
cmd.Flags().Set("arch", "fake")
|
||||
cmd.Flags().Set("os-version", "1")
|
||||
cmd.Flags().Set("os-features", "feature1")
|
||||
cmd.Flags().Set("variant", "v7")
|
||||
expectedError = "manifest entry for image has unsupported os/arch combination"
|
||||
assert.ErrorContains(t, cmd.Execute(), expectedError)
|
||||
|
||||
assert.NilError(t, cmd.Flags().Set("arch", "arm"))
|
||||
cmd.Flags().Set("arch", "arm")
|
||||
assert.NilError(t, cmd.Execute())
|
||||
|
||||
cmd = newInspectCommand(cli)
|
||||
|
||||
@@ -9,6 +9,53 @@ import (
|
||||
"github.com/docker/cli/cli/manifest/types"
|
||||
)
|
||||
|
||||
type osArch struct {
|
||||
os string
|
||||
arch string
|
||||
}
|
||||
|
||||
// Remove any unsupported os/arch combo
|
||||
// list of valid os/arch values (see "Optional Environment Variables" section
|
||||
// of https://go.dev/doc/install/source
|
||||
// Added linux/s390x as we know System z support already exists
|
||||
// Keep in sync with _docker_manifest_annotate in contrib/completion/bash/docker
|
||||
var validOSArches = map[osArch]bool{
|
||||
{os: "darwin", arch: "386"}: true,
|
||||
{os: "darwin", arch: "amd64"}: true,
|
||||
{os: "darwin", arch: "arm"}: true,
|
||||
{os: "darwin", arch: "arm64"}: true,
|
||||
{os: "dragonfly", arch: "amd64"}: true,
|
||||
{os: "freebsd", arch: "386"}: true,
|
||||
{os: "freebsd", arch: "amd64"}: true,
|
||||
{os: "freebsd", arch: "arm"}: true,
|
||||
{os: "linux", arch: "386"}: true,
|
||||
{os: "linux", arch: "amd64"}: true,
|
||||
{os: "linux", arch: "arm"}: true,
|
||||
{os: "linux", arch: "arm64"}: true,
|
||||
{os: "linux", arch: "ppc64le"}: true,
|
||||
{os: "linux", arch: "mips64"}: true,
|
||||
{os: "linux", arch: "mips64le"}: true,
|
||||
{os: "linux", arch: "riscv64"}: true,
|
||||
{os: "linux", arch: "s390x"}: true,
|
||||
{os: "netbsd", arch: "386"}: true,
|
||||
{os: "netbsd", arch: "amd64"}: true,
|
||||
{os: "netbsd", arch: "arm"}: true,
|
||||
{os: "openbsd", arch: "386"}: true,
|
||||
{os: "openbsd", arch: "amd64"}: true,
|
||||
{os: "openbsd", arch: "arm"}: true,
|
||||
{os: "plan9", arch: "386"}: true,
|
||||
{os: "plan9", arch: "amd64"}: true,
|
||||
{os: "solaris", arch: "amd64"}: true,
|
||||
{os: "windows", arch: "386"}: true,
|
||||
{os: "windows", arch: "amd64"}: true,
|
||||
}
|
||||
|
||||
func isValidOSArch(os string, arch string) bool {
|
||||
// check for existence of this combo
|
||||
_, ok := validOSArches[osArch{os, arch}]
|
||||
return ok
|
||||
}
|
||||
|
||||
func normalizeReference(ref string) (reference.Named, error) {
|
||||
namedRef, err := reference.ParseNormalizedNamed(ref)
|
||||
if err != nil {
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"github.com/docker/cli/internal/tui"
|
||||
registrytypes "github.com/moby/moby/api/types/registry"
|
||||
"github.com/moby/moby/client"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
@@ -227,7 +226,7 @@ func loginWithStoredCredentials(ctx context.Context, dockerCLI command.Cli, auth
|
||||
return resp.Auth.Status, err
|
||||
}
|
||||
|
||||
func loginUser(ctx context.Context, dockerCLI command.Cli, opts loginOptions, defaultUsername, serverAddress string) (message string, _ error) {
|
||||
func loginUser(ctx context.Context, dockerCLI command.Cli, opts loginOptions, defaultUsername, serverAddress string) (msg string, _ error) {
|
||||
// Some links documenting this:
|
||||
// - https://code.google.com/archive/p/mintty/issues/56
|
||||
// - https://github.com/docker/docker/issues/15272
|
||||
@@ -241,12 +240,13 @@ func loginUser(ctx context.Context, dockerCLI command.Cli, opts loginOptions, de
|
||||
|
||||
// If we're logging into the index server and the user didn't provide a username or password, use the device flow
|
||||
if serverAddress == registry.IndexServer && opts.user == "" && opts.password == "" {
|
||||
msg, err := loginWithDeviceCodeFlow(ctx, dockerCLI)
|
||||
if err == nil || !errors.Is(err, manager.ErrDeviceLoginStartFail) {
|
||||
var err error
|
||||
msg, err = loginWithDeviceCodeFlow(ctx, dockerCLI)
|
||||
// if the error represents a failure to initiate the device-code flow,
|
||||
// then we fallback to regular cli credentials login
|
||||
if !errors.Is(err, manager.ErrDeviceLoginStartFail) {
|
||||
return msg, err
|
||||
}
|
||||
|
||||
logrus.WithError(err).Debug("failed to start web-based login")
|
||||
_, _ = fmt.Fprint(dockerCLI.Err(), "Failed to start web-based login - falling back to command line login...\n\n")
|
||||
}
|
||||
|
||||
|
||||
@@ -210,9 +210,9 @@ func TestResourceOptionsToResourceRequirementsSwap(t *testing.T) {
|
||||
r, err = swapOptions.ToResourceRequirements(flags)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, r.SwapBytes != nil)
|
||||
assert.Check(t, is.Equal(*r.SwapBytes, int64(86000)))
|
||||
assert.Check(t, is.Equal(*(r.SwapBytes), int64(86000)))
|
||||
assert.Check(t, r.MemorySwappiness != nil)
|
||||
assert.Check(t, is.Equal(*r.MemorySwappiness, int64(23)))
|
||||
assert.Check(t, is.Equal(*(r.MemorySwappiness), int64(23)))
|
||||
}
|
||||
|
||||
func TestToServiceNetwork(t *testing.T) {
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
const ExportTimeout = 50 * time.Millisecond
|
||||
const exportTimeout = 50 * time.Millisecond
|
||||
|
||||
// TracerProvider is an extension of the trace.TracerProvider interface for CLI programs.
|
||||
type TracerProvider interface {
|
||||
@@ -205,7 +205,7 @@ func (r *cliReader) Shutdown(ctx context.Context) error {
|
||||
// We don't want CLI metrics to prevent the CLI from exiting
|
||||
// so if there's some kind of issue we need to abort pretty
|
||||
// quickly.
|
||||
ctx, cancel := context.WithTimeout(ctx, ExportTimeout)
|
||||
ctx, cancel := context.WithTimeout(ctx, exportTimeout)
|
||||
defer cancel()
|
||||
|
||||
return r.ForceFlush(ctx)
|
||||
|
||||
@@ -109,7 +109,7 @@ func dockerSpanExporter(ctx context.Context, cli Cli) []sdktrace.TracerProviderO
|
||||
otel.Handle(err)
|
||||
return nil
|
||||
}
|
||||
return []sdktrace.TracerProviderOption{sdktrace.WithBatcher(exp, sdktrace.WithExportTimeout(ExportTimeout))}
|
||||
return []sdktrace.TracerProviderOption{sdktrace.WithBatcher(exp, sdktrace.WithExportTimeout(exportTimeout))}
|
||||
}
|
||||
|
||||
func dockerMetricExporter(ctx context.Context, cli Cli) []sdkmetric.Option {
|
||||
|
||||
@@ -85,7 +85,7 @@ func startCobraCommandTimer(mp metric.MeterProvider, attrs []attribute.KeyValue)
|
||||
return func(err error) {
|
||||
// Use a new context for the export so that the command being cancelled
|
||||
// doesn't affect the metrics, and we get metrics for cancelled commands.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), ExportTimeout)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), exportTimeout)
|
||||
defer cancel()
|
||||
|
||||
duration := float64(time.Since(start)) / float64(time.Millisecond)
|
||||
|
||||
@@ -255,6 +255,7 @@ func mergeLoggingConfig(dst, src reflect.Value) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func mergeUlimitsConfig(dst, src reflect.Value) error {
|
||||
if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
|
||||
dst.Elem().Set(src.Elem())
|
||||
@@ -262,6 +263,7 @@ func mergeUlimitsConfig(dst, src reflect.Value) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func mergeShellCommand(dst, src reflect.Value) error {
|
||||
if src.Len() != 0 {
|
||||
dst.Set(src)
|
||||
@@ -269,6 +271,7 @@ func mergeShellCommand(dst, src reflect.Value) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func mergeServiceNetworkConfig(dst, src reflect.Value) error {
|
||||
if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() {
|
||||
dst.Elem().FieldByName("Aliases").Set(src.Elem().FieldByName("Aliases"))
|
||||
@@ -282,6 +285,7 @@ func mergeServiceNetworkConfig(dst, src reflect.Value) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func mergeUint64(dst, src reflect.Value) error {
|
||||
if !src.IsNil() {
|
||||
dst.Elem().Set(src.Elem())
|
||||
|
||||
@@ -27,13 +27,8 @@ func TerminatePrompt(ctx context.Context, t *testing.T, cmd *cobra.Command, cli
|
||||
}))
|
||||
cli.SetOut(outStream)
|
||||
|
||||
r, w, err := os.Pipe()
|
||||
r, _, err := os.Pipe()
|
||||
assert.NilError(t, err)
|
||||
t.Cleanup(func() {
|
||||
_ = w.Close()
|
||||
_ = r.Close()
|
||||
})
|
||||
|
||||
cli.SetIn(streams.NewIn(r))
|
||||
|
||||
notifyCtx, notifyCancel := context.WithCancel(ctx)
|
||||
@@ -63,6 +58,8 @@ func TerminatePrompt(ctx context.Context, t *testing.T, cmd *cobra.Command, cli
|
||||
}()
|
||||
}
|
||||
|
||||
assert.Check(t, cli.OutBuffer().Len() > 0)
|
||||
|
||||
// a small delay to ensure the plugin is prompting
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
|
||||
|
||||
@@ -11,9 +11,8 @@ import (
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/moby/moby/api/types/jsonstream"
|
||||
"github.com/docker/cli/internal/jsonstream"
|
||||
registrytypes "github.com/moby/moby/api/types/registry"
|
||||
"github.com/moby/moby/client/pkg/jsonmessage"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/theupdateframework/notary/client"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
@@ -47,7 +46,7 @@ func PushTrustedReference(ctx context.Context, ioStreams Streams, repoInfo *Repo
|
||||
// Count the times of calling for handleTarget,
|
||||
// if it is called more that once, that should be considered an error in a trusted push.
|
||||
cnt := 0
|
||||
handleTarget := func(msg jsonstream.Message) {
|
||||
handleTarget := func(msg jsonstream.JSONMessage) {
|
||||
cnt++
|
||||
if cnt > 1 {
|
||||
// handleTarget should only be called once. This will be treated as an error.
|
||||
@@ -79,14 +78,14 @@ func PushTrustedReference(ctx context.Context, ioStreams Streams, repoInfo *Repo
|
||||
default:
|
||||
// We want trust signatures to always take an explicit tag,
|
||||
// otherwise it will act as an untrusted push.
|
||||
if err := displayJSONMessages(ctx, in, ioStreams.Out(), nil); err != nil {
|
||||
if err := jsonstream.Display(ctx, in, ioStreams.Out()); err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = fmt.Fprintln(ioStreams.Err(), "No tag specified, skipping trust metadata push")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := displayJSONMessages(ctx, in, ioStreams.Out(), handleTarget); err != nil {
|
||||
if err := jsonstream.Display(ctx, in, ioStreams.Out(), jsonstream.WithAuxCallback(handleTarget)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -148,34 +147,3 @@ func PushTrustedReference(ctx context.Context, ioStreams Streams, repoInfo *Repo
|
||||
_, _ = fmt.Fprintf(ioStreams.Out(), "Successfully signed %s:%s\n", repoInfo.Name.Name(), tag)
|
||||
return nil
|
||||
}
|
||||
|
||||
type ctxReader struct {
|
||||
err chan error
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (r *ctxReader) Read(p []byte) (int, error) {
|
||||
select {
|
||||
case err := <-r.err:
|
||||
return 0, err
|
||||
default:
|
||||
return r.r.Read(p)
|
||||
}
|
||||
}
|
||||
|
||||
// displayJSONMessages prints the JSON messages from in to out. It returns
|
||||
// ctx.Err() if ctx is canceled, but not when in is closed.
|
||||
func displayJSONMessages(ctx context.Context, in io.Reader, out *streams.Out, auxCallback func(jsonstream.Message)) error {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
reader := &ctxReader{err: make(chan error, 1), r: in}
|
||||
stop := context.AfterFunc(ctx, func() { reader.err <- ctx.Err() })
|
||||
defer stop()
|
||||
|
||||
if err := jsonmessage.DisplayJSONMessagesStream(reader, out, out.FD(), out.IsTerminal(), auxCallback); err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package trust
|
||||
|
||||
import (
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/internal/commands"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
commands.Register(newTrustCommand)
|
||||
}
|
||||
|
||||
// newTrustCommand returns a cobra command for `trust` subcommands.
|
||||
func newTrustCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "trust",
|
||||
Short: "Manage trust on Docker images",
|
||||
Args: cli.NoArgs,
|
||||
RunE: command.ShowHelp(dockerCLI.Err()),
|
||||
|
||||
DisableFlagsInUseLine: true,
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newRevokeCommand(dockerCLI),
|
||||
newSignCommand(dockerCLI),
|
||||
newTrustKeyCommand(dockerCLI),
|
||||
newTrustSignerCommand(dockerCLI),
|
||||
newInspectCommand(dockerCLI),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
@@ -5,12 +5,12 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cmd/docker-trust/internal/trust"
|
||||
"github.com/docker/cli/internal/lazyregexp"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/theupdateframework/notary"
|
||||
"github.com/theupdateframework/notary/trustmanager"
|
||||
@@ -41,7 +41,7 @@ func newKeyGenerateCommand(dockerCLI command.Streams) *cobra.Command {
|
||||
}
|
||||
|
||||
// key names can use lowercase alphanumeric + _ + - characters
|
||||
var validKeyName = regexp.MustCompile(`^[a-z0-9][a-z0-9\_\-]*$`).MatchString
|
||||
var validKeyName = lazyregexp.New(`^[a-z0-9][a-z0-9\_\-]*$`).MatchString
|
||||
|
||||
// validate that all of the key names are unique and are alphanumeric + _ + -
|
||||
// and that we do not already have public key files in the target dir on disk
|
||||
|
||||
@@ -7,12 +7,12 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cmd/docker-trust/internal/trust"
|
||||
"github.com/docker/cli/internal/lazyregexp"
|
||||
"github.com/docker/cli/opts"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/theupdateframework/notary/client"
|
||||
@@ -45,7 +45,7 @@ func newSignerAddCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
var validSignerName = regexp.MustCompile(`^[a-z0-9][a-z0-9\_\-]*$`).MatchString
|
||||
var validSignerName = lazyregexp.New(`^[a-z0-9][a-z0-9\_\-]*$`).MatchString
|
||||
|
||||
func addSigner(ctx context.Context, dockerCLI command.Cli, options signerAddOptions) error {
|
||||
signerName := options.signer
|
||||
|
||||
@@ -580,15 +580,6 @@ func runDocker(ctx context.Context, dockerCli *command.DockerCli) error {
|
||||
}
|
||||
// Other missing or invalid plugins fall through to cmd.ExecuteContext(),
|
||||
// which reports the standard unknown-command error.
|
||||
//
|
||||
// Cobra handles "--version" before it checks for unknown commands,
|
||||
// so unset it to make "docker nonexistent --version" fail instead
|
||||
// of printing the version. A global "--version" flag (as in
|
||||
// "docker --version nonexistent") was already parsed, and still
|
||||
// prints the version.
|
||||
if !cmd.Flags().Changed("version") {
|
||||
cmd.Version = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4156,7 +4156,6 @@ _docker_manifest_annotate() {
|
||||
amd64
|
||||
arm
|
||||
arm64
|
||||
loong64
|
||||
mips64
|
||||
mips64le
|
||||
ppc64le
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# ALPINE_VERSION sets the version of the alpine base image to use.
|
||||
# It must be a supported tag in the docker.io/library/alpine image repository.
|
||||
ARG ALPINE_VERSION=3.24
|
||||
ARG ALPINE_VERSION=3.23
|
||||
|
||||
FROM alpine:${ALPINE_VERSION} AS gen
|
||||
RUN apk add --no-cache bash git
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.27.3
|
||||
ARG GO_VERSION=1.26.10
|
||||
|
||||
# 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.24
|
||||
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.38.1
|
||||
ARG BUILDX_VERSION=0.38.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.27.3
|
||||
ARG GO_VERSION=1.26.10
|
||||
|
||||
# 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.24
|
||||
ARG ALPINE_VERSION=3.23
|
||||
# GOLANGCI_LINT_VERSION sets the version of the golangci/golangci-lint image to use.
|
||||
ARG GOLANGCI_LINT_VERSION=v2.14.0
|
||||
ARG GOLANGCI_LINT_VERSION=v2.10.1
|
||||
|
||||
FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION}-alpine AS golangci-lint
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.27.3
|
||||
ARG GO_VERSION=1.26.10
|
||||
|
||||
# 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.24
|
||||
ARG MODOUTDATED_VERSION=v0.9.0
|
||||
ARG ALPINE_VERSION=3.23
|
||||
ARG MODOUTDATED_VERSION=v0.8.0
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
|
||||
ENV GOTOOLCHAIN=local
|
||||
|
||||
@@ -44,8 +44,8 @@ created. Supported `Dockerfile` instructions:
|
||||
$ docker ps
|
||||
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
c3f279d17e0a ubuntu:26.04 /bin/bash 7 days ago Up 25 hours desperate_dubinsky
|
||||
197387f1b436 ubuntu:26.04 /bin/bash 7 days ago Up 25 hours focused_hamilton
|
||||
c3f279d17e0a ubuntu:24.04 /bin/bash 7 days ago Up 25 hours desperate_dubinsky
|
||||
197387f1b436 ubuntu:24.04 /bin/bash 7 days ago Up 25 hours focused_hamilton
|
||||
|
||||
$ docker commit c3f279d17e0a svendowideit/testimage:version3
|
||||
|
||||
@@ -63,8 +63,8 @@ svendowideit/testimage version3 f5283438590d 16 sec
|
||||
$ docker ps
|
||||
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
c3f279d17e0a ubuntu:26.04 /bin/bash 7 days ago Up 25 hours desperate_dubinsky
|
||||
197387f1b436 ubuntu:26.04 /bin/bash 7 days ago Up 25 hours focused_hamilton
|
||||
c3f279d17e0a ubuntu:24.04 /bin/bash 7 days ago Up 25 hours desperate_dubinsky
|
||||
197387f1b436 ubuntu:24.04 /bin/bash 7 days ago Up 25 hours focused_hamilton
|
||||
|
||||
$ docker inspect -f "{{ .Config.Env }}" c3f279d17e0a
|
||||
|
||||
@@ -85,8 +85,8 @@ $ docker inspect -f "{{ .Config.Env }}" f5283438590d
|
||||
$ docker ps
|
||||
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
c3f279d17e0a ubuntu:26.04 /bin/bash 7 days ago Up 25 hours desperate_dubinsky
|
||||
197387f1b436 ubuntu:26.04 /bin/bash 7 days ago Up 25 hours focused_hamilton
|
||||
c3f279d17e0a ubuntu:24.04 /bin/bash 7 days ago Up 25 hours desperate_dubinsky
|
||||
197387f1b436 ubuntu:24.04 /bin/bash 7 days ago Up 25 hours focused_hamilton
|
||||
|
||||
$ docker commit --change='CMD ["apachectl", "-DFOREGROUND"]' -c "EXPOSE 80" c3f279d17e0a svendowideit/testimage:version4
|
||||
|
||||
@@ -100,6 +100,6 @@ $ docker ps
|
||||
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
89373736e2e7 testimage:version4 "apachectl -DFOREGROU" 3 seconds ago Up 2 seconds 80/tcp distracted_fermat
|
||||
c3f279d17e0a ubuntu:26.04 /bin/bash 7 days ago Up 25 hours desperate_dubinsky
|
||||
197387f1b436 ubuntu:26.04 /bin/bash 7 days ago Up 25 hours focused_hamilton
|
||||
c3f279d17e0a ubuntu:24.04 /bin/bash 7 days ago Up 25 hours desperate_dubinsky
|
||||
197387f1b436 ubuntu:24.04 /bin/bash 7 days ago Up 25 hours focused_hamilton
|
||||
```
|
||||
|
||||
@@ -33,7 +33,7 @@ Running `docker ps --no-trunc` showing 2 linked containers.
|
||||
$ docker ps --no-trunc
|
||||
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
ca5534a51dd04bbcebe9b23ba05f389466cf0c190f1f8f182d7eea92a9671d00 ubuntu:26.04 bash 17 seconds ago Up 16 seconds 3300-3310/tcp webapp
|
||||
ca5534a51dd04bbcebe9b23ba05f389466cf0c190f1f8f182d7eea92a9671d00 ubuntu:24.04 bash 17 seconds ago Up 16 seconds 3300-3310/tcp webapp
|
||||
9ca9747b233100676a48cc7806131586213fa5dab86dd1972d6a8732e3a84a4d crosbymichael/redis:latest /redis-server --dir 33 minutes ago Up 33 minutes 6379/tcp redis,webapp/db
|
||||
```
|
||||
|
||||
@@ -240,13 +240,13 @@ CONTAINER ID IMAGE COMMAND CREATED
|
||||
919e1179bdb8 ubuntu-c1 "top" About a minute ago Up About a minute admiring_lovelace
|
||||
```
|
||||
|
||||
Match containers based on the `ubuntu` version `26.04` image:
|
||||
Match containers based on the `ubuntu` version `24.04` image:
|
||||
|
||||
```console
|
||||
$ docker ps --filter ancestor=ubuntu:26.04
|
||||
$ docker ps --filter ancestor=ubuntu:24.04
|
||||
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
82a598284012 ubuntu:26.04 "top" 3 minutes ago Up 3 minutes sleepy_bose
|
||||
82a598284012 ubuntu:24.04 "top" 3 minutes ago Up 3 minutes sleepy_bose
|
||||
```
|
||||
|
||||
The following matches containers based on the layer `d0e008c6cf02` or an image
|
||||
@@ -256,7 +256,7 @@ that have this layer in its layer stack.
|
||||
$ docker ps --filter ancestor=d0e008c6cf02
|
||||
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
82a598284012 ubuntu:26.04 "top" 3 minutes ago Up 3 minutes sleepy_bose
|
||||
82a598284012 ubuntu:24.04 "top" 3 minutes ago Up 3 minutes sleepy_bose
|
||||
```
|
||||
|
||||
#### Create time
|
||||
|
||||
@@ -66,18 +66,18 @@ docker.io/library/debian:latest
|
||||
Docker images can consist of multiple layers. In the example above, the image
|
||||
consists of a single layer; `e756f3fdd6a3`.
|
||||
|
||||
Layers can be reused by images. For example, the `debian:trixie` image shares
|
||||
its layer with the `debian:latest`. Pulling the `debian:trixie` image therefore
|
||||
Layers can be reused by images. For example, the `debian:bookworm` image shares
|
||||
its layer with the `debian:latest`. Pulling the `debian:bookworm` image therefore
|
||||
only pulls its metadata, but not its layers, because the layer is already present
|
||||
locally:
|
||||
|
||||
```console
|
||||
$ docker image pull debian:trixie
|
||||
$ docker image pull debian:bookworm
|
||||
|
||||
trixie: Pulling from library/debian
|
||||
bookworm: Pulling from library/debian
|
||||
Digest: sha256:3f1d6c17773a45c97bd8f158d665c9709d7b29ed7917ac934086ad96f92e4510
|
||||
Status: Downloaded newer image for debian:trixie
|
||||
docker.io/library/debian:trixie
|
||||
Status: Downloaded newer image for debian:bookworm
|
||||
docker.io/library/debian:bookworm
|
||||
```
|
||||
|
||||
To see which images are present locally, use the [`docker images`](image_ls.md)
|
||||
@@ -87,13 +87,13 @@ command:
|
||||
$ docker images
|
||||
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
debian trixie 4eacea30377a 8 days ago 124MB
|
||||
debian bookworm 4eacea30377a 8 days ago 124MB
|
||||
debian latest 4eacea30377a 8 days ago 124MB
|
||||
```
|
||||
|
||||
Docker uses a content-addressable image store, and the image ID is a SHA256
|
||||
digest covering the image's configuration and layers. In the example above,
|
||||
`debian:trixie` and `debian:latest` have the same image ID because they are
|
||||
`debian:bookworm` and `debian:latest` have the same image ID because they are
|
||||
the same image tagged with different names. Because they are the same image,
|
||||
their layers are stored only once and do not consume extra disk space.
|
||||
|
||||
@@ -106,8 +106,8 @@ refer to [understand images, containers, and storage drivers](https://docs.docke
|
||||
So far, you've pulled images by their name (and "tag"). Using names and tags is
|
||||
a convenient way to work with images. When using tags, you can `docker pull` an
|
||||
image again to make sure you have the most up-to-date version of that image.
|
||||
For example, `docker pull ubuntu:26.04` pulls the latest version of the Ubuntu
|
||||
26.04 image.
|
||||
For example, `docker pull ubuntu:24.04` pulls the latest version of the Ubuntu
|
||||
24.04 image.
|
||||
|
||||
In some cases you don't want images to be updated to newer versions, but prefer
|
||||
to use a fixed version of an image. Docker enables you to pull an image by its
|
||||
@@ -116,16 +116,16 @@ of an image to pull. Doing so, allows you to "pin" an image to that version,
|
||||
and guarantee that the image you're using is always the same.
|
||||
|
||||
To know the digest of an image, pull the image first. Let's pull the latest
|
||||
`ubuntu:26.04` image from Docker Hub:
|
||||
`ubuntu:24.04` image from Docker Hub:
|
||||
|
||||
```console
|
||||
$ docker pull ubuntu:26.04
|
||||
$ docker pull ubuntu:24.04
|
||||
|
||||
26.04: Pulling from library/ubuntu
|
||||
24.04: Pulling from library/ubuntu
|
||||
125a6e411906: Pull complete
|
||||
Digest: sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30
|
||||
Status: Downloaded newer image for ubuntu:26.04
|
||||
docker.io/library/ubuntu:26.04
|
||||
Status: Downloaded newer image for ubuntu:24.04
|
||||
docker.io/library/ubuntu:24.04
|
||||
```
|
||||
|
||||
Docker prints the digest of the image after the pull has finished. In the example
|
||||
@@ -213,11 +213,11 @@ shorthand) to see the images that were pulled. The example below shows all the
|
||||
```console
|
||||
$ docker image ls --filter reference=ubuntu
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
ubuntu 24.04 8a3cdc4d1ad3 3 weeks ago 77.9MB
|
||||
ubuntu noble 8a3cdc4d1ad3 3 weeks ago 77.9MB
|
||||
ubuntu 26.04 35a88802559d 6 weeks ago 78.1MB
|
||||
ubuntu 22.04 8a3cdc4d1ad3 3 weeks ago 77.9MB
|
||||
ubuntu jammy 8a3cdc4d1ad3 3 weeks ago 77.9MB
|
||||
ubuntu 24.04 35a88802559d 6 weeks ago 78.1MB
|
||||
ubuntu latest 35a88802559d 6 weeks ago 78.1MB
|
||||
ubuntu resolute 35a88802559d 6 weeks ago 78.1MB
|
||||
ubuntu noble 35a88802559d 6 weeks ago 78.1MB
|
||||
```
|
||||
|
||||
### Cancel a pull
|
||||
|
||||
@@ -180,12 +180,13 @@ code, and authenticate.
|
||||
```console
|
||||
$ docker login
|
||||
|
||||
Using web-based login. To sign in with credentials on the command line, use 'docker login -u <username>'
|
||||
USING WEB-BASED LOGIN
|
||||
To sign in with credentials on the command line, use 'docker login -u <username>'
|
||||
|
||||
Your one-time device confirmation code is: LNFR-PGCJ
|
||||
Open this URL to authenticate: https://login.docker.com/activate?user_code=LNFR-PGCJ
|
||||
Press ENTER to open your browser or submit your device code here: https://login.docker.com/activate
|
||||
|
||||
Press ENTER to open the URL in your browser, or Ctrl+C to cancel.
|
||||
Waiting for authentication in the browser…
|
||||
```
|
||||
|
||||
After entering the code in your browser, you are authenticated to Docker Hub
|
||||
|
||||
@@ -706,7 +706,7 @@ follows:
|
||||
| `node.platform.os` | Node operating system | `node.platform.os==windows` |
|
||||
| `node.platform.arch` | Node architecture | `node.platform.arch==x86_64` |
|
||||
| `node.labels` | User-defined node labels | `node.labels.security==high` |
|
||||
| `engine.labels` | Docker Engine's labels | `engine.labels.operatingsystem==ubuntu-26.04` |
|
||||
| `engine.labels` | Docker Engine's labels | `engine.labels.operatingsystem==ubuntu-24.04` |
|
||||
|
||||
`engine.labels` apply to Docker Engine labels like operating system, drivers,
|
||||
etc. Swarm administrators add `node.labels` for operational purposes by using
|
||||
|
||||
@@ -339,8 +339,8 @@ $ docker events --filter 'type=network'
|
||||
|
||||
$ docker events --filter 'container=container_1' --filter 'container=container_2'
|
||||
|
||||
2014-09-03T15:49:29.999999999Z07:00 container die 4386fb97867d (image=ubuntu:26.04)
|
||||
2014-05-10T17:42:14.999999999Z07:00 container stop 4386fb97867d (image=ubuntu:26.04)
|
||||
2014-09-03T15:49:29.999999999Z07:00 container die 4386fb97867d (image=ubuntu:24.04)
|
||||
2014-05-10T17:42:14.999999999Z07:00 container stop 4386fb97867d (image=ubuntu:24.04)
|
||||
2014-05-10T17:42:14.999999999Z07:00 container die 7805c1d35632 (imager=redis:7.2)
|
||||
2014-09-03T15:49:29.999999999Z07:00 container stop 7805c1d35632 (image=redis:7.2)
|
||||
|
||||
|
||||
+19
-19
@@ -35,7 +35,7 @@ reference to create or run a container based on an image.
|
||||
|
||||
An image tag is the image version, which defaults to `latest` when omitted. Use
|
||||
the tag to run a container from specific version of an image. For example, to
|
||||
run version `26.04` of the `ubuntu` image: `docker run ubuntu:26.04`.
|
||||
run version `24.04` of the `ubuntu` image: `docker run ubuntu:24.04`.
|
||||
|
||||
#### Image digests
|
||||
|
||||
@@ -406,14 +406,14 @@ We have four ways to set user memory usage:
|
||||
Examples:
|
||||
|
||||
```console
|
||||
$ docker run -it ubuntu:26.04 /bin/bash
|
||||
$ docker run -it ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
We set nothing about memory, this means the processes in the container can use
|
||||
as much memory and swap memory as they need.
|
||||
|
||||
```console
|
||||
$ docker run -it -m 300M --memory-swap -1 ubuntu:26.04 /bin/bash
|
||||
$ docker run -it -m 300M --memory-swap -1 ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
We set memory limit and disabled swap memory limit, this means the processes in
|
||||
@@ -421,7 +421,7 @@ the container can use 300M memory and as much swap memory as they need (if the
|
||||
host supports swap memory).
|
||||
|
||||
```console
|
||||
$ docker run -it -m 300M ubuntu:26.04 /bin/bash
|
||||
$ docker run -it -m 300M ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
We set memory limit only, this means the processes in the container can use
|
||||
@@ -430,7 +430,7 @@ We set memory limit only, this means the processes in the container can use
|
||||
would be 2*300M, so processes can use 300M swap memory as well.
|
||||
|
||||
```console
|
||||
$ docker run -it -m 300M --memory-swap 1G ubuntu:26.04 /bin/bash
|
||||
$ docker run -it -m 300M --memory-swap 1G ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
We set both memory and swap memory, so the processes in the container can use
|
||||
@@ -456,7 +456,7 @@ The following example limits the memory (`-m`) to 500M and sets the memory
|
||||
reservation to 200M.
|
||||
|
||||
```console
|
||||
$ docker run -it -m 500M --memory-reservation 200M ubuntu:26.04 /bin/bash
|
||||
$ docker run -it -m 500M --memory-reservation 200M ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
Under this configuration, when the container consumes memory more than 200M and
|
||||
@@ -466,7 +466,7 @@ memory below 200M.
|
||||
The following example set memory reservation to 1G without a hard memory limit.
|
||||
|
||||
```console
|
||||
$ docker run -it --memory-reservation 1G ubuntu:26.04 /bin/bash
|
||||
$ docker run -it --memory-reservation 1G ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
The container can use as much memory as it needs. The memory reservation setting
|
||||
@@ -484,13 +484,13 @@ The following example limits the memory to 100M and disables the OOM killer for
|
||||
this container:
|
||||
|
||||
```console
|
||||
$ docker run -it -m 100M --oom-kill-disable ubuntu:26.04 /bin/bash
|
||||
$ docker run -it -m 100M --oom-kill-disable ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
The following example, illustrates a dangerous way to use the flag:
|
||||
|
||||
```console
|
||||
$ docker run -it --oom-kill-disable ubuntu:26.04 /bin/bash
|
||||
$ docker run -it --oom-kill-disable ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
The container has unlimited memory which can cause the host to run out memory
|
||||
@@ -510,7 +510,7 @@ between 0 and 100. A value of 0 turns off anonymous page swapping. A value of
|
||||
For example, you can set:
|
||||
|
||||
```console
|
||||
$ docker run -it --memory-swappiness=0 ubuntu:26.04 /bin/bash
|
||||
$ docker run -it --memory-swappiness=0 ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
Setting the `--memory-swappiness` option is helpful when you want to retain the
|
||||
@@ -561,7 +561,7 @@ And usually `--cpu-period` should work with `--cpu-quota`.
|
||||
Examples:
|
||||
|
||||
```console
|
||||
$ docker run -it --cpu-period=50000 --cpu-quota=25000 ubuntu:26.04 /bin/bash
|
||||
$ docker run -it --cpu-period=50000 --cpu-quota=25000 ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
If there is 1 CPU, this means the container can get 50% CPU worth of run-time every 50ms.
|
||||
@@ -582,13 +582,13 @@ We can set cpus in which to allow execution for containers.
|
||||
Examples:
|
||||
|
||||
```console
|
||||
$ docker run -it --cpuset-cpus="1,3" ubuntu:26.04 /bin/bash
|
||||
$ docker run -it --cpuset-cpus="1,3" ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
This means processes in container can be executed on cpu 1 and cpu 3.
|
||||
|
||||
```console
|
||||
$ docker run -it --cpuset-cpus="0-2" ubuntu:26.04 /bin/bash
|
||||
$ docker run -it --cpuset-cpus="0-2" ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
This means processes in container can be executed on cpu 0, cpu 1 and cpu 2.
|
||||
@@ -599,14 +599,14 @@ on NUMA systems.
|
||||
Examples:
|
||||
|
||||
```console
|
||||
$ docker run -it --cpuset-mems="1,3" ubuntu:26.04 /bin/bash
|
||||
$ docker run -it --cpuset-mems="1,3" ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
This example restricts the processes in the container to only use memory from
|
||||
memory nodes 1 and 3.
|
||||
|
||||
```console
|
||||
$ docker run -it --cpuset-mems="0-2" ubuntu:26.04 /bin/bash
|
||||
$ docker run -it --cpuset-mems="0-2" ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
This example restricts the processes in the container to only use memory from
|
||||
@@ -637,8 +637,8 @@ For example, the commands below create two containers with different blkio
|
||||
weight:
|
||||
|
||||
```console
|
||||
$ docker run -it --name c1 --blkio-weight 300 ubuntu:26.04 /bin/bash
|
||||
$ docker run -it --name c2 --blkio-weight 600 ubuntu:26.04 /bin/bash
|
||||
$ docker run -it --name c1 --blkio-weight 300 ubuntu:24.04 /bin/bash
|
||||
$ docker run -it --name c2 --blkio-weight 600 ubuntu:24.04 /bin/bash
|
||||
```
|
||||
|
||||
If you do block IO in the two containers at the same time, by, for example:
|
||||
@@ -854,11 +854,11 @@ For interacting with the network stack, instead of using `--privileged` they
|
||||
should use `--cap-add=NET_ADMIN` to modify the network interfaces.
|
||||
|
||||
```console
|
||||
$ docker run -it --rm ubuntu:26.04 ip link add dummy0 type dummy
|
||||
$ docker run -it --rm ubuntu:24.04 ip link add dummy0 type dummy
|
||||
|
||||
RTNETLINK answers: Operation not permitted
|
||||
|
||||
$ docker run -it --rm --cap-add=NET_ADMIN ubuntu:26.04 ip link add dummy0 type dummy
|
||||
$ docker run -it --rm --cap-add=NET_ADMIN ubuntu:24.04 ip link add dummy0 type dummy
|
||||
```
|
||||
|
||||
To mount a FUSE based filesystem, you need to combine both `--cap-add` and
|
||||
|
||||
@@ -62,22 +62,6 @@ func TestNonexistingHelp(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// TestNonexistingVersion ensures correct behaviour when invoking a
|
||||
// nonexistent plugin with `--version`.
|
||||
func TestNonexistingVersion(t *testing.T) {
|
||||
run, _, cleanup := prepare(t)
|
||||
defer cleanup()
|
||||
|
||||
for _, flag := range []string{"--version", "-v"} {
|
||||
res := icmd.RunCmd(run("nonexistent", flag))
|
||||
res.Assert(t, icmd.Expected{
|
||||
ExitCode: 1,
|
||||
Out: icmd.None,
|
||||
})
|
||||
golden.Assert(t, res.Stderr(), "docker-nonexistent-err.golden")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRunBad ensures correct behaviour when running an existent but invalid plugin
|
||||
func TestRunBad(t *testing.T) {
|
||||
run, _, cleanup := prepare(t)
|
||||
|
||||
@@ -30,5 +30,5 @@ func TestOauthLogin(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
|
||||
output, _ := io.ReadAll(p)
|
||||
assert.Check(t, strings.Contains(string(output), "Using web-based login"), string(output))
|
||||
assert.Check(t, strings.Contains(string(output), "USING WEB-BASED LOGIN"), string(output))
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/config/credentials"
|
||||
@@ -18,6 +17,8 @@ import (
|
||||
"github.com/docker/cli/internal/registry"
|
||||
"github.com/docker/cli/internal/tui"
|
||||
"github.com/morikuni/aec"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/pkg/browser"
|
||||
)
|
||||
|
||||
@@ -82,18 +83,19 @@ var ErrDeviceLoginStartFail = errors.New("failed to start device code flow login
|
||||
// The retrieved tokens are stored in the credentials store (under a separate
|
||||
// key), and the refresh token is concatenated with the client ID.
|
||||
func (m *OAuthManager) LoginDevice(ctx context.Context, w io.Writer) (*types.AuthConfig, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
state, err := m.api.GetDeviceCode(ctx, m.audience)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %w", ErrDeviceLoginStartFail, err)
|
||||
logrus.Debugf("failed to start device code login: %v", err)
|
||||
return nil, ErrDeviceLoginStartFail
|
||||
}
|
||||
|
||||
if state.UserCode == "" {
|
||||
return nil, fmt.Errorf("%w: missing user code", ErrDeviceLoginStartFail)
|
||||
logrus.Debugf("failed to start device code login: missing user code")
|
||||
return nil, ErrDeviceLoginStartFail
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(w, aec.Bold.Apply("\nUSING WEB-BASED LOGIN"))
|
||||
|
||||
var out tui.Output
|
||||
switch stream := w.(type) {
|
||||
case *streams.Out:
|
||||
@@ -101,10 +103,9 @@ func (m *OAuthManager) LoginDevice(ctx context.Context, w io.Writer) (*types.Aut
|
||||
default:
|
||||
out = tui.NewOutput(streams.NewOut(w))
|
||||
}
|
||||
out.PrintNote("Using web-based login. To sign in with credentials on the command line, use 'docker login -u <username>'\n")
|
||||
_, _ = fmt.Fprintln(w, "Your one-time device confirmation code is:", aec.Bold.Apply(state.UserCode))
|
||||
_, _ = fmt.Fprintln(w, "Open this URL to authenticate:", aec.Underline.Apply(state.VerificationURI))
|
||||
_, _ = fmt.Fprintln(w, "\n"+aec.Bold.Apply("Press ENTER")+" to open the URL in your browser, or "+aec.Bold.Apply("Ctrl+C")+" to cancel.")
|
||||
out.PrintNote("To sign in with credentials on the command line, use 'docker login -u <username>'\n")
|
||||
_, _ = fmt.Fprintf(w, "\nYour one-time device confirmation code is: "+aec.Bold.Apply("%s\n"), state.UserCode)
|
||||
_, _ = fmt.Fprintf(w, aec.Bold.Apply("Press ENTER")+" to open your browser or submit your device code here: "+aec.Underline.Apply("%s\n"), strings.Split(state.VerificationURI, "?")[0])
|
||||
|
||||
tokenResChan := make(chan api.TokenResponse)
|
||||
waitForTokenErrChan := make(chan error)
|
||||
@@ -119,25 +120,11 @@ func (m *OAuthManager) LoginDevice(ctx context.Context, w io.Writer) (*types.Aut
|
||||
|
||||
go func() {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
if _, err := reader.ReadString('\n'); err != nil || ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
err := m.openBrowser(state.VerificationURI)
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
_, _ = fmt.Fprintln(w, "No browser found. Open the URL above manually to continue.")
|
||||
} else {
|
||||
_, _ = fmt.Fprintln(w, "Failed to open browser:", err)
|
||||
_, _ = fmt.Fprintln(w, "Open the URL above manually to continue.")
|
||||
}
|
||||
return
|
||||
}
|
||||
_, _ = fmt.Fprintln(w, "\nOpening browser…")
|
||||
_, _ = reader.ReadString('\n')
|
||||
_ = m.openBrowser(state.VerificationURI)
|
||||
}()
|
||||
|
||||
_, _ = fmt.Fprint(w, "\nWaiting for authentication in the browser…\n")
|
||||
var tokenRes api.TokenResponse
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
||||
@@ -69,10 +69,10 @@ func getHTTPTransport(authConfig registrytypes.AuthConfig, endpoint registry.API
|
||||
// get the http transport, this will be used in a client to upload manifest
|
||||
base := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
TLSClientConfig: endpoint.TLSConfig,
|
||||
DisableKeepAlives: true,
|
||||
|
||||
@@ -27,13 +27,8 @@ func TerminatePrompt(ctx context.Context, t *testing.T, cmd *cobra.Command, cli
|
||||
}))
|
||||
cli.SetOut(outStream)
|
||||
|
||||
r, w, err := os.Pipe()
|
||||
r, _, err := os.Pipe()
|
||||
assert.NilError(t, err)
|
||||
t.Cleanup(func() {
|
||||
_ = w.Close()
|
||||
_ = r.Close()
|
||||
})
|
||||
|
||||
cli.SetIn(streams.NewIn(r))
|
||||
|
||||
notifyCtx, notifyCancel := context.WithCancel(ctx)
|
||||
@@ -63,6 +58,8 @@ func TerminatePrompt(ctx context.Context, t *testing.T, cmd *cobra.Command, cli
|
||||
}()
|
||||
}
|
||||
|
||||
assert.Check(t, cli.OutBuffer().Len() > 0)
|
||||
|
||||
// a small delay to ensure the plugin is prompting
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
|
||||
|
||||
+6
-14
@@ -83,24 +83,16 @@ set as the **URL**, the repository is cloned locally and then sent as the contex
|
||||
Add a line to /etc/hosts. The format is hostname=ip, or hostname:ip.
|
||||
The **--add-host** option can be set multiple times.
|
||||
|
||||
**--build-arg** *name*[=*value*]
|
||||
Set a build-time variable. The *name* is the variable name, as declared
|
||||
by an `ARG` instruction in the Dockerfile. If *value* is omitted, the
|
||||
value of the variable *name* from the local environment is used; if it is
|
||||
not set there either, the default from the `ARG` instruction (if any) is
|
||||
used.
|
||||
|
||||
Build-time variables are available as environment variables to commands
|
||||
run by the Dockerfile's `RUN` instructions, and can be used for variable
|
||||
expansion in other Dockerfile instructions. An environment variable defined
|
||||
by an `ENV` instruction always overrides an `ARG` of the same name. This
|
||||
mechanism is not meant for passing secret values.
|
||||
**--build-arg** *variable*
|
||||
name and value of a **buildarg**.
|
||||
|
||||
For example, if you want to pass a value for `http_proxy`, use
|
||||
`--build-arg=http_proxy="http://some.proxy.url"`
|
||||
|
||||
See also https://docs.docker.com/reference/dockerfile/#arg and
|
||||
https://docs.docker.com/reference/cli/docker/buildx/build/#build-arg.
|
||||
Users pass these values at build-time. Docker uses the `buildargs` as the
|
||||
environment context for command(s) run via the Dockerfile's `RUN` instruction
|
||||
or for variable expansion in other Dockerfile instructions. This is not meant
|
||||
for passing secret values. [Read more about the buildargs instruction](https://docs.docker.com/engine/reference/builder/#arg)
|
||||
|
||||
**--cache-from** ""
|
||||
Set image that will be used as a build cache source.
|
||||
|
||||
@@ -48,10 +48,10 @@ Valid placeholders for the Go template are listed below:
|
||||
|
||||
$ docker container ls -a
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
a87ecb4f327c ubuntu:26.04 /bin/sh -c #(nop) MA 20 minutes ago Exit 0 desperate_brattain
|
||||
a87ecb4f327c ubuntu:24.04 /bin/sh -c #(nop) MA 20 minutes ago Exit 0 desperate_brattain
|
||||
01946d9d34d8 busybox /bin/sh -c #(nop) MA 33 minutes ago Exit 0 thirsty_bell
|
||||
c1d3b0166030 acffc0358b9e /bin/sh -c yum -y up 2 weeks ago Exit 1 determined_torvalds
|
||||
41d50ecd2f57 ubuntu:26.04 /bin/sh -c #(nop) MA 2 weeks ago Exit 0 drunk_pike
|
||||
41d50ecd2f57 ubuntu:24.04 /bin/sh -c #(nop) MA 2 weeks ago Exit 0 drunk_pike
|
||||
|
||||
## Display only IDs of all containers, including non-running
|
||||
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ The title REPOSITORY for the first title may seem confusing. It is essentially
|
||||
the image name. However, because you can tag a specific image, and multiple tags
|
||||
(image instances) can be associated with a single name, the name is really a
|
||||
repository for all tagged images of the same name. For example consider an image
|
||||
called ubuntu. It may be tagged with 26.04 or 24.04, etc. to manage different
|
||||
called ubuntu. It may be tagged with 24.04 or 22.04, etc. to manage different
|
||||
versions.
|
||||
|
||||
## Filters
|
||||
|
||||
+19
-19
@@ -27,17 +27,17 @@ Docker Engine uses the `:latest` tag as a default. This example pulls the
|
||||
Docker images can consist of multiple layers. In the example above, the image
|
||||
consists of a single layer; `e756f3fdd6a3`.
|
||||
|
||||
Layers can be reused by images. For example, the `debian:trixie` image shares
|
||||
its layer with the `debian:latest`. Pulling the `debian:trixie` image therefore
|
||||
Layers can be reused by images. For example, the `debian:bookworm` image shares
|
||||
its layer with the `debian:latest`. Pulling the `debian:bookworm` image therefore
|
||||
only pulls its metadata, but not its layers, because the layer is already present
|
||||
locally:
|
||||
|
||||
$ docker image pull debian:trixie
|
||||
$ docker image pull debian:bookworm
|
||||
|
||||
trixie: Pulling from library/debian
|
||||
bookworm: Pulling from library/debian
|
||||
Digest: sha256:3f1d6c17773a45c97bd8f158d665c9709d7b29ed7917ac934086ad96f92e4510
|
||||
Status: Downloaded newer image for debian:trixie
|
||||
docker.io/library/debian:trixie
|
||||
Status: Downloaded newer image for debian:bookworm
|
||||
docker.io/library/debian:bookworm
|
||||
|
||||
To see which images are present locally, use the **docker-images(1)**
|
||||
command:
|
||||
@@ -45,12 +45,12 @@ command:
|
||||
$ docker images
|
||||
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
debian trixie 4eacea30377a 8 days ago 124MB
|
||||
debian bookworm 4eacea30377a 8 days ago 124MB
|
||||
debian latest 4eacea30377a 8 days ago 124MB
|
||||
|
||||
Docker uses a content-addressable image store, and the image ID is a SHA256
|
||||
digest covering the image's configuration and layers. In the example above,
|
||||
`debian:trixie` and `debian:latest` have the same image ID because they are
|
||||
`debian:bookworm` and `debian:latest` have the same image ID because they are
|
||||
the *same* image tagged with different names. Because they are the same image,
|
||||
their layers are stored only once and do not consume extra disk space.
|
||||
|
||||
@@ -64,8 +64,8 @@ in the online documentation.
|
||||
So far, you've pulled images by their name (and "tag"). Using names and tags is
|
||||
a convenient way to work with images. When using tags, you can `docker image pull` an
|
||||
image again to make sure you have the most up-to-date version of that image.
|
||||
For example, `docker image pull ubuntu:26.04` pulls the latest version of the Ubuntu
|
||||
26.04 image.
|
||||
For example, `docker image pull ubuntu:24.04` pulls the latest version of the Ubuntu
|
||||
24.04 image.
|
||||
|
||||
In some cases you don't want images to be updated to newer versions, but prefer
|
||||
to use a fixed version of an image. Docker enables you to pull an image by its
|
||||
@@ -74,15 +74,15 @@ of an image to pull. Doing so, allows you to "pin" an image to that version,
|
||||
and guarantee that the image you're using is always the same.
|
||||
|
||||
To know the digest of an image, pull the image first. Let's pull the latest
|
||||
`ubuntu:26.04` image from Docker Hub:
|
||||
`ubuntu:24.04` image from Docker Hub:
|
||||
|
||||
$ docker image pull ubuntu:26.04
|
||||
$ docker image pull ubuntu:24.04
|
||||
|
||||
26.04: Pulling from library/ubuntu
|
||||
24.04: Pulling from library/ubuntu
|
||||
125a6e411906: Pull complete
|
||||
Digest: sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30
|
||||
Status: Downloaded newer image for ubuntu:26.04
|
||||
docker.io/library/ubuntu:26.04
|
||||
Status: Downloaded newer image for ubuntu:24.04
|
||||
docker.io/library/ubuntu:24.04
|
||||
|
||||
Docker prints the digest of the image after the pull has finished. In the example
|
||||
above, the digest of the image is:
|
||||
@@ -159,11 +159,11 @@ images that are present locally:
|
||||
|
||||
$ docker image ls --filter reference=ubuntu
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
ubuntu 24.04 8a3cdc4d1ad3 3 weeks ago 77.9MB
|
||||
ubuntu noble 8a3cdc4d1ad3 3 weeks ago 77.9MB
|
||||
ubuntu 26.04 35a88802559d 6 weeks ago 78.1MB
|
||||
ubuntu 22.04 8a3cdc4d1ad3 3 weeks ago 77.9MB
|
||||
ubuntu jammy 8a3cdc4d1ad3 3 weeks ago 77.9MB
|
||||
ubuntu 24.04 35a88802559d 6 weeks ago 78.1MB
|
||||
ubuntu latest 35a88802559d 6 weeks ago 78.1MB
|
||||
ubuntu resolute 35a88802559d 6 weeks ago 78.1MB
|
||||
ubuntu noble 35a88802559d 6 weeks ago 78.1MB
|
||||
|
||||
## Cancel a pull
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ The following example outputs all events that were generated in the last 3 minut
|
||||
relative to the current time on the client machine:
|
||||
|
||||
# docker events --since '3m'
|
||||
2015-05-12T11:51:30.999999999Z07:00 4386fb97867d: (from ubuntu:26.04) die
|
||||
2015-05-12T15:52:12.999999999Z07:00 4386fb97867d: (from ubuntu:26.04) stop
|
||||
2015-05-12T11:51:30.999999999Z07:00 4386fb97867d: (from ubuntu:24.04) die
|
||||
2015-05-12T15:52:12.999999999Z07:00 4386fb97867d: (from ubuntu:24.04) stop
|
||||
2015-05-12T15:53:45.999999999Z07:00 7805c1d35632: (from redis:7.2) die
|
||||
2015-05-12T15:54:03.999999999Z07:00 7805c1d35632: (from redis:7.2) stop
|
||||
|
||||
@@ -97,21 +97,21 @@ Lines. For information about JSON Lines, refer to https://jsonlines.org .
|
||||
## Filters
|
||||
|
||||
$ docker events --filter 'event=stop'
|
||||
2014-05-10T17:42:14.999999999Z07:00 container stop 4386fb97867d (image=ubuntu:26.04)
|
||||
2014-05-10T17:42:14.999999999Z07:00 container stop 4386fb97867d (image=ubuntu:24.04)
|
||||
2014-09-03T17:42:14.999999999Z07:00 container stop 7805c1d35632 (image=redis:7.2)
|
||||
|
||||
$ docker events --filter 'image=ubuntu:26.04'
|
||||
2014-05-10T17:42:14.999999999Z07:00 container start 4386fb97867d (image=ubuntu:26.04)
|
||||
2014-05-10T17:42:14.999999999Z07:00 container die 4386fb97867d (image=ubuntu:26.04)
|
||||
2014-05-10T17:42:14.999999999Z07:00 container stop 4386fb97867d (image=ubuntu:26.04)
|
||||
$ docker events --filter 'image=ubuntu:24.04'
|
||||
2014-05-10T17:42:14.999999999Z07:00 container start 4386fb97867d (image=ubuntu:24.04)
|
||||
2014-05-10T17:42:14.999999999Z07:00 container die 4386fb97867d (image=ubuntu:24.04)
|
||||
2014-05-10T17:42:14.999999999Z07:00 container stop 4386fb97867d (image=ubuntu:24.04)
|
||||
|
||||
$ docker events --filter 'container=7805c1d35632'
|
||||
2014-05-10T17:42:14.999999999Z07:00 container die 7805c1d35632 (image=redis:7.2)
|
||||
2014-09-03T15:49:29.999999999Z07:00 container stop 7805c1d35632 (image= redis:7.2)
|
||||
|
||||
$ docker events --filter 'container=7805c1d35632' --filter 'container=4386fb97867d'
|
||||
2014-09-03T15:49:29.999999999Z07:00 container die 4386fb97867d (image=ubuntu:26.04)
|
||||
2014-05-10T17:42:14.999999999Z07:00 container stop 4386fb97867d (image=ubuntu:26.04)
|
||||
2014-09-03T15:49:29.999999999Z07:00 container die 4386fb97867d (image=ubuntu:24.04)
|
||||
2014-05-10T17:42:14.999999999Z07:00 container stop 4386fb97867d (image=ubuntu:24.04)
|
||||
2014-05-10T17:42:14.999999999Z07:00 container die 7805c1d35632 (image=redis:7.2)
|
||||
2014-09-03T15:49:29.999999999Z07:00 container stop 7805c1d35632 (image=redis:7.2)
|
||||
|
||||
|
||||
@@ -71,8 +71,8 @@ Server:
|
||||
seccomp
|
||||
Profile: builtin
|
||||
cgroupns
|
||||
Kernel Version: 6.8.0-31-generic
|
||||
Operating System: Ubuntu 24.04 LTS
|
||||
Kernel Version: 5.15.0-25-generic
|
||||
Operating System: Ubuntu 22.04 LTS
|
||||
OSType: linux
|
||||
Architecture: x86_64
|
||||
CPUs: 1
|
||||
|
||||
@@ -88,20 +88,13 @@ if [ "$CGO_ENABLED" = "1" ] && [ "$(go env GOOS)" != "windows" ]; then
|
||||
fi
|
||||
export GO_BUILDMODE
|
||||
|
||||
# Omit DWARF debug information to reduce binary size, but retain the symbol
|
||||
# table for tools that inspect Go binaries. Pass GO_LDFLAGS="-w=0" to retain DWARF.
|
||||
GO_LDFLAGS="-w ${GO_LDFLAGS:-}"
|
||||
|
||||
# Quotes are interpreted by Go's -ldflags parser, not the shell.
|
||||
# shellcheck disable=SC2089
|
||||
{
|
||||
GO_LDFLAGS="$GO_LDFLAGS -X \"github.com/docker/cli/cli/version.GitCommit=${GITCOMMIT}\""
|
||||
GO_LDFLAGS="$GO_LDFLAGS -X \"github.com/docker/cli/cli/version.BuildTime=${BUILDTIME}\""
|
||||
GO_LDFLAGS="$GO_LDFLAGS -X \"github.com/docker/cli/cli/version.Version=${VERSION}\""
|
||||
if test -n "${PLATFORM}"; then
|
||||
GO_LDFLAGS="$GO_LDFLAGS -X \"github.com/docker/cli/cli/version.PlatformName=${PLATFORM}\""
|
||||
fi
|
||||
}
|
||||
GO_LDFLAGS="${GO_LDFLAGS:-}"
|
||||
GO_LDFLAGS="$GO_LDFLAGS -X \"github.com/docker/cli/cli/version.GitCommit=${GITCOMMIT}\""
|
||||
GO_LDFLAGS="$GO_LDFLAGS -X \"github.com/docker/cli/cli/version.BuildTime=${BUILDTIME}\""
|
||||
GO_LDFLAGS="$GO_LDFLAGS -X \"github.com/docker/cli/cli/version.Version=${VERSION}\""
|
||||
if test -n "${PLATFORM}"; then
|
||||
GO_LDFLAGS="$GO_LDFLAGS -X \"github.com/docker/cli/cli/version.PlatformName=${PLATFORM}\""
|
||||
fi
|
||||
if [ "$CGO_ENABLED" = "1" ] && [ "$GO_LINKMODE" = "static" ] && [ "$(go env GOOS)" = "linux" ]; then
|
||||
GO_LDFLAGS="$GO_LDFLAGS -linkmode external -extldflags -static"
|
||||
fi
|
||||
@@ -135,7 +128,7 @@ if [ -n "$GO_STRIP" ]; then
|
||||
# /usr/local/go/pkg/tool/linux_amd64/link: /usr/local/go/pkg/tool/linux_amd64/link: running strip failed: exit status 1
|
||||
# llvm-strip: error: unsupported load command (cmd=0x5)
|
||||
# more info: https://github.com/docker/cli/pull/3717
|
||||
GO_LDFLAGS="$GO_LDFLAGS -s"
|
||||
GO_LDFLAGS="$GO_LDFLAGS -s -w"
|
||||
fi
|
||||
export GO_LDFLAGS="$GO_LDFLAGS" # https://github.com/koalaman/shellcheck/issues/2064
|
||||
|
||||
|
||||
+22
-23
@@ -21,7 +21,7 @@ require (
|
||||
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.12
|
||||
github.com/docker/docker-credential-helpers v0.9.9
|
||||
github.com/docker/go-connections v0.8.2
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/fvbommel/sortorder v1.2.1
|
||||
@@ -33,12 +33,12 @@ require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/mattn/go-runewidth v0.0.31
|
||||
github.com/moby/go-archive v0.3.3
|
||||
github.com/moby/moby/api v1.56.2-0.20261106011722-402d0a16f131
|
||||
github.com/moby/moby/client v0.6.3-0.20261106011722-402d0a16f131
|
||||
github.com/moby/moby/api v1.56.1
|
||||
github.com/moby/moby/client v0.6.2
|
||||
github.com/moby/patternmatcher v0.6.1
|
||||
github.com/moby/swarmkit/v2 v2.1.3
|
||||
github.com/moby/sys/atomicwriter v0.1.0
|
||||
github.com/moby/sys/capability v0.5.0
|
||||
github.com/moby/sys/capability v0.4.0
|
||||
github.com/moby/sys/sequential v0.7.0
|
||||
github.com/moby/sys/signal v0.7.1
|
||||
github.com/moby/sys/symlink v0.3.0
|
||||
@@ -52,25 +52,25 @@ require (
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.72.0
|
||||
go.opentelemetry.io/otel v1.47.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.47.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.47.0
|
||||
go.opentelemetry.io/otel/metric v1.47.0
|
||||
go.opentelemetry.io/otel/sdk v1.47.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.47.0
|
||||
go.opentelemetry.io/otel/trace v1.47.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0
|
||||
go.opentelemetry.io/otel v1.46.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.46.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0
|
||||
go.opentelemetry.io/otel/metric v1.46.0
|
||||
go.opentelemetry.io/otel/sdk v1.46.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.46.0
|
||||
go.opentelemetry.io/otel/trace v1.46.0
|
||||
go.yaml.in/yaml/v3 v3.0.6
|
||||
golang.org/x/sync v0.24.0
|
||||
golang.org/x/sys v0.49.1-0.20261022125458-f5cb0306ac0e // includes fix for https://go.dev/issue/82324
|
||||
golang.org/x/term v0.47.0
|
||||
golang.org/x/text v0.43.0
|
||||
golang.org/x/sync v0.23.0
|
||||
golang.org/x/sys v0.48.0
|
||||
golang.org/x/term v0.46.0
|
||||
golang.org/x/text v0.42.0
|
||||
gotest.tools/v3 v3.5.2
|
||||
tags.cncf.io/container-device-interface v1.1.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20261013165250-51075c6ea6bc // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20261006220739-8c912ac31dd4 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.3 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
@@ -99,14 +99,13 @@ require (
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // 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.47.0 // indirect
|
||||
go.opentelemetry.io/otel/log v1.47.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.11.0 // indirect
|
||||
golang.org/x/mod v0.42.0 // indirect
|
||||
golang.org/x/net v0.62.0 // indirect
|
||||
golang.org/x/mod v0.41.0 // indirect
|
||||
golang.org/x/net v0.60.0 // indirect
|
||||
golang.org/x/time v0.16.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260928230214-8a89bd6388cc // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260928230214-8a89bd6388cc // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 // indirect
|
||||
google.golang.org/grpc v1.83.2 // indirect
|
||||
google.golang.org/protobuf v1.36.12 // indirect
|
||||
)
|
||||
|
||||
+48
-48
@@ -2,8 +2,8 @@ dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20261013165250-51075c6ea6bc h1:58s5YzGPNAP7+e1uemaL0ax/NPSeJOHdRpRIowd+JDk=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20261013165250-51075c6ea6bc/go.mod h1:DMNF6Kxv+EcuUvVuNkvC3f+BjEEmc/Fn9aU+qiD8zpU=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20261006220739-8c912ac31dd4 h1:m2xcjRABtG288vnlV3U/PKh/HnPURxZp50RKNUwBxM8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20261006220739-8c912ac31dd4/go.mod h1:MTUE7ZuZO89FAtAwozGg6AWD83pcdefnEq5gF82MB8s=
|
||||
github.com/Microsoft/go-winio v0.6.3 h1:M/52tsoyTfwaxajLnXZm7PqkbMGRH8iSWP+8fcg9MQ4=
|
||||
github.com/Microsoft/go-winio v0.6.3/go.mod h1:ma0QpxizD4fyyzJ1C7MIblymTD2Nxz5Mho/3CepwIZg=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -35,8 +35,8 @@ 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.12 h1:hAgjJcMuC17DPf0Kl/HQSZQ7cxVo+wEA8Oo1SzkmqFg=
|
||||
github.com/docker/docker-credential-helpers v0.9.12/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||
github.com/docker/docker-credential-helpers v0.9.9 h1:BkydjIgZ46JnDbqyM2p2fc63KMw6y+KHL3Em/2AGJ7w=
|
||||
github.com/docker/docker-credential-helpers v0.9.9/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||
github.com/docker/go-connections v0.8.2 h1:9d9DEokx5W7qlr0shkFXIxEA7+TTD+llwzcykSlxMNc=
|
||||
github.com/docker/go-connections v0.8.2/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
|
||||
github.com/docker/go-events v0.1.0 h1:J8VX4H7Ta8mmBhv/K/24x1kosKOrEEnYmGjcdFHTOW0=
|
||||
@@ -88,18 +88,18 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/go-archive v0.3.3 h1:OxxR9paxsluYi+zDUEXTTaIxtkK3viymW+Ka7vRhhME=
|
||||
github.com/moby/go-archive v0.3.3/go.mod h1:Npdv43fFqlhZW7Xo8fbm3ZMYFvAGNviUPqX21VERbcE=
|
||||
github.com/moby/moby/api v1.56.2-0.20261106011722-402d0a16f131 h1:tPz9ahj8F7AjQw62XQFP65kmoxF3rXTYSit3q06/yQc=
|
||||
github.com/moby/moby/api v1.56.2-0.20261106011722-402d0a16f131/go.mod h1:pRZ1QRtx6AVhdckeFYo4DUX4ASNF0SCllhyY6aj2mgg=
|
||||
github.com/moby/moby/client v0.6.3-0.20261106011722-402d0a16f131 h1:ReIO7lalQVE4BetZuIvrxScg8Rxv4Sk74XRs06ryRDU=
|
||||
github.com/moby/moby/client v0.6.3-0.20261106011722-402d0a16f131/go.mod h1:dPyRteJDuLdP8to3kWmNAU/tbMwSFCE88yKHvY/nZ9k=
|
||||
github.com/moby/moby/api v1.56.1 h1:PpWkvVPB7Fr/No8w+TfyJ/I6rWZ2YPZhT3JorIg+06c=
|
||||
github.com/moby/moby/api v1.56.1/go.mod h1:sZ+THbVWkjOmBPPfbnzdD/G1LuIexWhqlSHHPTDQ1Uk=
|
||||
github.com/moby/moby/client v0.6.2 h1:ekSPlMccrZGcw6Scb8T9D39qE0mB+XupU/yS0zNw9N4=
|
||||
github.com/moby/moby/client v0.6.2/go.mod h1:DGcnO7qmPJrqobx/ggQgQ/AUk+2wgVdQv7uxB0/mEsc=
|
||||
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.3 h1:UcwtwkIRjr75qSgUWCTIuzKUzAXlidxsiqVMnyxSF1w=
|
||||
github.com/moby/swarmkit/v2 v2.1.3/go.mod h1:KhkxT93Hm4wHoNUPJ4JRD384pS7OODFGzJWoDRj9bLE=
|
||||
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.5.0 h1:Bm4oD9xGCtZguPnaoQy1/jvKTSwZHMvM0FrnxNYtGYU=
|
||||
github.com/moby/sys/capability v0.5.0/go.mod h1:i+0cXvNSriGrL3C6sxnmlmGL5HVU0OdxocizXdC8A+g=
|
||||
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
|
||||
github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
|
||||
github.com/moby/sys/mount v0.3.5 h1:eS3fsZTjHaBihwjp4/+5Z3jxqLXYsbwxqpVSfFv3M00=
|
||||
github.com/moby/sys/mount v0.3.5/go.mod h1:WUQDO+/uCiCIkIztx8SrwIDVn2dtMFRBebRhpDFT71M=
|
||||
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
|
||||
@@ -155,28 +155,26 @@ 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.72.0 h1:LxwW/9ctSCv+QkE/cLR7M91ZIkXNMqJtEMi1vCw9U8s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.72.0/go.mod h1:tOsftB4SslBwwErVEPaenU2RpThXWPIU8DoJHEC4dyw=
|
||||
go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM=
|
||||
go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.47.0 h1:LaLVGJtIzHcvHjVs2OftmKcc+M9mdOqS8vZ1fBD1Yes=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.47.0/go.mod h1:vF6H5sNDuQBJWgSX00iZgbIqk2MZZoVMH0ar5t5bOpw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0 h1:julhjPeUH/q/7hinbSdDdqt5h7Zw9YWmRlWRhI0jd54=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0/go.mod h1:Ao2mz688LH/tFf0yMAenidq6k2YNSx6SIY2q6jDACck=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.47.0 h1:UFpxOpYPmMNUtOWhdb+nC1WELIgVf8z9higURrbzYU4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.47.0/go.mod h1:YBGjxe3lt0jtXoEXxGrGhv/jM7oUBUDVK7zhAvNyEjc=
|
||||
go.opentelemetry.io/otel/log v1.47.0 h1:cOTS1CcLbSQeZKanGJ+0JpF/+t4PELi3O3bbl2lqCcI=
|
||||
go.opentelemetry.io/otel/log v1.47.0/go.mod h1:9byitSQ5pLC6PpqwGXjqdMKya6ZTswHRZh2vvXT33nw=
|
||||
go.opentelemetry.io/otel/metric v1.47.0 h1:4PptaldXx3Eat1XjMZ68pPJEs5wrhlemctZE9a3UdWY=
|
||||
go.opentelemetry.io/otel/metric v1.47.0/go.mod h1:ADGSXxRrXM6bjbvLo535EstVFlPpPYZm4LBKixjDHwU=
|
||||
go.opentelemetry.io/otel/metric/x v0.69.0 h1:DjRLr15H83v+hCW7JA9NoJvOkYTtmq5YoDRbe9deYpM=
|
||||
go.opentelemetry.io/otel/metric/x v0.69.0/go.mod h1:uVvsMPMFFyj/HUQfrUnH3JjnOQ1dwFDorgFLRBasM0k=
|
||||
go.opentelemetry.io/otel/sdk v1.47.0 h1:zWXEr4j2lFefG87TU6Yg8a7ngfohIKFZHKp0Hf5hC6I=
|
||||
go.opentelemetry.io/otel/sdk v1.47.0/go.mod h1:VUc24kiOeoGsxG8G9ULx3fWKvB7jMhnGE8Oi607lgR0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.47.0 h1:lfISg2j93VT6yqdk9OfUaZmw/GfcZqCCV3jdXtsPnKw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.47.0/go.mod h1:ypLp+mW1Nt2x+Szt3b5/i1syodyts49lMOwxpDI3VGw=
|
||||
go.opentelemetry.io/otel/trace v1.47.0 h1:JOjX/Oci8K94QHddo+bbfya/Ai/nf6/dt9ZfrFNWSrM=
|
||||
go.opentelemetry.io/otel/trace v1.47.0/go.mod h1:jNaSLa2PZEYFG6fRjJABAu+bw4FS08uDmPg28lTghu0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 h1:3g7B90UzBltIDKq1/5mrTGxTnOFDV0ICOhLoxiZ8jlg=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0/go.mod h1:Ef8SuTh59BT7+ofpDxN9z+yOlc4t2GjLmKDgYNJL/NU=
|
||||
go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc=
|
||||
go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.46.0 h1:qkDYCAFiZXLcs1L4aY+tP2wguQ4kURANqHOQMA2et2s=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.46.0/go.mod h1:tkipS4DRzmpAmvg+Gw4++O1IdDq6TVDnvnYU6cmbQVs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0 h1:OFnwLJr+pF3iHrlGSzbxyuo6/6HyBlnlN1CWEJmBVcw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0/go.mod h1:716wFneO0ov19A2beH5hjfh9AK5z/VWNAtDijp1Y0/g=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0 h1:w53CDeOA/Kurp7yRsegSr6pbbr759dOvJ+yNmWM6Hxs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0/go.mod h1:BOmGMCbAtvcJiSJ+hLuhgPLdDbimnraSl8irz3iY8sY=
|
||||
go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8=
|
||||
go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o=
|
||||
go.opentelemetry.io/otel/metric/x v0.68.0 h1:TA/cBT23D3MnxYPwHL7YFOdYGdx0A0v+s7Mzotpd1dU=
|
||||
go.opentelemetry.io/otel/metric/x v0.68.0/go.mod h1:agudOmvWhwUTjgibWDzxD2PoWYnpw5Ht5jISYOD2Hd4=
|
||||
go.opentelemetry.io/otel/sdk v1.46.0 h1:h5CNQQjEbuQXY/JfZtgt3i7HVFV3aHPO2OAwO2eTYPI=
|
||||
go.opentelemetry.io/otel/sdk v1.46.0/go.mod h1:GAERFXFt5SYCEB+YiKUbMBeza6UaDH7GmGOZEfh2gSM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.46.0 h1:0piZ26EG4RBfebb2jhDH6ERCYHoVWduc3kLgPCwSnSE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.46.0/go.mod h1:I1PbKrdVc8Qu8HYVDNtqVIwLwjNrhsV/uFuxfwg8mO4=
|
||||
go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c=
|
||||
go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI=
|
||||
go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk=
|
||||
go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
@@ -189,31 +187,31 @@ 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.42.0 h1:ICeL3t2L30ljSPHHxDnR4fqVcBt/zR2zJAb9juavmQg=
|
||||
golang.org/x/mod v0.42.0/go.mod h1:u5fzIrRInkL8MaO1cbRkUHT64v303YIT5KEqpNr7jRs=
|
||||
golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c=
|
||||
golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
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.62.0 h1:18ELalfM5+XNRwzM2nYqXsZQ9ByS1uhj2O/U7+5jqRY=
|
||||
golang.org/x/net v0.62.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI=
|
||||
golang.org/x/net v0.60.0 h1:79p50tfZlm0J9YfoDsSi639qSXNGVwEzOPLCxM2FsYU=
|
||||
golang.org/x/net v0.60.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA=
|
||||
golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
|
||||
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.49.1-0.20261022125458-f5cb0306ac0e h1:y3PdhEKVIFYc4c6gNZPEC7qPIoa9uH/2zIQEc0mw4R4=
|
||||
golang.org/x/sys v0.49.1-0.20261022125458-f5cb0306ac0e/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
golang.org/x/term v0.47.0 h1:cpyOwQkr2Nzxe2p6KnrTG7Ei2wtzjsuSjhpXXepZ/BM=
|
||||
golang.org/x/term v0.47.0/go.mod h1:efAfAuG6RKABvg1ArGebRkvX6LXul14SM67o2e4mUCY=
|
||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE=
|
||||
golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc=
|
||||
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.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc=
|
||||
golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s=
|
||||
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
|
||||
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
|
||||
golang.org/x/time v0.16.0 h1:vMb6ptszcQMkcwiRTAuNNU50gom6++Q/6gY2hDM6VDE=
|
||||
golang.org/x/time v0.16.0/go.mod h1:rVKOqvZeKvrDKTQiAHJ7wmwP0RzleSphoEA9RcdLA0s=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -226,10 +224,10 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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-20260928230214-8a89bd6388cc h1:rZHRz0ogq4Pid1IKtA/ivHi8XONVF5tqUQXA2TtgpkE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260928230214-8a89bd6388cc/go.mod h1:K/n0XLazCJHjD73YuMyNJsN38BPPy1kfFnKxUXaThFU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260928230214-8a89bd6388cc h1:4bNTbnb44EqGVy9HaQxSY2jnafSUdgV3qHtedvNpDKg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260928230214-8a89bd6388cc/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5 h1:izFU9hz7aeLI/Mi1J0991ae+xcwRLr7hTqWnB/9aIIU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5/go.mod h1:3LhxRw4YYkf+ylAfgaY9JlVLFKhokkCV8duhLLe7+t0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 h1:1VUiZAXyC+zmiFYi+WLtBzr68Cj8wOofHjjrA/kkizc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA=
|
||||
google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU=
|
||||
google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8=
|
||||
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
|
||||
@@ -237,5 +235,7 @@ google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
|
||||
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||
tags.cncf.io/container-device-interface v1.1.1 h1:YPwQz4xg8PlQ0yT/baR0BtLpTQROe4l6M1yuRgAu1vc=
|
||||
tags.cncf.io/container-device-interface v1.1.1/go.mod h1:S1PSJWYPD4Iom0/39mvr/VVFCfG0Yt14j20d0OYrY2M=
|
||||
|
||||
+5
-7
@@ -1,7 +1,5 @@
|
||||
package ansiterm
|
||||
|
||||
import "slices"
|
||||
|
||||
type csiEntryState struct {
|
||||
baseState
|
||||
}
|
||||
@@ -15,11 +13,11 @@ func (csiState csiEntryState) Handle(b byte) (s state, e error) {
|
||||
}
|
||||
|
||||
switch {
|
||||
case slices.Contains(alphabetics, b):
|
||||
case sliceContains(alphabetics, b):
|
||||
return csiState.parser.ground, nil
|
||||
case slices.Contains(csiCollectables, b):
|
||||
case sliceContains(csiCollectables, b):
|
||||
return csiState.parser.csiParam, nil
|
||||
case slices.Contains(executors, b):
|
||||
case sliceContains(executors, b):
|
||||
return csiState, csiState.parser.execute()
|
||||
}
|
||||
|
||||
@@ -35,9 +33,9 @@ func (csiState csiEntryState) Transition(s state) error {
|
||||
return csiState.parser.csiDispatch()
|
||||
case csiState.parser.csiParam:
|
||||
switch {
|
||||
case slices.Contains(csiParams, csiState.parser.context.currentChar):
|
||||
case sliceContains(csiParams, csiState.parser.context.currentChar):
|
||||
_ = csiState.parser.collectParam()
|
||||
case slices.Contains(intermeds, csiState.parser.context.currentChar):
|
||||
case sliceContains(intermeds, csiState.parser.context.currentChar):
|
||||
_ = csiState.parser.collectInter()
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -1,7 +1,5 @@
|
||||
package ansiterm
|
||||
|
||||
import "slices"
|
||||
|
||||
type csiParamState struct {
|
||||
baseState
|
||||
}
|
||||
@@ -15,11 +13,11 @@ func (csiState csiParamState) Handle(b byte) (s state, e error) {
|
||||
}
|
||||
|
||||
switch {
|
||||
case slices.Contains(alphabetics, b):
|
||||
case sliceContains(alphabetics, b):
|
||||
return csiState.parser.ground, nil
|
||||
case slices.Contains(csiCollectables, b):
|
||||
case sliceContains(csiCollectables, b):
|
||||
return csiState, csiState.parser.collectParam()
|
||||
case slices.Contains(executors, b):
|
||||
case sliceContains(executors, b):
|
||||
return csiState, csiState.parser.execute()
|
||||
}
|
||||
|
||||
|
||||
+3
-5
@@ -1,7 +1,5 @@
|
||||
package ansiterm
|
||||
|
||||
import "slices"
|
||||
|
||||
type escapeIntermediateState struct {
|
||||
baseState
|
||||
}
|
||||
@@ -14,11 +12,11 @@ func (escState escapeIntermediateState) Handle(b byte) (s state, e error) {
|
||||
}
|
||||
|
||||
switch {
|
||||
case slices.Contains(intermeds, b):
|
||||
case sliceContains(intermeds, b):
|
||||
return escState, escState.parser.collectInter()
|
||||
case slices.Contains(executors, b):
|
||||
case sliceContains(executors, b):
|
||||
return escState, escState.parser.execute()
|
||||
case slices.Contains(escapeIntermediateToGroundBytes, b):
|
||||
case sliceContains(escapeIntermediateToGroundBytes, b):
|
||||
return escState.parser.ground, nil
|
||||
}
|
||||
|
||||
|
||||
+3
-5
@@ -1,7 +1,5 @@
|
||||
package ansiterm
|
||||
|
||||
import "slices"
|
||||
|
||||
type escapeState struct {
|
||||
baseState
|
||||
}
|
||||
@@ -18,11 +16,11 @@ func (escState escapeState) Handle(b byte) (s state, e error) {
|
||||
return escState.parser.csiEntry, nil
|
||||
case b == ANSI_OSC_STRING_ENTRY:
|
||||
return escState.parser.oscString, nil
|
||||
case slices.Contains(executors, b):
|
||||
case sliceContains(executors, b):
|
||||
return escState, escState.parser.execute()
|
||||
case slices.Contains(escapeToGroundBytes, b):
|
||||
case sliceContains(escapeToGroundBytes, b):
|
||||
return escState.parser.ground, nil
|
||||
case slices.Contains(intermeds, b):
|
||||
case sliceContains(intermeds, b):
|
||||
return escState.parser.escapeIntermediate, nil
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -1,7 +1,5 @@
|
||||
package ansiterm
|
||||
|
||||
import "slices"
|
||||
|
||||
type groundState struct {
|
||||
baseState
|
||||
}
|
||||
@@ -15,10 +13,10 @@ func (gs groundState) Handle(b byte) (s state, e error) {
|
||||
}
|
||||
|
||||
switch {
|
||||
case slices.Contains(printables, b):
|
||||
case sliceContains(printables, b):
|
||||
return gs, gs.parser.print()
|
||||
|
||||
case slices.Contains(executors, b):
|
||||
case sliceContains(executors, b):
|
||||
return gs, gs.parser.execute()
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -20,12 +20,12 @@ type AnsiParser struct {
|
||||
oscString state
|
||||
stateMap []state
|
||||
|
||||
logf func(string, ...any)
|
||||
logf func(string, ...interface{})
|
||||
}
|
||||
|
||||
type Option func(*AnsiParser)
|
||||
|
||||
func WithLogf(f func(string, ...any)) Option {
|
||||
func WithLogf(f func(string, ...interface{})) Option {
|
||||
return func(ap *AnsiParser) {
|
||||
ap.logf = f
|
||||
}
|
||||
@@ -45,7 +45,7 @@ func CreateParser(initialState string, evtHandler AnsiEventHandler, opts ...Opti
|
||||
logger := log.New(logFile, "", log.LstdFlags)
|
||||
if ap.logf != nil {
|
||||
l := ap.logf
|
||||
ap.logf = func(s string, v ...any) {
|
||||
ap.logf = func(s string, v ...interface{}) {
|
||||
l(s, v...)
|
||||
logger.Printf(s, v...)
|
||||
}
|
||||
@@ -55,7 +55,7 @@ func CreateParser(initialState string, evtHandler AnsiEventHandler, opts ...Opti
|
||||
}
|
||||
|
||||
if ap.logf == nil {
|
||||
ap.logf = func(string, ...any) {}
|
||||
ap.logf = func(string, ...interface{}) {}
|
||||
}
|
||||
|
||||
ap.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: ap}}
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ func getInts(params []string, minCount int, dflt int) []int {
|
||||
|
||||
if len(ints) < minCount {
|
||||
remaining := minCount - len(ints)
|
||||
for range remaining {
|
||||
for i := 0; i < remaining; i++ {
|
||||
ints = append(ints, dflt)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-4
@@ -1,7 +1,5 @@
|
||||
package ansiterm
|
||||
|
||||
import "slices"
|
||||
|
||||
type state interface {
|
||||
Enter() error
|
||||
Exit() error
|
||||
@@ -34,7 +32,7 @@ func (base baseState) Handle(b byte) (s state, e error) {
|
||||
return base.parser.escape, nil
|
||||
case b == OSC_STRING:
|
||||
return base.parser.oscString, nil
|
||||
case slices.Contains(toGroundBytes, b):
|
||||
case sliceContains(toGroundBytes, b):
|
||||
return base.parser.ground, nil
|
||||
}
|
||||
|
||||
@@ -54,7 +52,7 @@ func (base baseState) Transition(s state) error {
|
||||
execBytes = append(execBytes, 0x99)
|
||||
execBytes = append(execBytes, 0x9A)
|
||||
|
||||
if slices.Contains(execBytes, base.parser.context.currentChar) {
|
||||
if sliceContains(execBytes, base.parser.context.currentChar) {
|
||||
return base.parser.execute()
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package ansiterm
|
||||
|
||||
func sliceContains(bytes []byte, b byte) bool {
|
||||
for _, v := range bytes {
|
||||
if v == b {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
|
||||
+90
-96
@@ -1,17 +1,14 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// ===========================================================================================================
|
||||
//===========================================================================================================
|
||||
// IMPORTANT NOTE:
|
||||
//
|
||||
// The methods below make extensive use of the "unsafe" package to obtain the required pointers.
|
||||
@@ -29,50 +26,51 @@ import (
|
||||
// -- The value is not referenced by the method after passing the pointer to Windows
|
||||
//
|
||||
// See http://golang.org/doc/go1.3.
|
||||
// ===========================================================================================================
|
||||
//===========================================================================================================
|
||||
|
||||
var (
|
||||
kernel32DLL = windows.NewLazyDLL("kernel32.dll")
|
||||
kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
getConsoleCursorInfoProc = kernel32DLL.NewProc("GetConsoleCursorInfo")
|
||||
setConsoleCursorInfoProc = kernel32DLL.NewProc("SetConsoleCursorInfo")
|
||||
setConsoleCursorPositionProc = kernel32DLL.NewProc("SetConsoleCursorPosition")
|
||||
setConsoleModeProc = kernel32DLL.NewProc("SetConsoleMode")
|
||||
getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
|
||||
setConsoleScreenBufferSizeProc = kernel32DLL.NewProc("SetConsoleScreenBufferSize")
|
||||
scrollConsoleScreenBufferProc = kernel32DLL.NewProc("ScrollConsoleScreenBufferA")
|
||||
setConsoleTextAttributeProc = kernel32DLL.NewProc("SetConsoleTextAttribute")
|
||||
setConsoleWindowInfoProc = kernel32DLL.NewProc("SetConsoleWindowInfo")
|
||||
writeConsoleOutputProc = kernel32DLL.NewProc("WriteConsoleOutputW")
|
||||
readConsoleInputProc = kernel32DLL.NewProc("ReadConsoleInputW")
|
||||
waitForSingleObjectProc = kernel32DLL.NewProc("WaitForSingleObject")
|
||||
)
|
||||
|
||||
// Console modes
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
|
||||
// Windows Console constants
|
||||
const (
|
||||
ENABLE_PROCESSED_INPUT = windows.ENABLE_PROCESSED_INPUT
|
||||
ENABLE_LINE_INPUT = windows.ENABLE_LINE_INPUT
|
||||
ENABLE_ECHO_INPUT = windows.ENABLE_ECHO_INPUT
|
||||
ENABLE_WINDOW_INPUT = windows.ENABLE_WINDOW_INPUT
|
||||
ENABLE_MOUSE_INPUT = windows.ENABLE_MOUSE_INPUT
|
||||
ENABLE_INSERT_MODE = windows.ENABLE_INSERT_MODE
|
||||
ENABLE_QUICK_EDIT_MODE = windows.ENABLE_QUICK_EDIT_MODE
|
||||
ENABLE_EXTENDED_FLAGS = windows.ENABLE_EXTENDED_FLAGS
|
||||
ENABLE_AUTO_POSITION = windows.ENABLE_AUTO_POSITION
|
||||
ENABLE_VIRTUAL_TERMINAL_INPUT = windows.ENABLE_VIRTUAL_TERMINAL_INPUT
|
||||
// Console modes
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
|
||||
ENABLE_PROCESSED_INPUT = 0x0001
|
||||
ENABLE_LINE_INPUT = 0x0002
|
||||
ENABLE_ECHO_INPUT = 0x0004
|
||||
ENABLE_WINDOW_INPUT = 0x0008
|
||||
ENABLE_MOUSE_INPUT = 0x0010
|
||||
ENABLE_INSERT_MODE = 0x0020
|
||||
ENABLE_QUICK_EDIT_MODE = 0x0040
|
||||
ENABLE_EXTENDED_FLAGS = 0x0080
|
||||
ENABLE_AUTO_POSITION = 0x0100
|
||||
ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200
|
||||
|
||||
ENABLE_PROCESSED_OUTPUT = windows.ENABLE_PROCESSED_OUTPUT
|
||||
ENABLE_WRAP_AT_EOL_OUTPUT = windows.ENABLE_WRAP_AT_EOL_OUTPUT
|
||||
ENABLE_VIRTUAL_TERMINAL_PROCESSING = windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
DISABLE_NEWLINE_AUTO_RETURN = windows.DISABLE_NEWLINE_AUTO_RETURN
|
||||
ENABLE_LVB_GRID_WORLDWIDE = windows.ENABLE_LVB_GRID_WORLDWIDE
|
||||
)
|
||||
ENABLE_PROCESSED_OUTPUT = 0x0001
|
||||
ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
|
||||
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
|
||||
DISABLE_NEWLINE_AUTO_RETURN = 0x0008
|
||||
ENABLE_LVB_GRID_WORLDWIDE = 0x0010
|
||||
|
||||
// Character attributes
|
||||
//
|
||||
// Foreground and background attributes can be combined to produce colors.
|
||||
// Clearing all foreground or background color bits produces black; setting
|
||||
// all color bits produces white.
|
||||
//
|
||||
// See https://learn.microsoft.com/windows/console/char-info-str.
|
||||
const (
|
||||
// Character attributes
|
||||
// Note:
|
||||
// -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan).
|
||||
// Clearing all foreground or background colors results in black; setting all creates white.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes.
|
||||
FOREGROUND_BLUE uint16 = 0x0001
|
||||
FOREGROUND_GREEN uint16 = 0x0002
|
||||
FOREGROUND_RED uint16 = 0x0004
|
||||
@@ -88,29 +86,23 @@ const (
|
||||
COMMON_LVB_MASK uint16 = 0xFF00
|
||||
COMMON_LVB_REVERSE_VIDEO uint16 = 0x4000
|
||||
COMMON_LVB_UNDERSCORE uint16 = 0x8000
|
||||
)
|
||||
|
||||
// Input event types
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
|
||||
const (
|
||||
KEY_EVENT = windows.KEY_EVENT
|
||||
MOUSE_EVENT = windows.MOUSE_EVENT
|
||||
WINDOW_BUFFER_SIZE_EVENT = windows.WINDOW_BUFFER_SIZE_EVENT
|
||||
MENU_EVENT = windows.MENU_EVENT
|
||||
FOCUS_EVENT = windows.FOCUS_EVENT
|
||||
)
|
||||
// Input event types
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
|
||||
KEY_EVENT = 0x0001
|
||||
MOUSE_EVENT = 0x0002
|
||||
WINDOW_BUFFER_SIZE_EVENT = 0x0004
|
||||
MENU_EVENT = 0x0008
|
||||
FOCUS_EVENT = 0x0010
|
||||
|
||||
// WaitForSingleObject return codes.
|
||||
const (
|
||||
WAIT_ABANDONED = windows.WAIT_ABANDONED
|
||||
WAIT_FAILED = windows.WAIT_FAILED
|
||||
WAIT_SIGNALED = windows.WAIT_OBJECT_0
|
||||
WAIT_TIMEOUT = 0x00000102 // Keep as untyped int for backward compat; windows.WAIT_TIMEOUT is typed as syscall.Errno.
|
||||
)
|
||||
// WaitForSingleObject return codes
|
||||
WAIT_ABANDONED = 0x00000080
|
||||
WAIT_FAILED = 0xFFFFFFFF
|
||||
WAIT_SIGNALED = 0x0000000
|
||||
WAIT_TIMEOUT = 0x00000102
|
||||
|
||||
// WaitForSingleObject wait durations.
|
||||
const (
|
||||
WAIT_INFINITE = windows.INFINITE
|
||||
// WaitForSingleObject wait duration
|
||||
WAIT_INFINITE = 0xFFFFFFFF
|
||||
WAIT_ONE_SECOND = 1000
|
||||
WAIT_HALF_SECOND = 500
|
||||
WAIT_QUARTER_SECOND = 250
|
||||
@@ -130,8 +122,6 @@ type (
|
||||
Visible int32
|
||||
}
|
||||
|
||||
// CONSOLE_SCREEN_BUFFER_INFO is identical to [windows.ConsoleScreenBufferInfo]
|
||||
// but implements [fmt.Stringer].
|
||||
CONSOLE_SCREEN_BUFFER_INFO struct {
|
||||
Size COORD
|
||||
CursorPosition COORD
|
||||
@@ -140,11 +130,17 @@ type (
|
||||
MaximumWindowSize COORD
|
||||
}
|
||||
|
||||
// COORD is identical to [windows.Coord] but implements [fmt.Stringer].
|
||||
COORD windows.Coord
|
||||
COORD struct {
|
||||
X int16
|
||||
Y int16
|
||||
}
|
||||
|
||||
// SMALL_RECT is identical to [windows.SmallRect] but implements [fmt.Stringer].
|
||||
SMALL_RECT windows.SmallRect
|
||||
SMALL_RECT struct {
|
||||
Left int16
|
||||
Top int16
|
||||
Right int16
|
||||
Bottom int16
|
||||
}
|
||||
|
||||
// INPUT_RECORD is a C/C++ union of which KEY_EVENT_RECORD is one case, it is also the largest
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
|
||||
@@ -193,45 +189,43 @@ func SetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error
|
||||
// SetConsoleCursorPosition location of the console cursor.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx.
|
||||
func SetConsoleCursorPosition(handle uintptr, coord COORD) error {
|
||||
return windows.SetConsoleCursorPosition(windows.Handle(handle), windows.Coord(coord))
|
||||
r1, r2, err := setConsoleCursorPositionProc.Call(handle, coordToPointer(coord))
|
||||
use(coord)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// GetConsoleMode gets the console mode for the given handle.
|
||||
// GetConsoleMode gets the console mode for given file descriptor
|
||||
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx.
|
||||
func GetConsoleMode(handle uintptr) (mode uint32, err error) {
|
||||
err = windows.GetConsoleMode(windows.Handle(handle), &mode)
|
||||
err = syscall.GetConsoleMode(syscall.Handle(handle), &mode)
|
||||
return mode, err
|
||||
}
|
||||
|
||||
// SetConsoleMode sets the console mode for the given handle.
|
||||
// SetConsoleMode sets the console mode for given file descriptor
|
||||
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx.
|
||||
func SetConsoleMode(handle uintptr, mode uint32) error {
|
||||
return windows.SetConsoleMode(windows.Handle(handle), mode)
|
||||
r1, r2, err := setConsoleModeProc.Call(handle, uintptr(mode), 0)
|
||||
use(mode)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
// GetConsoleScreenBufferInfo retrieves information about the specified console screen buffer.
|
||||
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx.
|
||||
func GetConsoleScreenBufferInfo(handle uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) {
|
||||
var info windows.ConsoleScreenBufferInfo
|
||||
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(handle), &info); err != nil {
|
||||
info := CONSOLE_SCREEN_BUFFER_INFO{}
|
||||
err := checkError(getConsoleScreenBufferInfoProc.Call(handle, uintptr(unsafe.Pointer(&info)), 0))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CONSOLE_SCREEN_BUFFER_INFO{
|
||||
Size: COORD(info.Size),
|
||||
CursorPosition: COORD(info.CursorPosition),
|
||||
Attributes: info.Attributes,
|
||||
Window: SMALL_RECT(info.Window),
|
||||
MaximumWindowSize: COORD(info.MaximumWindowSize),
|
||||
}, nil
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func ScrollConsoleScreenBuffer(handle uintptr, scrollRect SMALL_RECT, clipRect SMALL_RECT, destOrigin COORD, char CHAR_INFO) error {
|
||||
r1, r2, err := scrollConsoleScreenBufferProc.Call(handle, uintptr(unsafe.Pointer(&scrollRect)), uintptr(unsafe.Pointer(&clipRect)), coordToPointer(destOrigin), uintptr(unsafe.Pointer(&char)))
|
||||
runtime.KeepAlive(scrollRect)
|
||||
runtime.KeepAlive(clipRect)
|
||||
runtime.KeepAlive(destOrigin)
|
||||
runtime.KeepAlive(char)
|
||||
use(scrollRect)
|
||||
use(clipRect)
|
||||
use(destOrigin)
|
||||
use(char)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
@@ -239,7 +233,7 @@ func ScrollConsoleScreenBuffer(handle uintptr, scrollRect SMALL_RECT, clipRect S
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686044(v=vs.85).aspx.
|
||||
func SetConsoleScreenBufferSize(handle uintptr, coord COORD) error {
|
||||
r1, r2, err := setConsoleScreenBufferSizeProc.Call(handle, coordToPointer(coord))
|
||||
runtime.KeepAlive(coord)
|
||||
use(coord)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
@@ -248,7 +242,7 @@ func SetConsoleScreenBufferSize(handle uintptr, coord COORD) error {
|
||||
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx.
|
||||
func SetConsoleTextAttribute(handle uintptr, attribute uint16) error {
|
||||
r1, r2, err := setConsoleTextAttributeProc.Call(handle, uintptr(attribute), 0)
|
||||
runtime.KeepAlive(attribute)
|
||||
use(attribute)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
@@ -257,8 +251,8 @@ func SetConsoleTextAttribute(handle uintptr, attribute uint16) error {
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686125(v=vs.85).aspx.
|
||||
func SetConsoleWindowInfo(handle uintptr, isAbsolute bool, rect SMALL_RECT) error {
|
||||
r1, r2, err := setConsoleWindowInfoProc.Call(handle, uintptr(boolToBOOL(isAbsolute)), uintptr(unsafe.Pointer(&rect)))
|
||||
runtime.KeepAlive(isAbsolute)
|
||||
runtime.KeepAlive(rect)
|
||||
use(isAbsolute)
|
||||
use(rect)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
@@ -266,9 +260,9 @@ func SetConsoleWindowInfo(handle uintptr, isAbsolute bool, rect SMALL_RECT) erro
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687404(v=vs.85).aspx.
|
||||
func WriteConsoleOutput(handle uintptr, buffer []CHAR_INFO, bufferSize COORD, bufferCoord COORD, writeRegion *SMALL_RECT) error {
|
||||
r1, r2, err := writeConsoleOutputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), coordToPointer(bufferSize), coordToPointer(bufferCoord), uintptr(unsafe.Pointer(writeRegion)))
|
||||
runtime.KeepAlive(buffer)
|
||||
runtime.KeepAlive(bufferSize)
|
||||
runtime.KeepAlive(bufferCoord)
|
||||
use(buffer)
|
||||
use(bufferSize)
|
||||
use(bufferCoord)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
@@ -276,7 +270,7 @@ func WriteConsoleOutput(handle uintptr, buffer []CHAR_INFO, bufferSize COORD, bu
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx.
|
||||
func ReadConsoleInput(handle uintptr, buffer []INPUT_RECORD, count *uint32) error {
|
||||
r1, r2, err := readConsoleInputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)), uintptr(unsafe.Pointer(count)))
|
||||
runtime.KeepAlive(buffer)
|
||||
use(buffer)
|
||||
return checkError(r1, r2, err)
|
||||
}
|
||||
|
||||
@@ -284,19 +278,15 @@ func ReadConsoleInput(handle uintptr, buffer []INPUT_RECORD, count *uint32) erro
|
||||
// It returns true if the handle was signaled; false otherwise.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx.
|
||||
func WaitForSingleObject(handle uintptr, msWait uint32) (bool, error) {
|
||||
event, err := windows.WaitForSingleObject(windows.Handle(handle), msWait)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
switch event {
|
||||
case windows.WAIT_ABANDONED, uint32(windows.WAIT_TIMEOUT):
|
||||
r1, _, err := waitForSingleObjectProc.Call(handle, uintptr(uint32(msWait)))
|
||||
switch r1 {
|
||||
case WAIT_ABANDONED, WAIT_TIMEOUT:
|
||||
return false, nil
|
||||
case windows.WAIT_OBJECT_0:
|
||||
case WAIT_SIGNALED:
|
||||
return true, nil
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
use(msWait)
|
||||
return false, err
|
||||
}
|
||||
|
||||
// String helpers
|
||||
@@ -331,3 +321,7 @@ func coordToPointer(c COORD) uintptr {
|
||||
// Note: This code assumes the two SHORTs are correctly laid out; the "cast" to uint32 is just to get a pointer to pass.
|
||||
return uintptr(*((*uint32)(unsafe.Pointer(&c))))
|
||||
}
|
||||
|
||||
// use is a no-op, but the compiler cannot see that it is.
|
||||
// Calling use(p) ensures that p is kept live until that point.
|
||||
func use(p interface{}) {}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
|
||||
+33
-11
@@ -1,12 +1,10 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
const (
|
||||
horizontal = iota
|
||||
vertical
|
||||
line
|
||||
column
|
||||
)
|
||||
|
||||
func (h *windowsAnsiEventHandler) getCursorWindow(info *CONSOLE_SCREEN_BUFFER_INFO) SMALL_RECT {
|
||||
@@ -60,20 +58,44 @@ func (h *windowsAnsiEventHandler) moveCursor(moveMode int, param int) error {
|
||||
position.X += int16(param)
|
||||
case vertical:
|
||||
position.Y += int16(param)
|
||||
case line:
|
||||
position.X = 0
|
||||
position.Y += int16(param)
|
||||
case column:
|
||||
position.X = int16(param) - 1
|
||||
}
|
||||
|
||||
return h.setCursorPosition(position, h.getCursorWindow(info))
|
||||
if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) moveCursorLine(param int) error {
|
||||
return h.moveCursor(line, param)
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
position := info.CursorPosition
|
||||
position.X = 0
|
||||
position.Y += int16(param)
|
||||
|
||||
if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) moveCursorColumn(param int) error {
|
||||
return h.moveCursor(column, param)
|
||||
info, err := GetConsoleScreenBufferInfo(h.fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
position := info.CursorPosition
|
||||
position.X = int16(param) - 1
|
||||
|
||||
if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+13
-3
@@ -1,4 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
@@ -50,7 +50,12 @@ func (h *windowsAnsiEventHandler) clearRange(attributes uint16, fromCoord COORD,
|
||||
coordStart.X, coordStart.Y = xCurrent, yCurrent
|
||||
coordEnd.X, coordEnd.Y = xEnd, yEnd
|
||||
|
||||
return h.clearRect(attributes, coordStart, coordEnd)
|
||||
err = h.clearRect(attributes, coordStart, coordEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) clearRect(attributes uint16, fromCoord COORD, toCoord COORD) error {
|
||||
@@ -70,5 +75,10 @@ func (h *windowsAnsiEventHandler) clearRect(attributes uint16, fromCoord COORD,
|
||||
buffer[i] = char
|
||||
}
|
||||
|
||||
return WriteConsoleOutput(h.fd, buffer, COORD{X: width, Y: height}, COORD{X: 0, Y: 0}, ®ion)
|
||||
err := WriteConsoleOutput(h.fd, buffer, COORD{X: width, Y: height}, COORD{X: 0, Y: 0}, ®ion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+11
-4
@@ -1,4 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
@@ -39,8 +39,9 @@ func (h *windowsAnsiEventHandler) deleteLines(param int) error {
|
||||
if start >= sr.top && start <= sr.bottom {
|
||||
sr.top = start
|
||||
return h.scroll(param, sr, info)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) insertLines(param int) error {
|
||||
@@ -71,7 +72,10 @@ func (h *windowsAnsiEventHandler) scroll(param int, sr scrollRegion, info *CONSO
|
||||
Attributes: h.attributes,
|
||||
}
|
||||
|
||||
return ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char)
|
||||
if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) deleteCharacters(param int) error {
|
||||
@@ -107,5 +111,8 @@ func (h *windowsAnsiEventHandler) scrollLine(columns int, position COORD, info *
|
||||
Attributes: h.attributes,
|
||||
}
|
||||
|
||||
return ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char)
|
||||
if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
|
||||
+33
-19
@@ -1,4 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package winterm
|
||||
|
||||
@@ -169,7 +169,9 @@ func (h *windowsAnsiEventHandler) executeLF() error {
|
||||
return err
|
||||
}
|
||||
h.logf("Resetting cursor position for LF without CR")
|
||||
return SetConsoleCursorPosition(h.fd, pos)
|
||||
if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -371,7 +373,7 @@ func (h *windowsAnsiEventHandler) CUP(row int, col int) error {
|
||||
}
|
||||
|
||||
window := h.getCursorWindow(info)
|
||||
position := COORD{X: window.Left + int16(col) - 1, Y: window.Top + int16(row) - 1}
|
||||
position := COORD{window.Left + int16(col) - 1, window.Top + int16(row) - 1}
|
||||
return h.setCursorPosition(position, window)
|
||||
}
|
||||
|
||||
@@ -421,7 +423,7 @@ func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
|
||||
targetWidth = 132
|
||||
}
|
||||
if info.Size.X < targetWidth {
|
||||
if err := SetConsoleScreenBufferSize(h.fd, COORD{X: targetWidth, Y: info.Size.Y}); err != nil {
|
||||
if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
|
||||
h.logf("set buffer failed: %v", err)
|
||||
return err
|
||||
}
|
||||
@@ -434,12 +436,12 @@ func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
|
||||
return err
|
||||
}
|
||||
if info.Size.X > targetWidth {
|
||||
if err := SetConsoleScreenBufferSize(h.fd, COORD{X: targetWidth, Y: info.Size.Y}); err != nil {
|
||||
if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
|
||||
h.logf("set buffer failed: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return SetConsoleCursorPosition(h.fd, COORD{X: 0, Y: 0})
|
||||
return SetConsoleCursorPosition(h.fd, COORD{0, 0})
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) ED(param int) error {
|
||||
@@ -466,15 +468,15 @@ func (h *windowsAnsiEventHandler) ED(param int) error {
|
||||
switch param {
|
||||
case 0:
|
||||
start = info.CursorPosition
|
||||
end = COORD{X: info.Size.X - 1, Y: info.Size.Y - 1}
|
||||
end = COORD{info.Size.X - 1, info.Size.Y - 1}
|
||||
|
||||
case 1:
|
||||
start = COORD{X: 0, Y: 0}
|
||||
start = COORD{0, 0}
|
||||
end = info.CursorPosition
|
||||
|
||||
case 2:
|
||||
start = COORD{X: 0, Y: 0}
|
||||
end = COORD{X: info.Size.X - 1, Y: info.Size.Y - 1}
|
||||
start = COORD{0, 0}
|
||||
end = COORD{info.Size.X - 1, info.Size.Y - 1}
|
||||
}
|
||||
|
||||
err = h.clearRange(h.attributes, start, end)
|
||||
@@ -493,7 +495,9 @@ func (h *windowsAnsiEventHandler) ED(param int) error {
|
||||
if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
|
||||
return err
|
||||
}
|
||||
return SetConsoleWindowInfo(h.fd, true, window)
|
||||
if err := SetConsoleWindowInfo(h.fd, true, window); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -521,18 +525,23 @@ func (h *windowsAnsiEventHandler) EL(param int) error {
|
||||
switch param {
|
||||
case 0:
|
||||
start = info.CursorPosition
|
||||
end = COORD{X: info.Size.X, Y: info.CursorPosition.Y}
|
||||
end = COORD{info.Size.X, info.CursorPosition.Y}
|
||||
|
||||
case 1:
|
||||
start = COORD{X: 0, Y: info.CursorPosition.Y}
|
||||
start = COORD{0, info.CursorPosition.Y}
|
||||
end = info.CursorPosition
|
||||
|
||||
case 2:
|
||||
start = COORD{X: 0, Y: info.CursorPosition.Y}
|
||||
end = COORD{X: info.Size.X, Y: info.CursorPosition.Y}
|
||||
start = COORD{0, info.CursorPosition.Y}
|
||||
end = COORD{info.Size.X, info.CursorPosition.Y}
|
||||
}
|
||||
|
||||
return h.clearRange(h.attributes, start, end)
|
||||
err = h.clearRange(h.attributes, start, end)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) IL(param int) error {
|
||||
@@ -602,7 +611,12 @@ func (h *windowsAnsiEventHandler) SGR(params []int) error {
|
||||
if h.inverted {
|
||||
attributes = invertAttributes(attributes)
|
||||
}
|
||||
return SetConsoleTextAttribute(h.fd, attributes)
|
||||
err := SetConsoleTextAttribute(h.fd, attributes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *windowsAnsiEventHandler) SU(param int) error {
|
||||
@@ -688,8 +702,8 @@ func (h *windowsAnsiEventHandler) Flush() error {
|
||||
}
|
||||
|
||||
charInfo := []CHAR_INFO{{UnicodeChar: uint16(h.marginByte), Attributes: info.Attributes}}
|
||||
size := COORD{X: 1, Y: 1}
|
||||
position := COORD{X: 0, Y: 0}
|
||||
size := COORD{1, 1}
|
||||
position := COORD{0, 0}
|
||||
region := SMALL_RECT{Left: info.CursorPosition.X, Top: info.CursorPosition.Y, Right: info.CursorPosition.X, Bottom: info.CursorPosition.Y}
|
||||
if err := WriteConsoleOutput(h.fd, charInfo, size, position, ®ion); err != nil {
|
||||
return err
|
||||
|
||||
-1
@@ -52,7 +52,6 @@ func (es *EndpointSettings) Copy() *EndpointSettings {
|
||||
epCopy.Aliases = slices.Clone(es.Aliases)
|
||||
epCopy.DNSNames = slices.Clone(es.DNSNames)
|
||||
epCopy.DriverOpts = maps.Clone(es.DriverOpts)
|
||||
epCopy.MacAddress = slices.Clone(es.MacAddress)
|
||||
|
||||
return &epCopy
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
testdata/rapid/**
|
||||
+2
-2
@@ -108,7 +108,7 @@ const DummyHost = "api.moby.localhost"
|
||||
// overriding the version and disable API-version negotiation.
|
||||
//
|
||||
// This version may be lower than the version of the api library module used.
|
||||
const MaxAPIVersion = "1.57"
|
||||
const MaxAPIVersion = "1.56"
|
||||
|
||||
// MinAPIVersion is the minimum API version supported by the client. API versions
|
||||
// below this version are not considered when performing API-version negotiation.
|
||||
@@ -436,7 +436,7 @@ func (cli *Client) dialer() func(context.Context) (net.Conn, error) {
|
||||
return dialPipeContext(ctx, cli.addr)
|
||||
default:
|
||||
if tlsConfig := cli.tlsConfig(); tlsConfig != nil {
|
||||
return (&tls.Dialer{Config: tlsConfig}).DialContext(ctx, cli.proto, cli.addr)
|
||||
return tls.Dial(cli.proto, cli.addr, tlsConfig)
|
||||
}
|
||||
return net.Dial(cli.proto, cli.addr)
|
||||
}
|
||||
|
||||
+1
-3
@@ -100,10 +100,8 @@ func (cli *Client) setupHijackConn(req *http.Request, proto string) (retConn net
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
// Closing the body ends the otelhttp span. A 101 response has no body,
|
||||
// so this does not close the connection.
|
||||
_ = resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusSwitchingProtocols {
|
||||
_ = resp.Body.Close()
|
||||
return nil, "", fmt.Errorf("unable to upgrade to %s, received %d", proto, resp.StatusCode)
|
||||
}
|
||||
|
||||
|
||||
-16
@@ -5,21 +5,6 @@ from https://github.com/syndtr/gocapability/commit/42c35b4376354fd5.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.5.0] - 2026-10-23
|
||||
|
||||
### Breaking changes
|
||||
* Go 1.24 or later is now required. (#264)
|
||||
|
||||
### Fixed
|
||||
* [LastCap] no longer reads `/proc/sys/kernel/cap_last_cap`, querying the
|
||||
kernel via `prctl(PR_CAPBSET_READ)` instead. This makes the package work
|
||||
when `/proc` is not mounted, and fixes a possible panic or infinite loop
|
||||
when `/proc/sys/kernel/cap_last_cap` contains a bogus value (for example,
|
||||
if something is mounted over `/proc`). (#265, #269)
|
||||
|
||||
### Changed
|
||||
* Minor code cleanups. (#187)
|
||||
|
||||
## [0.4.0] - 2024-11-11
|
||||
|
||||
### Added
|
||||
@@ -119,7 +104,6 @@ This is an initial release since the fork.
|
||||
[SetAmbient]: https://pkg.go.dev/github.com/moby/sys/capability#SetAmbient
|
||||
|
||||
<!-- Minor releases. -->
|
||||
[0.5.0]: https://github.com/moby/sys/releases/tag/capability%2Fv0.5.0
|
||||
[0.4.0]: https://github.com/moby/sys/releases/tag/capability%2Fv0.4.0
|
||||
[0.3.0]: https://github.com/moby/sys/releases/tag/capability%2Fv0.3.0
|
||||
[0.2.0]: https://github.com/moby/sys/releases/tag/capability%2Fv0.2.0
|
||||
|
||||
+15
-63
@@ -13,6 +13,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
@@ -24,75 +25,26 @@ const (
|
||||
linuxCapVer3 = 0x20080522
|
||||
)
|
||||
|
||||
// capValid reports whether the running kernel supports capability c.
|
||||
func capValid(c Cap) (bool, error) {
|
||||
err := prctl(syscall.PR_CAPBSET_READ, uintptr(c), 0)
|
||||
if errors.Is(err, syscall.EINVAL) {
|
||||
return false, nil
|
||||
}
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
// lastCap returns the highest capability supported by the running kernel.
|
||||
var lastCap = sync.OnceValues(func() (Cap, error) {
|
||||
last, err := findLastCap()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("unable to get last capability: %w", err)
|
||||
}
|
||||
return last, nil
|
||||
})
|
||||
|
||||
func findLastCap() (Cap, error) {
|
||||
// Based on the fact Go 1.24+ supports Linux >= 3.2, and
|
||||
// - CAP_WAKE_ALARM (35) was added in 3.0;
|
||||
// - CAP_CHECKPOINT_RESTORE (40) was added in 5.9, and it is
|
||||
// the last added capability as of October 2026 (Linux 7.3);
|
||||
//
|
||||
// the last capability is at least minLastCap, and most probably
|
||||
// lastKnownCap (but newer kernels may have more).
|
||||
//
|
||||
// maxLastCap is the highest capability which can be represented
|
||||
// by the kernel capability ABI v3 (two 32-bit words per set).
|
||||
const (
|
||||
minLastCap = CAP_WAKE_ALARM
|
||||
lastKnownCap = CAP_CHECKPOINT_RESTORE
|
||||
maxLastCap = Cap(63)
|
||||
)
|
||||
|
||||
// Start from lastKnownCap, and go up if it's valid, or down if it's
|
||||
// not. For most kernels, this takes two syscalls.
|
||||
c := lastKnownCap
|
||||
ok, err := capValid(c)
|
||||
f, err := os.Open("/proc/sys/kernel/cap_last_cap")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if ok {
|
||||
// Go up while the next one is valid.
|
||||
for c < maxLastCap {
|
||||
ok, err := capValid(c + 1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
c++
|
||||
}
|
||||
return c, nil
|
||||
|
||||
buf := make([]byte, 11)
|
||||
l, err := f.Read(buf)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Go down until a valid one is found.
|
||||
for c > minLastCap {
|
||||
c--
|
||||
ok, err := capValid(c)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if ok {
|
||||
return c, nil
|
||||
}
|
||||
buf = buf[:l]
|
||||
|
||||
last, err := strconv.Atoi(strings.TrimSpace(string(buf)))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return 0, fmt.Errorf("PR_CAPBSET_READ returned EINVAL for %s", minLastCap)
|
||||
}
|
||||
return Cap(last), nil
|
||||
})
|
||||
|
||||
func capUpperMask() uint32 {
|
||||
last, err := lastCap()
|
||||
|
||||
+3
-4
@@ -141,17 +141,16 @@ func setVfsCap(path string, data *vfscapData) (err error) {
|
||||
return
|
||||
}
|
||||
var size uintptr
|
||||
switch data.version {
|
||||
case 1:
|
||||
if data.version == 1 {
|
||||
data.magic = vfsCapVer1
|
||||
size = vfscapDataSizeV1
|
||||
case 2:
|
||||
} else if data.version == 2 {
|
||||
data.magic = vfsCapVer2
|
||||
if data.effective[0] != 0 || data.effective[1] != 0 {
|
||||
data.magic |= vfsCapFlageffective
|
||||
}
|
||||
size = vfscapDataSizeV2
|
||||
default:
|
||||
} else {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
_, _, e1 := syscall.RawSyscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(data)), size, 0, 0)
|
||||
|
||||
+5
-24
@@ -10,31 +10,12 @@ import (
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// Legacy HTTP attribute keys retained for compatibility.
|
||||
// Attribute keys that can be added to a span.
|
||||
const (
|
||||
// ReadBytesKey is the attribute key for the number of bytes returned by an
|
||||
// individual read from an HTTP request body.
|
||||
//
|
||||
// Deprecated: there is no direct semantic-convention replacement.
|
||||
ReadBytesKey = attribute.Key("http.read_bytes")
|
||||
|
||||
// ReadErrorKey is the attribute key for the string form of a non-EOF error
|
||||
// returned while reading an HTTP request body.
|
||||
//
|
||||
// Deprecated: there is no direct semantic-convention replacement.
|
||||
ReadErrorKey = attribute.Key("http.read_error")
|
||||
|
||||
// WroteBytesKey is the attribute key for the number of bytes returned by an
|
||||
// individual write to an HTTP response body.
|
||||
//
|
||||
// Deprecated: there is no direct semantic-convention replacement.
|
||||
WroteBytesKey = attribute.Key("http.wrote_bytes")
|
||||
|
||||
// WriteErrorKey is the attribute key for the string form of a non-EOF error
|
||||
// returned while writing an HTTP response body.
|
||||
//
|
||||
// Deprecated: there is no direct semantic-convention replacement.
|
||||
WriteErrorKey = attribute.Key("http.write_error")
|
||||
ReadBytesKey = attribute.Key("http.read_bytes") // if anything was read from the request body, the total number of bytes read
|
||||
ReadErrorKey = attribute.Key("http.read_error") // If an error occurred while reading a request, the string of the error (io.EOF is not recorded)
|
||||
WroteBytesKey = attribute.Key("http.wrote_bytes") // if anything was written to the response writer, the total number of bytes written
|
||||
WriteErrorKey = attribute.Key("http.write_error") // if an error occurred while writing a reply, the string of the error (io.EOF is not recorded)
|
||||
)
|
||||
|
||||
// Filter is a predicate used to determine whether a given http.request should
|
||||
|
||||
+6
-7
@@ -143,15 +143,14 @@ const (
|
||||
)
|
||||
|
||||
// WithMessageEvents configures the Handler to record the specified events
|
||||
// (span.AddEvent) on spans. By default, no read or write events are recorded.
|
||||
// (span.AddEvent) on spans. By default only summary attributes are added at the
|
||||
// end of the request.
|
||||
//
|
||||
// Valid events are:
|
||||
// - ReadEvents: Record a "read" span event after every http.Request.Body.Read.
|
||||
// - WriteEvents: Record a "write" span event after every http.ResponseWriter.Write.
|
||||
//
|
||||
// The events do not include per-call byte-count attributes. Total request and
|
||||
// response body sizes are recorded on the span according to HTTP semantic
|
||||
// conventions.
|
||||
// - ReadEvents: Record the number of bytes read after every http.Request.Body.Read
|
||||
// using the ReadBytesKey
|
||||
// - WriteEvents: Record the number of bytes written after every http.ResponeWriter.Write
|
||||
// using the WriteBytesKey
|
||||
func WithMessageEvents(events ...Event) Option {
|
||||
return optionFunc(func(c *config) {
|
||||
for _, e := range events {
|
||||
|
||||
+11
-20
@@ -129,8 +129,8 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http
|
||||
|
||||
readRecordFunc := func(int64) {}
|
||||
if h.readEvent {
|
||||
readRecordFunc = func(int64) {
|
||||
span.AddEvent("read")
|
||||
readRecordFunc = func(n int64) {
|
||||
span.AddEvent("read", trace.WithAttributes(ReadBytesKey.Int64(n)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,8 +150,8 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http
|
||||
|
||||
writeRecordFunc := func(int64) {}
|
||||
if h.writeEvent {
|
||||
writeRecordFunc = func(int64) {
|
||||
span.AddEvent("write")
|
||||
writeRecordFunc = func(n int64) {
|
||||
span.AddEvent("write", trace.WithAttributes(WroteBytesKey.Int64(n)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,21 +180,12 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http
|
||||
if !found {
|
||||
ctx = ContextWithLabeler(ctx, labeler)
|
||||
}
|
||||
rCtx := r.WithContext(ctx)
|
||||
defer func() {
|
||||
// Copy MultipartForm back to the original request so net/http can
|
||||
// find and remove any temp files ParseMultipartForm created on the
|
||||
// copy. Deferred so the copy-back also runs during panic unwinding,
|
||||
// letting net/http cleanup paths that still run (HTTP/2 handler
|
||||
// recovery, outer recovery middleware) find the form.
|
||||
if rCtx.MultipartForm != nil {
|
||||
r.MultipartForm = rCtx.MultipartForm
|
||||
}
|
||||
}()
|
||||
next.ServeHTTP(w, rCtx)
|
||||
|
||||
if rCtx.Pattern != "" {
|
||||
span.SetName(h.spanNameFormatter(h.operation, rCtx))
|
||||
r = r.WithContext(ctx)
|
||||
next.ServeHTTP(w, r)
|
||||
|
||||
if r.Pattern != "" {
|
||||
span.SetName(h.spanNameFormatter(h.operation, r))
|
||||
}
|
||||
|
||||
statusCode := rww.StatusCode()
|
||||
@@ -213,9 +204,9 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http
|
||||
ServerName: h.server,
|
||||
ResponseSize: bytesWritten,
|
||||
MetricAttributes: semconv.MetricAttributes{
|
||||
Req: rCtx,
|
||||
Req: r,
|
||||
StatusCode: statusCode,
|
||||
AdditionalAttributes: append(labeler.Get(), h.metricAttributesFromRequest(rCtx)...),
|
||||
AdditionalAttributes: append(labeler.Get(), h.metricAttributesFromRequest(r)...),
|
||||
},
|
||||
MetricData: semconv.MetricData{
|
||||
RequestSize: bytesRead,
|
||||
|
||||
Generated
Vendored
+5
-3
@@ -38,9 +38,11 @@ func SplitHostPort(hostport string) (host string, port int) {
|
||||
host = hostport[1:addrEnd]
|
||||
return host, port
|
||||
}
|
||||
} else if !strings.Contains(hostport, ":") {
|
||||
host = hostport
|
||||
return host, port
|
||||
} else {
|
||||
if i := strings.LastIndexByte(hostport, ':'); i < 0 {
|
||||
host = hostport
|
||||
return host, port
|
||||
}
|
||||
}
|
||||
|
||||
host, pStr, err := net.SplitHostPort(hostport)
|
||||
|
||||
-24
@@ -56,27 +56,3 @@ func LabelerFromContext(ctx context.Context) (*Labeler, bool) {
|
||||
}
|
||||
return l, ok
|
||||
}
|
||||
|
||||
// clientLabelerContextKey is a separate context key for the client-side Labeler.
|
||||
// This prevents server-side Labeler attributes (e.g., http.route) from leaking
|
||||
// into client-side metrics when a server handler propagates its request context
|
||||
// into an outbound HTTP client request.
|
||||
const clientLabelerContextKey labelerContextKeyType = 1
|
||||
|
||||
// ContextWithClientLabeler returns a new context with the provided Labeler instance
|
||||
// for use with client-side HTTP instrumentation (otelhttp.Transport).
|
||||
// Attributes added to this labeler will be attached to client-side metrics.
|
||||
func ContextWithClientLabeler(parent context.Context, l *Labeler) context.Context {
|
||||
return context.WithValue(parent, clientLabelerContextKey, l)
|
||||
}
|
||||
|
||||
// ClientLabelerFromContext retrieves a Labeler instance from the provided context
|
||||
// for use with client-side HTTP instrumentation. If no Labeler was found, a new,
|
||||
// empty Labeler is returned and the second return value is false.
|
||||
func ClientLabelerFromContext(ctx context.Context) (*Labeler, bool) {
|
||||
l, ok := ctx.Value(clientLabelerContextKey).(*Labeler)
|
||||
if !ok {
|
||||
l = &Labeler{}
|
||||
}
|
||||
return l, ok
|
||||
}
|
||||
|
||||
Generated
Vendored
+52
-180
@@ -9,7 +9,6 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -20,6 +19,7 @@ import (
|
||||
otelsemconv "go.opentelemetry.io/otel/semconv/v1.43.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
|
||||
)
|
||||
|
||||
@@ -82,142 +82,6 @@ func defaultTransportFormatter(_ string, r *http.Request) string {
|
||||
return "HTTP " + r.Method
|
||||
}
|
||||
|
||||
// requestTracker tracks the request body lifecycle of a single RoundTrip and
|
||||
// records the client metrics exactly once, after the request body reaches a
|
||||
// final state.
|
||||
//
|
||||
// A single tracker is allocated per request with a body, replacing the
|
||||
// body-wrapper allocation of the untracked implementation: the first request
|
||||
// body is wrapped by the embedded first field, and the lifecycle state is
|
||||
// held in explicit fields with methods instead of escaping closures. Only
|
||||
// GetBody retries allocate an additional trackedRequestBody.
|
||||
type requestTracker struct {
|
||||
transport *Transport
|
||||
first trackedRequestBody
|
||||
|
||||
originalGetBody func() (io.ReadCloser, error)
|
||||
|
||||
// mu protects body, published, and recorded. Body close callbacks may
|
||||
// fire on a different goroutine (e.g. during transport retries or
|
||||
// asynchronous closes) while the main goroutine publishes the metric
|
||||
// data after RoundTrip returns.
|
||||
mu sync.Mutex
|
||||
body *trackedRequestBody // Last wrapped request body; nil records no data.
|
||||
published bool
|
||||
recorded bool
|
||||
|
||||
// Metric data published when RoundTrip returns; immutable afterwards.
|
||||
ctx context.Context
|
||||
duration time.Duration
|
||||
opts semconv.MetricOpts
|
||||
}
|
||||
|
||||
func newRequestTracker(t *Transport, r *http.Request) *requestTracker {
|
||||
rt := &requestTracker{transport: t}
|
||||
rt.first = trackedRequestBody{body: r.Body, tracker: rt}
|
||||
rt.body = &rt.first
|
||||
r.Body = &rt.first
|
||||
if r.GetBody != nil {
|
||||
rt.originalGetBody = r.GetBody
|
||||
r.GetBody = rt.getBody
|
||||
}
|
||||
return rt
|
||||
}
|
||||
|
||||
func (rt *requestTracker) getBody() (io.ReadCloser, error) {
|
||||
b, err := rt.originalGetBody()
|
||||
if err != nil {
|
||||
// The underlying transport will fail to make a retry request,
|
||||
// hence, record no data.
|
||||
rt.setBody(nil)
|
||||
return nil, err
|
||||
}
|
||||
if b == nil || b == http.NoBody {
|
||||
return b, nil
|
||||
}
|
||||
tb := &trackedRequestBody{body: b, tracker: rt}
|
||||
rt.setBody(tb)
|
||||
return tb, nil
|
||||
}
|
||||
|
||||
func (rt *requestTracker) setBody(b *trackedRequestBody) {
|
||||
rt.mu.Lock()
|
||||
rt.body = b
|
||||
rt.mu.Unlock()
|
||||
}
|
||||
|
||||
// publish stores the metric data gathered when RoundTrip returns and records
|
||||
// the metrics if the request body already reached a final state.
|
||||
func (rt *requestTracker) publish(ctx context.Context, d time.Duration, opts semconv.MetricOpts) {
|
||||
rt.mu.Lock()
|
||||
rt.ctx = ctx
|
||||
rt.duration = d
|
||||
rt.opts = opts
|
||||
rt.published = true
|
||||
rt.mu.Unlock()
|
||||
rt.maybeRecord()
|
||||
}
|
||||
|
||||
// maybeRecord records the client metrics once the metric data has been
|
||||
// published and the request body has reached a final state. Close is the
|
||||
// only authoritative completion signal for a non-nil body: a body may yield
|
||||
// more bytes than its declared ContentLength, so a read count is never proof
|
||||
// of completion.
|
||||
func (rt *requestTracker) maybeRecord() {
|
||||
rt.mu.Lock()
|
||||
body := rt.body
|
||||
record := rt.published && !rt.recorded && (body == nil || body.closed.Load())
|
||||
if record {
|
||||
rt.recorded = true
|
||||
}
|
||||
rt.mu.Unlock()
|
||||
if !record {
|
||||
return
|
||||
}
|
||||
|
||||
// The winning caller evaluates the request size here so the recorded
|
||||
// value reflects the latest byte count.
|
||||
var requestSize int64
|
||||
if body != nil {
|
||||
requestSize = body.read.Load()
|
||||
}
|
||||
rt.transport.semconv.RecordMetrics(
|
||||
rt.ctx,
|
||||
semconv.MetricData{
|
||||
RequestSize: requestSize,
|
||||
RequestDuration: rt.duration,
|
||||
},
|
||||
rt.opts,
|
||||
)
|
||||
}
|
||||
|
||||
// trackedRequestBody wraps a request body to count read bytes and signal the
|
||||
// tracker when the body is closed.
|
||||
type trackedRequestBody struct {
|
||||
body io.ReadCloser
|
||||
tracker *requestTracker
|
||||
|
||||
read atomic.Int64
|
||||
closed atomic.Bool
|
||||
}
|
||||
|
||||
var _ io.ReadCloser = (*trackedRequestBody)(nil)
|
||||
|
||||
func (b *trackedRequestBody) Read(p []byte) (int, error) {
|
||||
n, err := b.body.Read(p)
|
||||
b.read.Add(int64(n))
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (b *trackedRequestBody) Close() error {
|
||||
// Finalize telemetry before delegating to the underlying Close, which
|
||||
// may block (mirroring wrappedBody.Close). Otherwise a blocking close
|
||||
// could suppress client metrics indefinitely.
|
||||
b.closed.Store(true)
|
||||
b.tracker.maybeRecord()
|
||||
return b.body.Close()
|
||||
}
|
||||
|
||||
// RoundTrip creates a Span and propagates its context via the provided request's headers
|
||||
// before handing the request to the configured base RoundTripper. The created span will
|
||||
// end when the response body is closed or when a read from the body returns io.EOF.
|
||||
@@ -246,20 +110,33 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
ctx = httptrace.WithClientTrace(ctx, t.clientTrace(ctx))
|
||||
}
|
||||
|
||||
labeler, found := ClientLabelerFromContext(ctx)
|
||||
labeler, found := LabelerFromContext(ctx)
|
||||
if !found {
|
||||
ctx = ContextWithClientLabeler(ctx, labeler)
|
||||
ctx = ContextWithLabeler(ctx, labeler)
|
||||
}
|
||||
|
||||
r = r.Clone(ctx) // According to RoundTripper spec, we shouldn't modify the origin request.
|
||||
|
||||
// Track the request body lifecycle so metrics report the final read
|
||||
// size even when the transport keeps reading the body after RoundTrip
|
||||
// returns. Bodyless requests allocate no tracker and record
|
||||
// synchronously below.
|
||||
var tracker *requestTracker
|
||||
if r.Body != nil && r.Body != http.NoBody {
|
||||
tracker = newRequestTracker(t, r)
|
||||
var lastBW *request.BodyWrapper // Records the last body wrapper. Can be nil.
|
||||
maybeWrapBody := func(body io.ReadCloser) io.ReadCloser {
|
||||
if body == nil || body == http.NoBody {
|
||||
return body
|
||||
}
|
||||
bw := request.NewBodyWrapper(body, func(int64) {})
|
||||
lastBW = bw
|
||||
return bw
|
||||
}
|
||||
r.Body = maybeWrapBody(r.Body)
|
||||
if r.GetBody != nil {
|
||||
originalGetBody := r.GetBody
|
||||
r.GetBody = func() (io.ReadCloser, error) {
|
||||
b, err := originalGetBody()
|
||||
if err != nil {
|
||||
lastBW = nil // The underlying transport will fail to make a retry request, hence, record no data.
|
||||
return nil, err
|
||||
}
|
||||
return maybeWrapBody(b), nil
|
||||
}
|
||||
}
|
||||
|
||||
span.SetAttributes(t.semconv.RequestTraceAttrs(r)...)
|
||||
@@ -270,32 +147,29 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
res, err = ensureResponseBody(t.rt, r, res)
|
||||
}
|
||||
|
||||
requestDuration := time.Since(requestStartTime)
|
||||
// Record the metrics on error or no error.
|
||||
statusCode := 0
|
||||
if err == nil {
|
||||
statusCode = res.StatusCode
|
||||
}
|
||||
metricOpts := t.semconv.MetricOptions(semconv.MetricAttributes{
|
||||
Req: r,
|
||||
Resp: res,
|
||||
StatusCode: statusCode,
|
||||
Err: err,
|
||||
AdditionalAttributes: append(labeler.Get(), t.metricAttributesFromRequest(r)...),
|
||||
})
|
||||
|
||||
if tracker != nil {
|
||||
// Delay metric recording until the request body reaches a final
|
||||
// state. The transport can continue reading the body after
|
||||
// RoundTrip returns, including on errors, since RoundTrippers may
|
||||
// close the request body asynchronously.
|
||||
tracker.publish(ctx, requestDuration, metricOpts)
|
||||
} else {
|
||||
t.semconv.RecordMetrics(
|
||||
ctx,
|
||||
semconv.MetricData{RequestDuration: requestDuration},
|
||||
metricOpts,
|
||||
)
|
||||
var requestSize int64
|
||||
if lastBW != nil {
|
||||
requestSize = lastBW.BytesRead()
|
||||
}
|
||||
t.semconv.RecordMetrics(
|
||||
ctx,
|
||||
semconv.MetricData{
|
||||
RequestSize: requestSize,
|
||||
RequestDuration: time.Since(requestStartTime),
|
||||
},
|
||||
t.semconv.MetricOptions(semconv.MetricAttributes{
|
||||
Req: r,
|
||||
Resp: res,
|
||||
StatusCode: statusCode,
|
||||
Err: err,
|
||||
AdditionalAttributes: append(labeler.Get(), t.metricAttributesFromRequest(r)...),
|
||||
}),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
span.SetAttributes(otelsemconv.ErrorType(err))
|
||||
@@ -389,7 +263,8 @@ func (wb *wrappedBody) Read(b []byte) (int, error) {
|
||||
case nil:
|
||||
// nothing to do here but fall through to the return
|
||||
case io.EOF:
|
||||
wb.recordMetricsOnce()
|
||||
wb.recordBytesRead()
|
||||
wb.span.End()
|
||||
default:
|
||||
wb.span.SetAttributes(otelsemconv.ErrorType(err))
|
||||
wb.span.SetStatus(codes.Error, err.Error())
|
||||
@@ -397,24 +272,21 @@ func (wb *wrappedBody) Read(b []byte) (int, error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
// recordMetricsOnce ensures the final number of bytes read is recorded once.
|
||||
func (wb *wrappedBody) recordMetricsOnce() {
|
||||
// note: it is more performant (and equally correct) to use atomic.Bool
|
||||
// over sync.Once here. In the event that two goroutines are racing to
|
||||
// call this method, the number of bytes read will no longer increase.
|
||||
// Using CompareAndSwap allows later goroutines to return quickly and not
|
||||
// block waiting for the race winner to finish calling
|
||||
// wb.record(wb.read.Load()).
|
||||
// recordBytesRead is a function that ensures the number of bytes read is recorded once and only once.
|
||||
func (wb *wrappedBody) recordBytesRead() {
|
||||
// note: it is more performant (and equally correct) to use atomic.Bool over sync.Once here. In the event that
|
||||
// two goroutines are racing to call this method, the number of bytes read will no longer increase. Using
|
||||
// CompareAndSwap allows later goroutines to return quickly and not block waiting for the race winner to finish
|
||||
// calling wb.record(wb.read.Load()).
|
||||
if wb.recorded.CompareAndSwap(false, true) {
|
||||
// Record the total number of bytes read
|
||||
wb.record(wb.read.Load())
|
||||
wb.span.End()
|
||||
}
|
||||
}
|
||||
|
||||
func (wb *wrappedBody) Close() error {
|
||||
// Finalize telemetry before delegating to the underlying Close, which
|
||||
// may block (e.g. upgraded streams or custom RoundTripper bodies).
|
||||
wb.recordMetricsOnce()
|
||||
wb.recordBytesRead()
|
||||
wb.span.End()
|
||||
if wb.body != nil {
|
||||
return wb.body.Close()
|
||||
}
|
||||
|
||||
+1
-1
@@ -4,4 +4,4 @@
|
||||
package otelhttp
|
||||
|
||||
// Version is the current release version of the otelhttp instrumentation.
|
||||
const Version = "0.72.0"
|
||||
const Version = "0.71.0"
|
||||
|
||||
+8
-4
@@ -230,6 +230,13 @@ linters:
|
||||
- revive
|
||||
path: schema/v.*/types/.*
|
||||
text: avoid meaningless package names
|
||||
# TODO: Having appropriate comments for exported objects helps development,
|
||||
# even for objects in internal packages. Appropriate comments for all
|
||||
# exported objects should be added and this exclusion removed.
|
||||
- linters:
|
||||
- revive
|
||||
path: .*internal/.*
|
||||
text: exported (method|function|type|const) (.+) should have comment or be unexported
|
||||
# Yes, they are, but it's okay in a test.
|
||||
- linters:
|
||||
- revive
|
||||
@@ -265,10 +272,7 @@ formatters:
|
||||
- golines
|
||||
settings:
|
||||
gofumpt:
|
||||
extra:
|
||||
group-params: true
|
||||
clothe-returns: true
|
||||
balance-calls: true
|
||||
extra-rules: true
|
||||
goimports:
|
||||
local-prefixes:
|
||||
- go.opentelemetry.io/otel
|
||||
|
||||
+1
-90
@@ -11,93 +11,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
<!-- Released section -->
|
||||
<!-- Don't change this section unless doing release -->
|
||||
|
||||
## [1.47.0/0.69.0/0.23.0/0.1.0] - 2026-10-02
|
||||
|
||||
This release contains the first stable release of the OpenTelemetry Go Logs API and SDK.
|
||||
Our project stability guarantees now apply to the following modules:
|
||||
|
||||
- `go.opentelemetry.io/otel/log`
|
||||
- `go.opentelemetry.io/otel/sdk/log`
|
||||
|
||||
See our [versioning policy](VERSIONING.md) for more information about these stability guarantees.
|
||||
|
||||
### Added
|
||||
|
||||
- Add `String` method for `KeyValue` type in `go.opentelemetry.io/otel/attribute`. (#8205)
|
||||
- Add `String` method for `Set` type in `go.opentelemetry.io/otel/attribute`. (#8347)
|
||||
- Add `WithMaxExportBatchSize` to `go.opentelemetry.io/otel/sdk/metric` to configure the maximum export batch size for `PeriodicReader`. (#8960)
|
||||
- Add `WithAttributeValueDepthLimit`, `DefaultAttributeValueDepthLimit`, and `SpanLimits.AttributeValueDepthLimit` in `go.opentelemetry.io/otel/sdk/trace` to configure the maximum depth of span, event, link, and instrumentation scope attribute values. (#8938)
|
||||
- Add `WithAttributeValueDepthLimit` in `go.opentelemetry.io/otel/sdk/log` to configure the maximum depth of log record and instrumentation scope attribute values. (#8938)
|
||||
- Add `WithMaxResponseSize` to `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`,
|
||||
`go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`, and
|
||||
`go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8941)
|
||||
- Add experimental `ProbabilitySampler` in `go.opentelemetry.io/otel/sdk/trace/x` that conforms to the [OpenTelemetry specification's threshold-based sampling algorithm](https://opentelemetry.io/docs/specs/otel/trace/sdk/#probabilitysampler). (#8123)
|
||||
- Add experimental `*Binder` extension interfaces in `go.opentelemetry.io/otel/metric/x` for instruments that support binding attributes ahead of time. (#8760)
|
||||
- Add the experimental `Finisher` synchronous metric instrument extension interface to `go.opentelemetry.io/otel/metric/x`. (#8906)
|
||||
|
||||
### Changed
|
||||
|
||||
- Use the [OpenTelemetry Attribute Collection representation for non-OTLP protocols](https://opentelemetry.io/docs/specs/otel/common/#attribute-collection-representation-for-non-otlp) for metric data diffs in `go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest`. (#8993)
|
||||
- Apply a maximum depth of 64 by default to span, event, link, log record, and instrumentation scope attribute values in `go.opentelemetry.io/otel/sdk/trace` and `go.opentelemetry.io/otel/sdk/log`. (#8938)
|
||||
- Reduce allocations when applying attribute value length limits in `go.opentelemetry.io/otel/sdk/trace` and `go.opentelemetry.io/otel/sdk/log` by rebuilding composite values only when truncation is needed. (#8912)
|
||||
- Decode `traceparent` using a hex lookup table in `go.opentelemetry.io/otel/propagation`, which rejects the specification-disallowed upper-case characters without a separate scan of the header. (#8739)
|
||||
- Decode all characters at constant indices in `TraceIDFromHex` and `SpanIDFromHex` in `go.opentelemetry.io/otel/trace` to eliminate bounds checks. (#8740)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Count valid U+FFFD replacement characters toward attribute value length limits in `go.opentelemetry.io/otel/trace`, `go.opentelemetry.io/otel/sdk/trace`, and `go.opentelemetry.io/otel/sdk/log`.
|
||||
- Truncate string attribute values to empty strings when the configured length limit is zero, including malformed UTF-8, in `go.opentelemetry.io/otel/trace`, `go.opentelemetry.io/otel/sdk/trace`, and `go.opentelemetry.io/otel/sdk/log`. (#9054)
|
||||
- Fix a data race in `NewPeriodicReader` where `r.inst` was assigned after the background goroutine was launched in `go.opentelemetry.io/otel/sdk/metric`. (#9028)
|
||||
- Prevent precision loss for large `int64` values in `Sum`, `Histogram`, and `ExponentialHistogram` aggregations in `go.opentelemetry.io/otel/sdk/metric`. (#8981)
|
||||
- Ensure `grpc.DialOption` values passed via `WithDialOption` take precedence over conflicting internally-computed defaults in
|
||||
`go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`,
|
||||
`go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`, and
|
||||
`go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. (#8805, #8834, #8836)
|
||||
- Treat overflowing `OTEL_METRIC_EXPORT_INTERVAL` and `OTEL_METRIC_EXPORT_TIMEOUT` values as invalid in `go.opentelemetry.io/otel/sdk/metric`. (#8800)
|
||||
- Prevent a panic in `NewFixedSizeReservoir` and `FixedSizeReservoirProvider` when given a negative size in `go.opentelemetry.io/otel/sdk/metric/exemplar`; negative sizes are now clamped to zero, consistent with a size of zero. (#8832)
|
||||
- Preserve exponential histogram `ZeroThreshold` during OTLP metric export in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric`. (#8801)
|
||||
- Ensure metric helpers in `go.opentelemetry.io/otel/semconv/v1.32.0`, `go.opentelemetry.io/otel/semconv/v1.33.0`, and `go.opentelemetry.io/otel/semconv/v1.34.0` record measurements only once when no attributes are provided. (#8849)
|
||||
- Treat empty `OTEL_TRACES_SAMPLER` and `OTEL_TRACES_SAMPLER_ARG` values as unset in `go.opentelemetry.io/otel/sdk/trace`. (#8873)
|
||||
- Normalize global `OTEL_EXPORTER_OTLP_ENDPOINT` path joining in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp` so trailing-slash base URLs do not produce double-slash log export paths. (#8864)
|
||||
- Prevent a deadlock when formatting an error passed to `RecordError` calls back into the span in `go.opentelemetry.io/otel/sdk/trace`. (#8815)
|
||||
- Prevent `SimpleSpanProcessor.Shutdown` from panicking when constructed with a nil exporter in `go.opentelemetry.io/otel/sdk/trace`. (#8844)
|
||||
- Propagate invalid exponential histogram scale errors to Prometheus exporter self-observability metrics in `go.opentelemetry.io/otel/exporters/prometheus`. (#8839)
|
||||
- Retain instrument advisory attributes configured with `go.opentelemetry.io/otel/metric/x.WithDefaultAttributes` when a matching View in `go.opentelemetry.io/otel/sdk/metric` does not specify an attribute filter. (#8859)
|
||||
- Ignore HTTP URL paths when building OTLP/gRPC metric exporter targets from `OTEL_EXPORTER_OTLP_ENDPOINT` and `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT`, while preserving `unix://` and `unix-abstract://` targets; treat `unix-abstract://` endpoints as insecure in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#8862)
|
||||
- Ignore HTTP(S) paths when deriving gRPC trace exporter endpoints from `OTEL_EXPORTER_OTLP_ENDPOINT` and `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. (#8852)
|
||||
- Bound memory used while parsing dense W3C property metadata by storing it compactly and materializing `Property` values on demand in `go.opentelemetry.io/otel/baggage` and `go.opentelemetry.io/otel/propagation`.
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove the experimental `OTEL_GO_X_METRIC_EXPORT_BATCH_SIZE` environment variable in `go.opentelemetry.io/otel/sdk/metric`.
|
||||
Use the `WithMaxExportBatchSize` option instead. (#8960)
|
||||
|
||||
## [1.47.0-rc.1] - 2026-08-28
|
||||
|
||||
This is a release candidate for the v1.47.0 release.
|
||||
That release is expected to include the `v1` release of the OpenTelemetry Go Logs API and SDK and will provide stability guarantees for the following modules:
|
||||
|
||||
- `go.opentelemetry.io/otel/log`
|
||||
- `go.opentelemetry.io/otel/sdk/log`
|
||||
|
||||
See our [versioning policy](VERSIONING.md) for more information about these stability guarantees.
|
||||
|
||||
### Added
|
||||
|
||||
- Add `Logger`, `GetLoggerProvider`, and `SetLoggerProvider` to `go.opentelemetry.io/otel`. (#8874)
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Deprecate `go.opentelemetry.io/otel/log/global`; use the equivalent APIs in `go.opentelemetry.io/otel`. (#8874)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Ignore attempts to unregister unknown span processors in `go.opentelemetry.io/otel/sdk/trace`. (#8840)
|
||||
|
||||
### Removed
|
||||
|
||||
- Drop support for [Go 1.25]. (#8876)
|
||||
|
||||
## [1.46.0/0.68.0/0.22.0/0.0.19] - 2026-08-25
|
||||
|
||||
This release is the last to support [Go 1.25].
|
||||
@@ -3897,9 +3810,7 @@ It contains api and sdk for trace and meter.
|
||||
- CircleCI build CI manifest files.
|
||||
- CODEOWNERS file to track owners of this project.
|
||||
|
||||
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.47.0...HEAD
|
||||
[1.47.0/0.69.0/0.23.0/0.1.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.47.0
|
||||
[1.47.0-rc.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.47.0-rc.1
|
||||
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.46.0...HEAD
|
||||
[1.46.0/0.68.0/0.22.0/0.0.19]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.46.0
|
||||
[1.45.0/0.67.0/0.21.0/0.0.18]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.45.0
|
||||
[1.44.0/0.66.0/0.20.0/0.0.17]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.44.0
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# Instructions for Claude Code
|
||||
|
||||
@AGENTS.md
|
||||
+1
-1
@@ -12,6 +12,6 @@
|
||||
# https://help.github.com/en/articles/about-code-owners
|
||||
#
|
||||
|
||||
* @MrAlias @XSAM @dashpole @pellared @dmathieu @flc1125 @ps-mir
|
||||
* @MrAlias @XSAM @dashpole @pellared @dmathieu @flc1125
|
||||
|
||||
CODEOWNERS @MrAlias @pellared @dashpole @XSAM @dmathieu
|
||||
|
||||
+9
-11
@@ -779,7 +779,7 @@ func newInstrumentation() (*instrumentation, error) {
|
||||
if !x.Observability.Enabled() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
meter := otel.GetMeterProvider().Meter(
|
||||
"<component-package-name>",
|
||||
metric.WithInstrumentationVersion(sdk.Version()),
|
||||
@@ -949,7 +949,7 @@ func BenchmarkExportSpans(b *testing.B) {
|
||||
{"ObsDisabled", false},
|
||||
{"ObsEnabled", true},
|
||||
}
|
||||
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
b.Run(scenario.name, func(b *testing.B) {
|
||||
b.Setenv(
|
||||
@@ -980,7 +980,7 @@ func newInstrumentation() (*instrumentation, error) {
|
||||
if !x.Observability.Enabled() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
m := otel.GetMeterProvider().Meter(/* initialize meter */)
|
||||
counter, err := otelconv.NewSDKComponentCounter(m)
|
||||
// Use the partially initialized counter if available.
|
||||
@@ -996,7 +996,7 @@ func newInstrumentation() *instrumentation {
|
||||
if !x.Observability.Enabled() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
m := otel.GetMeterProvider().Meter(/* initialize meter */)
|
||||
counter, err := otelconv.NewSDKComponentCounter(m)
|
||||
if err != nil {
|
||||
@@ -1023,7 +1023,7 @@ func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan)
|
||||
if e.inst.Enabled(ctx) {
|
||||
e.inst.recordSpanExportStarted(ctx, len(spans))
|
||||
}
|
||||
|
||||
|
||||
err := e.doExport(ctx, spans)
|
||||
|
||||
if e.inst.Enabled(ctx) {
|
||||
@@ -1043,11 +1043,11 @@ func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan)
|
||||
func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error {
|
||||
// ❌ Do not break the context propagation.
|
||||
e.inst.recordSpanExportStarted(context.Background(), len(spans))
|
||||
|
||||
|
||||
err := e.doExport(ctx, spans)
|
||||
|
||||
/* ... */
|
||||
|
||||
|
||||
return err
|
||||
}
|
||||
```
|
||||
@@ -1118,7 +1118,7 @@ func TestObservability(t *testing.T) {
|
||||
|
||||
// Reset component ID counter to ensure deterministic component names.
|
||||
componentIDCounter.Store(0)
|
||||
|
||||
|
||||
/* ... test code ... */
|
||||
}
|
||||
```
|
||||
@@ -1203,20 +1203,18 @@ For more information about the maintainer role, see the [community repository](h
|
||||
### Approvers
|
||||
|
||||
- [Flc](https://github.com/flc1125), Independent
|
||||
- [Puneet Singh](https://github.com/ps-mir), Independent
|
||||
|
||||
For more information about the approver role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#approver).
|
||||
|
||||
### Triagers
|
||||
|
||||
- [Marc Schäfer](https://github.com/marcschaeferger), T&A SYSTEME GmbH
|
||||
- [Alex Kats](https://github.com/akats7), Capital One
|
||||
|
||||
For more information about the triager role, see the [community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#triager).
|
||||
|
||||
### Emeritus
|
||||
|
||||
- [Aaron Clawson](https://github.com/MadVikingGod)
|
||||
- [Alex Kats](https://github.com/akats7)
|
||||
- [Anthony Mirabella](https://github.com/Aneurysm9)
|
||||
- [Cheng-Zhen Yang](https://github.com/scorpionknifes)
|
||||
- [Chester Cheung](https://github.com/hanyuancheung)
|
||||
|
||||
+19
-2
@@ -3,11 +3,12 @@
|
||||
[](https://github.com/open-telemetry/opentelemetry-go/actions/workflows/ci.yml)
|
||||
[](https://app.codecov.io/gh/open-telemetry/opentelemetry-go?branch=main)
|
||||
[](https://pkg.go.dev/go.opentelemetry.io/otel)
|
||||
[](https://goreportcard.com/report/go.opentelemetry.io/otel)
|
||||
[](https://scorecard.dev/viewer/?uri=github.com/open-telemetry/opentelemetry-go)
|
||||
[](https://www.bestpractices.dev/projects/9996)
|
||||
[](https://issues.oss-fuzz.com/issues?q=project:opentelemetry-go)
|
||||
[](https://app.fossa.com/projects/custom%2B162%2Fgithub.com%2Fopen-telemetry%2Fopentelemetry-go?ref=badge_shield&issueType=license)
|
||||
[](https://slack.cncf.io/)
|
||||
[](https://cloud-native.slack.com/archives/C01NPAXACKT)
|
||||
|
||||
OpenTelemetry-Go is the [Go](https://golang.org/) implementation of [OpenTelemetry](https://opentelemetry.io/).
|
||||
It provides a set of APIs to directly measure performance and behavior of your software and send this data to observability platforms.
|
||||
@@ -18,7 +19,7 @@ It provides a set of APIs to directly measure performance and behavior of your s
|
||||
|---------|--------------------|
|
||||
| Traces | Stable |
|
||||
| Metrics | Stable |
|
||||
| Logs | Stable |
|
||||
| Logs | Beta[^1] |
|
||||
|
||||
Progress and status specific to this repository is tracked in our
|
||||
[project boards](https://github.com/open-telemetry/opentelemetry-go/projects)
|
||||
@@ -28,6 +29,8 @@ and
|
||||
Project versioning information and stability guarantees can be found in the
|
||||
[versioning documentation](VERSIONING.md).
|
||||
|
||||
[^1]: https://github.com/orgs/open-telemetry/projects/43
|
||||
|
||||
### Compatibility
|
||||
|
||||
OpenTelemetry-Go ensures compatibility with the current supported versions of
|
||||
@@ -52,18 +55,25 @@ Currently, this project supports the following environments.
|
||||
|----------|------------|--------------|
|
||||
| Ubuntu | 1.27 | amd64 |
|
||||
| Ubuntu | 1.26 | amd64 |
|
||||
| Ubuntu | 1.25 | amd64 |
|
||||
| Ubuntu | 1.27 | 386 |
|
||||
| Ubuntu | 1.26 | 386 |
|
||||
| Ubuntu | 1.25 | 386 |
|
||||
| Ubuntu | 1.27 | arm64 |
|
||||
| Ubuntu | 1.26 | arm64 |
|
||||
| Ubuntu | 1.25 | arm64 |
|
||||
| macOS | 1.27 | amd64 |
|
||||
| macOS | 1.26 | amd64 |
|
||||
| macOS | 1.25 | amd64 |
|
||||
| macOS | 1.27 | arm64 |
|
||||
| macOS | 1.26 | arm64 |
|
||||
| macOS | 1.25 | arm64 |
|
||||
| Windows | 1.27 | amd64 |
|
||||
| Windows | 1.26 | amd64 |
|
||||
| Windows | 1.25 | amd64 |
|
||||
| Windows | 1.27 | 386 |
|
||||
| Windows | 1.26 | 386 |
|
||||
| Windows | 1.25 | 386 |
|
||||
|
||||
While this project should work for other systems, no compatibility guarantees
|
||||
are made for those systems currently.
|
||||
@@ -110,3 +120,10 @@ All officially supported exporters for the OpenTelemetry project are contained i
|
||||
## Contributing
|
||||
|
||||
See the [contributing documentation](CONTRIBUTING.md).
|
||||
|
||||
### Emeritus
|
||||
|
||||
- [Alex Kats](https://github.com/akats7), Triager
|
||||
|
||||
For more information about the emeritus role, see the
|
||||
[community repository](https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md#emeritus-maintainerapprovertriager).
|
||||
|
||||
-3
@@ -21,7 +21,6 @@ func New() Hash {
|
||||
return Hash{d: xxhash.New()}
|
||||
}
|
||||
|
||||
// Uint64 hashes val into h, returning the updated Hash.
|
||||
func (h Hash) Uint64(val uint64) Hash {
|
||||
var buf [8]byte
|
||||
binary.LittleEndian.PutUint64(buf[:], val)
|
||||
@@ -41,12 +40,10 @@ func (h Hash) Bool(val bool) Hash { // nolint:revive // This is a hashing functi
|
||||
return h.Uint64(0)
|
||||
}
|
||||
|
||||
// Float64 hashes val into h, returning the updated Hash.
|
||||
func (h Hash) Float64(val float64) Hash {
|
||||
return h.Uint64(math.Float64bits(val))
|
||||
}
|
||||
|
||||
// Int64 hashes val into h, returning the updated Hash.
|
||||
func (h Hash) Int64(val int64) Hash {
|
||||
return h.Uint64(uint64(val)) // nolint:gosec // Overflow doesn't matter since we are hashing.
|
||||
}
|
||||
|
||||
-10
@@ -13,16 +13,6 @@ type KeyValue struct {
|
||||
Value Value
|
||||
}
|
||||
|
||||
// String returns a string representation of KeyValue using the
|
||||
// [OpenTelemetry Attribute representation for non-OTLP protocols] rules.
|
||||
//
|
||||
// The KeyValue is encoded as a JSON object containing a single key-value pair.
|
||||
//
|
||||
// [OpenTelemetry Attribute representation for non-OTLP protocols]: https://opentelemetry.io/docs/specs/otel/common/#attribute-representation-for-non-otlp
|
||||
func (kv KeyValue) String() string {
|
||||
return formatMap([]KeyValue{kv})
|
||||
}
|
||||
|
||||
// Valid reports whether kv is a valid OpenTelemetry attribute.
|
||||
func (kv KeyValue) Valid() bool {
|
||||
return kv.Key.Defined()
|
||||
|
||||
-15
@@ -409,21 +409,6 @@ func computeDataReflect(kvs []KeyValue) any {
|
||||
return at.Interface()
|
||||
}
|
||||
|
||||
// String returns a string representation of the Set using the
|
||||
// [OpenTelemetry Attribute Collection representation for non-OTLP protocols]
|
||||
// rules.
|
||||
//
|
||||
// The Set is encoded as a JSON object. A nil or empty Set is encoded as an
|
||||
// empty JSON object.
|
||||
//
|
||||
// [OpenTelemetry Attribute Collection representation for non-OTLP protocols]: https://opentelemetry.io/docs/specs/otel/common/#attribute-collection-representation-for-non-otlp
|
||||
func (l *Set) String() string {
|
||||
if l == nil || l.hash == 0 {
|
||||
return "{}"
|
||||
}
|
||||
return formatMapValue(l.data)
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding of the Set.
|
||||
func (l *Set) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(l.data)
|
||||
|
||||
+68
-167
@@ -247,78 +247,10 @@ func (p properties) String() string {
|
||||
return strings.Join(props, propertyDelimiter)
|
||||
}
|
||||
|
||||
// validateMetadata validates non-empty W3C property elements without decoding
|
||||
// or materializing Property values. Empty elements are accepted to preserve
|
||||
// existing parsing behavior.
|
||||
func validateMetadata(raw string) (string, bool) {
|
||||
start := 0
|
||||
for end := 0; end <= len(raw); end++ {
|
||||
if end != len(raw) && raw[end] != propertyDelimiter[0] {
|
||||
continue
|
||||
}
|
||||
|
||||
if start != end {
|
||||
if _, ok := parsePropertyFields(raw[start:end]); !ok {
|
||||
return raw[start:end], false
|
||||
}
|
||||
}
|
||||
start = end + 1
|
||||
}
|
||||
return "", true
|
||||
}
|
||||
|
||||
// normalizeMetadata removes empty property elements while preserving the raw
|
||||
// representation of all non-empty elements.
|
||||
func normalizeMetadata(metadata string) string {
|
||||
if metadata == "" ||
|
||||
(metadata[0] != propertyDelimiter[0] &&
|
||||
metadata[len(metadata)-1] != propertyDelimiter[0] &&
|
||||
!strings.Contains(metadata, propertyDelimiter+propertyDelimiter)) {
|
||||
return metadata
|
||||
}
|
||||
|
||||
var normalized strings.Builder
|
||||
normalized.Grow(len(metadata))
|
||||
for property := range strings.SplitSeq(metadata, propertyDelimiter) {
|
||||
if property == "" {
|
||||
continue
|
||||
}
|
||||
if normalized.Len() > 0 {
|
||||
_, _ = normalized.WriteString(propertyDelimiter)
|
||||
}
|
||||
_, _ = normalized.WriteString(property)
|
||||
}
|
||||
return normalized.String()
|
||||
}
|
||||
|
||||
func propertiesFromMetadata(metadata string) properties {
|
||||
if metadata == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
props := make(properties, 0, strings.Count(metadata, propertyDelimiter)+1)
|
||||
for pStr := range strings.SplitSeq(metadata, propertyDelimiter) {
|
||||
if pStr == "" {
|
||||
continue
|
||||
}
|
||||
p, err := parseProperty(pStr)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
props = append(props, p)
|
||||
}
|
||||
return props
|
||||
}
|
||||
|
||||
// Member is a list-member of a baggage-string as defined by the W3C Baggage
|
||||
// specification.
|
||||
type Member struct {
|
||||
key, value string
|
||||
|
||||
// metadata contains the opaque W3C property suffix extracted from the wire
|
||||
// without the leading semicolon. properties contains API-created
|
||||
// properties. The fields are mutually exclusive.
|
||||
metadata string
|
||||
properties properties
|
||||
|
||||
// hasData indicates whether the created property contains data or not.
|
||||
@@ -375,16 +307,21 @@ func newInvalidMember() Member {
|
||||
return Member{}
|
||||
}
|
||||
|
||||
// parseMember attempts to decode a Member from the passed string. Property
|
||||
// metadata grammar is validated without decoding or materializing properties.
|
||||
// parseMember attempts to decode a Member from the passed string. It returns
|
||||
// an error if the input is invalid according to the W3C Baggage
|
||||
// specification.
|
||||
func parseMember(member string) (Member, error) {
|
||||
var metadata string
|
||||
keyValue, rawMetadata, found := strings.Cut(member, propertyDelimiter)
|
||||
var props properties
|
||||
keyValue, properties, found := strings.Cut(member, propertyDelimiter)
|
||||
if found {
|
||||
if invalid, ok := validateMetadata(rawMetadata); !ok {
|
||||
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidProperty, invalid)
|
||||
// Parse the member properties.
|
||||
for pStr := range strings.SplitSeq(properties, propertyDelimiter) {
|
||||
p, err := parseProperty(pStr)
|
||||
if err != nil {
|
||||
return newInvalidMember(), err
|
||||
}
|
||||
props = append(props, p)
|
||||
}
|
||||
metadata = normalizeMetadata(rawMetadata)
|
||||
}
|
||||
// Parse the member key/value pair.
|
||||
|
||||
@@ -412,7 +349,7 @@ func parseMember(member string) (Member, error) {
|
||||
}
|
||||
|
||||
value := replaceInvalidUTF8Sequences(len(rawVal), unescapeVal)
|
||||
return Member{key: key, value: value, metadata: metadata, hasData: true}, nil
|
||||
return Member{key: key, value: value, properties: props, hasData: true}, nil
|
||||
}
|
||||
|
||||
// replaceInvalidUTF8Sequences replaces invalid UTF-8 sequences with '�'.
|
||||
@@ -462,14 +399,10 @@ func (m Member) Key() string { return m.key }
|
||||
func (m Member) Value() string { return m.value }
|
||||
|
||||
// Properties returns a copy of the Member properties.
|
||||
func (m Member) Properties() []Property {
|
||||
if m.metadata != "" {
|
||||
return propertiesFromMetadata(m.metadata)
|
||||
}
|
||||
return m.properties.Copy()
|
||||
}
|
||||
func (m Member) Properties() []Property { return m.properties.Copy() }
|
||||
|
||||
// String encodes Member into a baggage header string.
|
||||
// String encodes Member into a header string compliant with the W3C Baggage
|
||||
// specification.
|
||||
// It would return empty string if the key is invalid with the W3C Baggage
|
||||
// specification. This could happen for a UTF-8 key, as it may contain
|
||||
// invalid characters.
|
||||
@@ -480,31 +413,12 @@ func (m Member) String() string {
|
||||
}
|
||||
|
||||
s := m.key + keyValueDelimiter + valueEscape(m.value)
|
||||
if m.metadata != "" {
|
||||
s += propertyDelimiter + m.metadata
|
||||
} else if len(m.properties) > 0 {
|
||||
if len(m.properties) > 0 {
|
||||
s += propertyDelimiter + m.properties.String()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func memberFromItem(key string, item baggage.Item) Member {
|
||||
return Member{
|
||||
key: key,
|
||||
value: item.Value(),
|
||||
metadata: item.Metadata(),
|
||||
properties: fromInternalProperties(item.Properties()),
|
||||
hasData: true,
|
||||
}
|
||||
}
|
||||
|
||||
func itemFromMember(member Member) baggage.Item {
|
||||
if member.metadata != "" {
|
||||
return baggage.NewItemWithMetadata(member.value, member.metadata)
|
||||
}
|
||||
return baggage.NewItemWithProperties(member.value, member.properties.asInternal())
|
||||
}
|
||||
|
||||
// Baggage is a list of baggage members representing the baggage-string as
|
||||
// defined by the W3C Baggage specification.
|
||||
type Baggage struct { //nolint:golint
|
||||
@@ -530,7 +444,10 @@ func New(members ...Member) (Baggage, error) {
|
||||
return Baggage{}, errInvalidMember
|
||||
}
|
||||
// OpenTelemetry resolves duplicates by last-one-wins.
|
||||
b[m.key] = itemFromMember(m)
|
||||
b[m.key] = baggage.Item{
|
||||
Value: m.value,
|
||||
Properties: m.properties.asInternal(),
|
||||
}
|
||||
}
|
||||
|
||||
var truncateErr error
|
||||
@@ -550,7 +467,11 @@ func New(members ...Member) (Baggage, error) {
|
||||
totalBytes := 0
|
||||
first := true
|
||||
for k := range b {
|
||||
m := memberFromItem(k, b[k])
|
||||
m := Member{
|
||||
key: k,
|
||||
value: b[k].Value,
|
||||
properties: fromInternalProperties(b[k].Properties),
|
||||
}
|
||||
memberSize := len(m.String())
|
||||
if !first {
|
||||
memberSize++ // comma separator
|
||||
@@ -642,7 +563,10 @@ func Parse(bStr string) (Baggage, error) {
|
||||
}
|
||||
|
||||
// OpenTelemetry resolves duplicates by last-one-wins.
|
||||
b[m.key] = itemFromMember(m)
|
||||
b[m.key] = baggage.Item{
|
||||
Value: m.value,
|
||||
Properties: m.properties.asInternal(),
|
||||
}
|
||||
sizes[m.key] = memberBytes
|
||||
totalBytes = newTotalBytes
|
||||
}
|
||||
@@ -673,7 +597,12 @@ func (b Baggage) Member(key string) Member {
|
||||
return newInvalidMember()
|
||||
}
|
||||
|
||||
return memberFromItem(key, v)
|
||||
return Member{
|
||||
key: key,
|
||||
value: v.Value,
|
||||
properties: fromInternalProperties(v.Properties),
|
||||
hasData: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Members returns all the baggage list-members.
|
||||
@@ -688,7 +617,12 @@ func (b Baggage) Members() []Member {
|
||||
|
||||
members := make([]Member, 0, len(b.list))
|
||||
for k, v := range b.list {
|
||||
members = append(members, memberFromItem(k, v))
|
||||
members = append(members, Member{
|
||||
key: k,
|
||||
value: v.Value,
|
||||
properties: fromInternalProperties(v.Properties),
|
||||
hasData: true,
|
||||
})
|
||||
}
|
||||
return members
|
||||
}
|
||||
@@ -718,7 +652,10 @@ func (b Baggage) SetMember(member Member) (Baggage, error) {
|
||||
list[k] = v
|
||||
}
|
||||
|
||||
list[member.key] = itemFromMember(member)
|
||||
list[member.key] = baggage.Item{
|
||||
Value: member.value,
|
||||
Properties: member.properties.asInternal(),
|
||||
}
|
||||
|
||||
return Baggage{list: list}, nil
|
||||
}
|
||||
@@ -755,7 +692,11 @@ func (b Baggage) Len() int {
|
||||
func (b Baggage) String() string {
|
||||
members := make([]string, 0, len(b.list))
|
||||
for k, v := range b.list {
|
||||
s := memberFromItem(k, v).String()
|
||||
s := Member{
|
||||
key: k,
|
||||
value: v.Value,
|
||||
properties: fromInternalProperties(v.Properties),
|
||||
}.String()
|
||||
|
||||
// Ignored empty members.
|
||||
if s != "" {
|
||||
@@ -765,14 +706,9 @@ func (b Baggage) String() string {
|
||||
return strings.Join(members, listDelimiter)
|
||||
}
|
||||
|
||||
type propertyFields struct {
|
||||
key, rawValue string
|
||||
hasValue bool
|
||||
}
|
||||
|
||||
// parsePropertyFields validates and locates a property's fields without
|
||||
// decoding or allocating them.
|
||||
func parsePropertyFields(s string) (fields propertyFields, ok bool) {
|
||||
// parsePropertyInternal attempts to decode a Property from the passed string.
|
||||
// It follows the spec at https://www.w3.org/TR/baggage/#definition.
|
||||
func parsePropertyInternal(s string) (p Property, ok bool) {
|
||||
// For the entire function we will use " key = value " as an example.
|
||||
// Attempting to parse the key.
|
||||
// First skip spaces at the beginning "< >key = value " (they could be empty).
|
||||
@@ -791,7 +727,7 @@ func parsePropertyFields(s string) (fields propertyFields, ok bool) {
|
||||
// If we couldn't find any valid key character,
|
||||
// it means the key is either empty or invalid.
|
||||
if keyStart == keyEnd {
|
||||
return fields, ok
|
||||
return p, ok
|
||||
}
|
||||
|
||||
// Skip spaces after the key: " key< >= value ".
|
||||
@@ -799,14 +735,15 @@ func parsePropertyFields(s string) (fields propertyFields, ok bool) {
|
||||
|
||||
if index == len(s) {
|
||||
// A key can have no value, like: " key ".
|
||||
fields.key = s[keyStart:keyEnd]
|
||||
return fields, true
|
||||
ok = true
|
||||
p.key = s[keyStart:keyEnd]
|
||||
return p, ok
|
||||
}
|
||||
|
||||
// If we have not reached the end and we can't find the '=' delimiter,
|
||||
// it means the property is invalid.
|
||||
if s[index] != keyValueDelimiter[0] {
|
||||
return fields, ok
|
||||
return p, ok
|
||||
}
|
||||
|
||||
// Attempting to parse the value.
|
||||
@@ -832,59 +769,23 @@ func parsePropertyFields(s string) (fields propertyFields, ok bool) {
|
||||
// we have not reached the end, it means the property is
|
||||
// invalid, something like: " key = value value1".
|
||||
if index != len(s) {
|
||||
return fields, ok
|
||||
}
|
||||
rawVal := s[valueStart:valueEnd]
|
||||
if !validPercentEncoding(rawVal) {
|
||||
return fields, ok
|
||||
}
|
||||
|
||||
fields.key = s[keyStart:keyEnd]
|
||||
fields.rawValue = rawVal
|
||||
fields.hasValue = true
|
||||
return fields, true
|
||||
}
|
||||
|
||||
func validPercentEncoding(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] != '%' {
|
||||
continue
|
||||
}
|
||||
if i+2 >= len(s) || !isHex(s[i+1]) || !isHex(s[i+2]) {
|
||||
return false
|
||||
}
|
||||
i += 2
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isHex(c byte) bool {
|
||||
return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F'
|
||||
}
|
||||
|
||||
// parsePropertyInternal attempts to decode a Property from the passed string.
|
||||
// It follows the spec at https://www.w3.org/TR/baggage/#definition.
|
||||
func parsePropertyInternal(s string) (p Property, ok bool) {
|
||||
fields, ok := parsePropertyFields(s)
|
||||
if !ok {
|
||||
return p, false
|
||||
}
|
||||
if !fields.hasValue {
|
||||
p.key = fields.key
|
||||
return p, true
|
||||
return p, ok
|
||||
}
|
||||
|
||||
// Decode a percent-encoded value.
|
||||
unescapeVal, err := url.PathUnescape(fields.rawValue)
|
||||
rawVal := s[valueStart:valueEnd]
|
||||
unescapeVal, err := url.PathUnescape(rawVal)
|
||||
if err != nil {
|
||||
return p, false
|
||||
return p, ok
|
||||
}
|
||||
value := replaceInvalidUTF8Sequences(len(fields.rawValue), unescapeVal)
|
||||
value := replaceInvalidUTF8Sequences(len(rawVal), unescapeVal)
|
||||
|
||||
p.key = fields.key
|
||||
ok = true
|
||||
p.key = s[keyStart:keyEnd]
|
||||
p.hasValue = true
|
||||
|
||||
p.value = value
|
||||
return p, true
|
||||
return p, ok
|
||||
}
|
||||
|
||||
func skipSpace(s string, offset int) int {
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ const (
|
||||
maxCode = 3
|
||||
)
|
||||
|
||||
// Code is a 32-bit representation of a status state.
|
||||
// Code is an 32-bit representation of a status state.
|
||||
type Code uint32
|
||||
|
||||
var codeToStr = map[Code]string{
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# This is a renovate-friendly source of Docker images.
|
||||
FROM python:3.13.6-slim-bullseye@sha256:e98b521460ee75bca92175c16247bdf7275637a8faaeb2bcfa19d879ae5c4b9a AS python
|
||||
FROM otel/weaver:v0.26.1@sha256:9094862c0ab261bdbcb079bb981f9a573b3659b130a6d2ab8616eca6ba37aaec AS weaver
|
||||
FROM otel/weaver:v0.25.1@sha256:9ad46ca9cd4fa5974b121f886aa3e9946a8ef8ea905001a96c018d21f9db87ca AS weaver
|
||||
FROM avtodev/markdown-lint:v1@sha256:6aeedc2f49138ce7a1cd0adffc1b1c0321b841dc2102408967d9301c031949ee AS markdown
|
||||
|
||||
Generated
Vendored
+2
-2
@@ -18,8 +18,8 @@ package internal
|
||||
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/envconfig.go.tmpl "--data={\"envconfigImportPath\": \"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/envconfig\"}" --out=oconf/envconfig.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/envconfig_test.go.tmpl "--data={}" --out=oconf/envconfig_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/options.go.tmpl "--data={\"isHTTP\": false, \"retryImportPath\": \"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/retry\"}" --out=oconf/options.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/options_test.go.tmpl "--data={\"isHTTP\": false, \"envconfigImportPath\": \"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/envconfig\"}" --out=oconf/options_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/options.go.tmpl "--data={\"retryImportPath\": \"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/retry\"}" --out=oconf/options.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/options_test.go.tmpl "--data={\"envconfigImportPath\": \"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/envconfig\"}" --out=oconf/options_test.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/optiontypes.go.tmpl "--data={}" --out=oconf/optiontypes.go
|
||||
//go:generate gotmpl --body=../../../../../internal/shared/otlp/otlpmetric/oconf/tls.go.tmpl "--data={}" --out=oconf/tls.go
|
||||
|
||||
|
||||
vendor/go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/oconf/envconfig.go
Generated
Vendored
+2
-13
@@ -121,22 +121,11 @@ func withEndpointForGRPC(u *url.URL) func(cfg Config) Config {
|
||||
return func(cfg Config) Config {
|
||||
// For OTLP/gRPC endpoints, this is the target to which the
|
||||
// exporter is going to send telemetry.
|
||||
cfg.Metrics.Endpoint = grpcEndpoint(u)
|
||||
cfg.Metrics.Endpoint = path.Join(u.Host, u.Path)
|
||||
return cfg
|
||||
}
|
||||
}
|
||||
|
||||
func grpcEndpoint(u *url.URL) string {
|
||||
switch strings.ToLower(u.Scheme) {
|
||||
case "http", "https":
|
||||
return u.Host
|
||||
case "":
|
||||
return path.Join(u.Host, u.Path)
|
||||
default:
|
||||
return u.String()
|
||||
}
|
||||
}
|
||||
|
||||
// WithEnvCompression retrieves the specified config and passes it to ConfigFn as a Compression.
|
||||
func WithEnvCompression(n string, fn func(Compression)) func(e *envconfig.EnvOptionsReader) {
|
||||
return func(e *envconfig.EnvOptionsReader) {
|
||||
@@ -153,7 +142,7 @@ func WithEnvCompression(n string, fn func(Compression)) func(e *envconfig.EnvOpt
|
||||
|
||||
func withEndpointScheme(u *url.URL) GenericOption {
|
||||
switch strings.ToLower(u.Scheme) {
|
||||
case "http", "unix", "unix-abstract":
|
||||
case "http", "unix":
|
||||
return WithInsecure()
|
||||
default:
|
||||
return WithSecure()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user