mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-05-17 16:37:46 +08:00
use cli-plugins metadata package
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
db4b96e62c
commit
391acba718
@ -11,7 +11,7 @@ import (
|
||||
"github.com/docker/buildx/util/desktop"
|
||||
"github.com/docker/buildx/version"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli-plugins/plugin"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/debug"
|
||||
@ -64,7 +64,7 @@ func flushMetrics(cmd *command.DockerCli) {
|
||||
|
||||
func runPlugin(cmd *command.DockerCli) error {
|
||||
rootCmd := commands.NewRootCmd("buildx", true, cmd)
|
||||
return plugin.RunPlugin(cmd, rootCmd, manager.Metadata{
|
||||
return plugin.RunPlugin(cmd, rootCmd, metadata.Metadata{
|
||||
SchemaVersion: "0.1.0",
|
||||
Vendor: "Docker Inc.",
|
||||
Version: version.Version,
|
||||
|
18
vendor/github.com/docker/cli/cli-plugins/hooks/printer.go
generated
vendored
18
vendor/github.com/docker/cli/cli-plugins/hooks/printer.go
generated
vendored
@ -1,18 +0,0 @@
|
||||
package hooks
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/morikuni/aec"
|
||||
)
|
||||
|
||||
func PrintNextSteps(out io.Writer, messages []string) {
|
||||
if len(messages) == 0 {
|
||||
return
|
||||
}
|
||||
_, _ = fmt.Fprintln(out, aec.Bold.Apply("\nWhat's next:"))
|
||||
for _, n := range messages {
|
||||
_, _ = fmt.Fprintln(out, " ", n)
|
||||
}
|
||||
}
|
116
vendor/github.com/docker/cli/cli-plugins/hooks/template.go
generated
vendored
116
vendor/github.com/docker/cli/cli-plugins/hooks/template.go
generated
vendored
@ -1,116 +0,0 @@
|
||||
package hooks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type HookType int
|
||||
|
||||
const (
|
||||
NextSteps = iota
|
||||
)
|
||||
|
||||
// HookMessage represents a plugin hook response. Plugins
|
||||
// declaring support for CLI hooks need to print a json
|
||||
// representation of this type when their hook subcommand
|
||||
// is invoked.
|
||||
type HookMessage struct {
|
||||
Type HookType
|
||||
Template string
|
||||
}
|
||||
|
||||
// TemplateReplaceSubcommandName returns a hook template string
|
||||
// that will be replaced by the CLI subcommand being executed
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// "you ran the subcommand: " + TemplateReplaceSubcommandName()
|
||||
//
|
||||
// when being executed after the command:
|
||||
// `docker run --name "my-container" alpine`
|
||||
// will result in the message:
|
||||
// `you ran the subcommand: run`
|
||||
func TemplateReplaceSubcommandName() string {
|
||||
return hookTemplateCommandName
|
||||
}
|
||||
|
||||
// TemplateReplaceFlagValue returns a hook template string
|
||||
// that will be replaced by the flags value.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// "you ran a container named: " + TemplateReplaceFlagValue("name")
|
||||
//
|
||||
// when being executed after the command:
|
||||
// `docker run --name "my-container" alpine`
|
||||
// will result in the message:
|
||||
// `you ran a container named: my-container`
|
||||
func TemplateReplaceFlagValue(flag string) string {
|
||||
return fmt.Sprintf(hookTemplateFlagValue, flag)
|
||||
}
|
||||
|
||||
// TemplateReplaceArg takes an index i and returns a hook
|
||||
// template string that the CLI will replace the template with
|
||||
// the ith argument, after processing the passed flags.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// "run this image with `docker run " + TemplateReplaceArg(0) + "`"
|
||||
//
|
||||
// when being executed after the command:
|
||||
// `docker pull alpine`
|
||||
// will result in the message:
|
||||
// "Run this image with `docker run alpine`"
|
||||
func TemplateReplaceArg(i int) string {
|
||||
return fmt.Sprintf(hookTemplateArg, strconv.Itoa(i))
|
||||
}
|
||||
|
||||
func ParseTemplate(hookTemplate string, cmd *cobra.Command) ([]string, error) {
|
||||
tmpl := template.New("").Funcs(commandFunctions)
|
||||
tmpl, err := tmpl.Parse(hookTemplate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := bytes.Buffer{}
|
||||
err = tmpl.Execute(&b, cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return strings.Split(b.String(), "\n"), nil
|
||||
}
|
||||
|
||||
var ErrHookTemplateParse = errors.New("failed to parse hook template")
|
||||
|
||||
const (
|
||||
hookTemplateCommandName = "{{.Name}}"
|
||||
hookTemplateFlagValue = `{{flag . "%s"}}`
|
||||
hookTemplateArg = "{{arg . %s}}"
|
||||
)
|
||||
|
||||
var commandFunctions = template.FuncMap{
|
||||
"flag": getFlagValue,
|
||||
"arg": getArgValue,
|
||||
}
|
||||
|
||||
func getFlagValue(cmd *cobra.Command, flag string) (string, error) {
|
||||
cmdFlag := cmd.Flag(flag)
|
||||
if cmdFlag == nil {
|
||||
return "", ErrHookTemplateParse
|
||||
}
|
||||
return cmdFlag.Value.String(), nil
|
||||
}
|
||||
|
||||
func getArgValue(cmd *cobra.Command, i int) (string, error) {
|
||||
flags := cmd.Flags()
|
||||
if flags == nil {
|
||||
return "", ErrHookTemplateParse
|
||||
}
|
||||
return flags.Arg(i), nil
|
||||
}
|
30
vendor/github.com/docker/cli/cli-plugins/manager/annotations.go
generated
vendored
30
vendor/github.com/docker/cli/cli-plugins/manager/annotations.go
generated
vendored
@ -1,30 +0,0 @@
|
||||
package manager
|
||||
|
||||
import "github.com/docker/cli/cli-plugins/metadata"
|
||||
|
||||
const (
|
||||
// CommandAnnotationPlugin is added to every stub command added by
|
||||
// AddPluginCommandStubs with the value "true" and so can be
|
||||
// used to distinguish plugin stubs from regular commands.
|
||||
CommandAnnotationPlugin = metadata.CommandAnnotationPlugin
|
||||
|
||||
// CommandAnnotationPluginVendor is added to every stub command
|
||||
// added by AddPluginCommandStubs and contains the vendor of
|
||||
// that plugin.
|
||||
CommandAnnotationPluginVendor = metadata.CommandAnnotationPluginVendor
|
||||
|
||||
// CommandAnnotationPluginVersion is added to every stub command
|
||||
// added by AddPluginCommandStubs and contains the version of
|
||||
// that plugin.
|
||||
CommandAnnotationPluginVersion = metadata.CommandAnnotationPluginVersion
|
||||
|
||||
// CommandAnnotationPluginInvalid is added to any stub command
|
||||
// added by AddPluginCommandStubs for an invalid command (that
|
||||
// is, one which failed it's candidate test) and contains the
|
||||
// reason for the failure.
|
||||
CommandAnnotationPluginInvalid = metadata.CommandAnnotationPluginInvalid
|
||||
|
||||
// CommandAnnotationPluginCommandPath is added to overwrite the
|
||||
// command path for a plugin invocation.
|
||||
CommandAnnotationPluginCommandPath = metadata.CommandAnnotationPluginCommandPath
|
||||
)
|
25
vendor/github.com/docker/cli/cli-plugins/manager/candidate.go
generated
vendored
25
vendor/github.com/docker/cli/cli-plugins/manager/candidate.go
generated
vendored
@ -1,25 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
)
|
||||
|
||||
// Candidate represents a possible plugin candidate, for mocking purposes
|
||||
type Candidate interface {
|
||||
Path() string
|
||||
Metadata() ([]byte, error)
|
||||
}
|
||||
|
||||
type candidate struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (c *candidate) Path() string {
|
||||
return c.path
|
||||
}
|
||||
|
||||
func (c *candidate) Metadata() ([]byte, error) {
|
||||
return exec.Command(c.path, metadata.MetadataSubcommandName).Output() // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
|
||||
}
|
77
vendor/github.com/docker/cli/cli-plugins/manager/cobra.go
generated
vendored
77
vendor/github.com/docker/cli/cli-plugins/manager/cobra.go
generated
vendored
@ -1,77 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var pluginCommandStubsOnce sync.Once
|
||||
|
||||
// AddPluginCommandStubs adds a stub cobra.Commands for each valid and invalid
|
||||
// plugin. The command stubs will have several annotations added, see
|
||||
// `CommandAnnotationPlugin*`.
|
||||
func AddPluginCommandStubs(dockerCLI config.Provider, rootCmd *cobra.Command) (err error) {
|
||||
pluginCommandStubsOnce.Do(func() {
|
||||
var plugins []Plugin
|
||||
plugins, err = ListPlugins(dockerCLI, rootCmd)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, p := range plugins {
|
||||
vendor := p.Vendor
|
||||
if vendor == "" {
|
||||
vendor = "unknown"
|
||||
}
|
||||
annotations := map[string]string{
|
||||
metadata.CommandAnnotationPlugin: "true",
|
||||
metadata.CommandAnnotationPluginVendor: vendor,
|
||||
metadata.CommandAnnotationPluginVersion: p.Version,
|
||||
}
|
||||
if p.Err != nil {
|
||||
annotations[metadata.CommandAnnotationPluginInvalid] = p.Err.Error()
|
||||
}
|
||||
rootCmd.AddCommand(&cobra.Command{
|
||||
Use: p.Name,
|
||||
Short: p.ShortDescription,
|
||||
Run: func(_ *cobra.Command, _ []string) {},
|
||||
Annotations: annotations,
|
||||
DisableFlagParsing: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
flags := rootCmd.PersistentFlags()
|
||||
flags.SetOutput(nil)
|
||||
perr := flags.Parse(args)
|
||||
if perr != nil {
|
||||
return err
|
||||
}
|
||||
if flags.Changed("help") {
|
||||
cmd.HelpFunc()(rootCmd, args)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("docker: unknown command: docker %s\n\nRun 'docker --help' for more information", cmd.Name())
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
// Delegate completion to plugin
|
||||
cargs := []string{p.Path, cobra.ShellCompRequestCmd, p.Name}
|
||||
cargs = append(cargs, args...)
|
||||
cargs = append(cargs, toComplete)
|
||||
os.Args = cargs
|
||||
runCommand, runErr := PluginRunCommand(dockerCLI, p.Name, cmd)
|
||||
if runErr != nil {
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
runErr = runCommand.Run()
|
||||
if runErr == nil {
|
||||
os.Exit(0) // plugin already rendered complete data
|
||||
}
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
return err
|
||||
}
|
54
vendor/github.com/docker/cli/cli-plugins/manager/error.go
generated
vendored
54
vendor/github.com/docker/cli/cli-plugins/manager/error.go
generated
vendored
@ -1,54 +0,0 @@
|
||||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.22
|
||||
|
||||
package manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// pluginError is set as Plugin.Err by NewPlugin if the plugin
|
||||
// candidate fails one of the candidate tests. This exists primarily
|
||||
// to implement encoding.TextMarshaller such that rendering a plugin as JSON
|
||||
// (e.g. for `docker info -f '{{json .CLIPlugins}}'`) renders the Err
|
||||
// field as a useful string and not just `{}`. See
|
||||
// https://github.com/golang/go/issues/10748 for some discussion
|
||||
// around why the builtin error type doesn't implement this.
|
||||
type pluginError struct {
|
||||
cause error
|
||||
}
|
||||
|
||||
// Error satisfies the core error interface for pluginError.
|
||||
func (e *pluginError) Error() string {
|
||||
return e.cause.Error()
|
||||
}
|
||||
|
||||
// Cause satisfies the errors.causer interface for pluginError.
|
||||
func (e *pluginError) Cause() error {
|
||||
return e.cause
|
||||
}
|
||||
|
||||
// Unwrap provides compatibility for Go 1.13 error chains.
|
||||
func (e *pluginError) Unwrap() error {
|
||||
return e.cause
|
||||
}
|
||||
|
||||
// MarshalText marshalls the pluginError into a textual form.
|
||||
func (e *pluginError) MarshalText() (text []byte, err error) {
|
||||
return []byte(e.cause.Error()), nil
|
||||
}
|
||||
|
||||
// wrapAsPluginError wraps an error in a pluginError with an
|
||||
// additional message.
|
||||
func wrapAsPluginError(err error, msg string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &pluginError{cause: fmt.Errorf("%s: %w", msg, err)}
|
||||
}
|
||||
|
||||
// NewPluginError creates a new pluginError, analogous to
|
||||
// errors.Errorf.
|
||||
func NewPluginError(msg string, args ...any) error {
|
||||
return &pluginError{cause: fmt.Errorf(msg, args...)}
|
||||
}
|
203
vendor/github.com/docker/cli/cli-plugins/manager/hooks.go
generated
vendored
203
vendor/github.com/docker/cli/cli-plugins/manager/hooks.go
generated
vendored
@ -1,203 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli-plugins/hooks"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// HookPluginData is the type representing the information
|
||||
// that plugins declaring support for hooks get passed when
|
||||
// being invoked following a CLI command execution.
|
||||
type HookPluginData struct {
|
||||
// RootCmd is a string representing the matching hook configuration
|
||||
// which is currently being invoked. If a hook for `docker context` is
|
||||
// configured and the user executes `docker context ls`, the plugin will
|
||||
// be invoked with `context`.
|
||||
RootCmd string
|
||||
Flags map[string]string
|
||||
CommandError string
|
||||
}
|
||||
|
||||
// RunCLICommandHooks is the entrypoint into the hooks execution flow after
|
||||
// a main CLI command was executed. It calls the hook subcommand for all
|
||||
// present CLI plugins that declare support for hooks in their metadata and
|
||||
// parses/prints their responses.
|
||||
func RunCLICommandHooks(ctx context.Context, dockerCLI config.Provider, rootCmd, subCommand *cobra.Command, cmdErrorMessage string) {
|
||||
commandName := strings.TrimPrefix(subCommand.CommandPath(), rootCmd.Name()+" ")
|
||||
flags := getCommandFlags(subCommand)
|
||||
|
||||
runHooks(ctx, dockerCLI.ConfigFile(), rootCmd, subCommand, commandName, flags, cmdErrorMessage)
|
||||
}
|
||||
|
||||
// RunPluginHooks is the entrypoint for the hooks execution flow
|
||||
// after a plugin command was just executed by the CLI.
|
||||
func RunPluginHooks(ctx context.Context, dockerCLI config.Provider, rootCmd, subCommand *cobra.Command, args []string) {
|
||||
commandName := strings.Join(args, " ")
|
||||
flags := getNaiveFlags(args)
|
||||
|
||||
runHooks(ctx, dockerCLI.ConfigFile(), rootCmd, subCommand, commandName, flags, "")
|
||||
}
|
||||
|
||||
func runHooks(ctx context.Context, cfg *configfile.ConfigFile, rootCmd, subCommand *cobra.Command, invokedCommand string, flags map[string]string, cmdErrorMessage string) {
|
||||
nextSteps := invokeAndCollectHooks(ctx, cfg, rootCmd, subCommand, invokedCommand, flags, cmdErrorMessage)
|
||||
hooks.PrintNextSteps(subCommand.ErrOrStderr(), nextSteps)
|
||||
}
|
||||
|
||||
func invokeAndCollectHooks(ctx context.Context, cfg *configfile.ConfigFile, rootCmd, subCmd *cobra.Command, subCmdStr string, flags map[string]string, cmdErrorMessage string) []string {
|
||||
// check if the context was cancelled before invoking hooks
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
pluginsCfg := cfg.Plugins
|
||||
if pluginsCfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
pluginDirs, err := getPluginDirs(cfg)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
nextSteps := make([]string, 0, len(pluginsCfg))
|
||||
for pluginName, cfg := range pluginsCfg {
|
||||
match, ok := pluginMatch(cfg, subCmdStr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
p, err := getPlugin(pluginName, pluginDirs, rootCmd)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
hookReturn, err := p.RunHook(ctx, HookPluginData{
|
||||
RootCmd: match,
|
||||
Flags: flags,
|
||||
CommandError: cmdErrorMessage,
|
||||
})
|
||||
if err != nil {
|
||||
// skip misbehaving plugins, but don't halt execution
|
||||
continue
|
||||
}
|
||||
|
||||
var hookMessageData hooks.HookMessage
|
||||
err = json.Unmarshal(hookReturn, &hookMessageData)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// currently the only hook type
|
||||
if hookMessageData.Type != hooks.NextSteps {
|
||||
continue
|
||||
}
|
||||
|
||||
processedHook, err := hooks.ParseTemplate(hookMessageData.Template, subCmd)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var appended bool
|
||||
nextSteps, appended = appendNextSteps(nextSteps, processedHook)
|
||||
if !appended {
|
||||
logrus.Debugf("Plugin %s responded with an empty hook message %q. Ignoring.", pluginName, string(hookReturn))
|
||||
}
|
||||
}
|
||||
return nextSteps
|
||||
}
|
||||
|
||||
// appendNextSteps appends the processed hook output to the nextSteps slice.
|
||||
// If the processed hook output is empty, it is not appended.
|
||||
// Empty lines are not stripped if there's at least one non-empty line.
|
||||
func appendNextSteps(nextSteps []string, processed []string) ([]string, bool) {
|
||||
empty := true
|
||||
for _, l := range processed {
|
||||
if strings.TrimSpace(l) != "" {
|
||||
empty = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if empty {
|
||||
return nextSteps, false
|
||||
}
|
||||
|
||||
return append(nextSteps, processed...), true
|
||||
}
|
||||
|
||||
// pluginMatch takes a plugin configuration and a string representing the
|
||||
// command being executed (such as 'image ls' – the root 'docker' is omitted)
|
||||
// and, if the configuration includes a hook for the invoked command, returns
|
||||
// the configured hook string.
|
||||
func pluginMatch(pluginCfg map[string]string, subCmd string) (string, bool) {
|
||||
configuredPluginHooks, ok := pluginCfg["hooks"]
|
||||
if !ok || configuredPluginHooks == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
commands := strings.Split(configuredPluginHooks, ",")
|
||||
for _, hookCmd := range commands {
|
||||
if hookMatch(hookCmd, subCmd) {
|
||||
return hookCmd, true
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
func hookMatch(hookCmd, subCmd string) bool {
|
||||
hookCmdTokens := strings.Split(hookCmd, " ")
|
||||
subCmdTokens := strings.Split(subCmd, " ")
|
||||
|
||||
if len(hookCmdTokens) > len(subCmdTokens) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, v := range hookCmdTokens {
|
||||
if v != subCmdTokens[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func getCommandFlags(cmd *cobra.Command) map[string]string {
|
||||
flags := make(map[string]string)
|
||||
cmd.Flags().Visit(func(f *pflag.Flag) {
|
||||
var fValue string
|
||||
if f.Value.Type() == "bool" {
|
||||
fValue = f.Value.String()
|
||||
}
|
||||
flags[f.Name] = fValue
|
||||
})
|
||||
return flags
|
||||
}
|
||||
|
||||
// getNaiveFlags string-matches argv and parses them into a map.
|
||||
// This is used when calling hooks after a plugin command, since
|
||||
// in this case we can't rely on the cobra command tree to parse
|
||||
// flags in this case. In this case, no values are ever passed,
|
||||
// since we don't have enough information to process them.
|
||||
func getNaiveFlags(args []string) map[string]string {
|
||||
flags := make(map[string]string)
|
||||
for _, arg := range args {
|
||||
if strings.HasPrefix(arg, "--") {
|
||||
flags[arg[2:]] = ""
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(arg, "-") {
|
||||
flags[arg[1:]] = ""
|
||||
}
|
||||
}
|
||||
return flags
|
||||
}
|
252
vendor/github.com/docker/cli/cli-plugins/manager/manager.go
generated
vendored
252
vendor/github.com/docker/cli/cli-plugins/manager/manager.go
generated
vendored
@ -1,252 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/fvbommel/sortorder"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
const (
|
||||
// ReexecEnvvar is the name of an ennvar which is set to the command
|
||||
// used to originally invoke the docker CLI when executing a
|
||||
// plugin. Assuming $PATH and $CWD remain unchanged this should allow
|
||||
// the plugin to re-execute the original CLI.
|
||||
ReexecEnvvar = metadata.ReexecEnvvar
|
||||
|
||||
// ResourceAttributesEnvvar is the name of the envvar that includes additional
|
||||
// resource attributes for OTEL.
|
||||
//
|
||||
// Deprecated: The "OTEL_RESOURCE_ATTRIBUTES" env-var is part of the OpenTelemetry specification; users should define their own const for this. This const will be removed in the next release.
|
||||
ResourceAttributesEnvvar = "OTEL_RESOURCE_ATTRIBUTES"
|
||||
)
|
||||
|
||||
// errPluginNotFound is the error returned when a plugin could not be found.
|
||||
type errPluginNotFound string
|
||||
|
||||
func (errPluginNotFound) NotFound() {}
|
||||
|
||||
func (e errPluginNotFound) Error() string {
|
||||
return "Error: No such CLI plugin: " + string(e)
|
||||
}
|
||||
|
||||
type notFound interface{ NotFound() }
|
||||
|
||||
// IsNotFound is true if the given error is due to a plugin not being found.
|
||||
func IsNotFound(err error) bool {
|
||||
if e, ok := err.(*pluginError); ok {
|
||||
err = e.Cause()
|
||||
}
|
||||
_, ok := err.(notFound)
|
||||
return ok
|
||||
}
|
||||
|
||||
// getPluginDirs returns the platform-specific locations to search for plugins
|
||||
// in order of preference.
|
||||
//
|
||||
// Plugin-discovery is performed in the following order of preference:
|
||||
//
|
||||
// 1. The "cli-plugins" directory inside the CLIs [config.Path] (usually "~/.docker/cli-plugins").
|
||||
// 2. Additional plugin directories as configured through [ConfigFile.CLIPluginsExtraDirs].
|
||||
// 3. Platform-specific defaultSystemPluginDirs.
|
||||
//
|
||||
// [ConfigFile.CLIPluginsExtraDirs]: https://pkg.go.dev/github.com/docker/cli@v26.1.4+incompatible/cli/config/configfile#ConfigFile.CLIPluginsExtraDirs
|
||||
func getPluginDirs(cfg *configfile.ConfigFile) ([]string, error) {
|
||||
var pluginDirs []string
|
||||
|
||||
if cfg != nil {
|
||||
pluginDirs = append(pluginDirs, cfg.CLIPluginsExtraDirs...)
|
||||
}
|
||||
pluginDir, err := config.Path("cli-plugins")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pluginDirs = append(pluginDirs, pluginDir)
|
||||
pluginDirs = append(pluginDirs, defaultSystemPluginDirs...)
|
||||
return pluginDirs, nil
|
||||
}
|
||||
|
||||
func addPluginCandidatesFromDir(res map[string][]string, d string) {
|
||||
dentries, err := os.ReadDir(d)
|
||||
// Silently ignore any directories which we cannot list (e.g. due to
|
||||
// permissions or anything else) or which is not a directory
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, dentry := range dentries {
|
||||
switch dentry.Type() & os.ModeType {
|
||||
case 0, os.ModeSymlink:
|
||||
// Regular file or symlink, keep going
|
||||
default:
|
||||
// Something else, ignore.
|
||||
continue
|
||||
}
|
||||
name := dentry.Name()
|
||||
if !strings.HasPrefix(name, metadata.NamePrefix) {
|
||||
continue
|
||||
}
|
||||
name = strings.TrimPrefix(name, metadata.NamePrefix)
|
||||
var err error
|
||||
if name, err = trimExeSuffix(name); err != nil {
|
||||
continue
|
||||
}
|
||||
res[name] = append(res[name], filepath.Join(d, dentry.Name()))
|
||||
}
|
||||
}
|
||||
|
||||
// listPluginCandidates returns a map from plugin name to the list of (unvalidated) Candidates. The list is in descending order of priority.
|
||||
func listPluginCandidates(dirs []string) map[string][]string {
|
||||
result := make(map[string][]string)
|
||||
for _, d := range dirs {
|
||||
addPluginCandidatesFromDir(result, d)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetPlugin returns a plugin on the system by its name
|
||||
func GetPlugin(name string, dockerCLI config.Provider, rootcmd *cobra.Command) (*Plugin, error) {
|
||||
pluginDirs, err := getPluginDirs(dockerCLI.ConfigFile())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getPlugin(name, pluginDirs, rootcmd)
|
||||
}
|
||||
|
||||
func getPlugin(name string, pluginDirs []string, rootcmd *cobra.Command) (*Plugin, error) {
|
||||
candidates := listPluginCandidates(pluginDirs)
|
||||
if paths, ok := candidates[name]; ok {
|
||||
if len(paths) == 0 {
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
c := &candidate{paths[0]}
|
||||
p, err := newPlugin(c, rootcmd.Commands())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !IsNotFound(p.Err) {
|
||||
p.ShadowedPaths = paths[1:]
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
|
||||
// ListPlugins produces a list of the plugins available on the system
|
||||
func ListPlugins(dockerCli config.Provider, rootcmd *cobra.Command) ([]Plugin, error) {
|
||||
pluginDirs, err := getPluginDirs(dockerCli.ConfigFile())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
candidates := listPluginCandidates(pluginDirs)
|
||||
|
||||
var plugins []Plugin
|
||||
var mu sync.Mutex
|
||||
eg, _ := errgroup.WithContext(context.TODO())
|
||||
cmds := rootcmd.Commands()
|
||||
for _, paths := range candidates {
|
||||
func(paths []string) {
|
||||
eg.Go(func() error {
|
||||
if len(paths) == 0 {
|
||||
return nil
|
||||
}
|
||||
c := &candidate{paths[0]}
|
||||
p, err := newPlugin(c, cmds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !IsNotFound(p.Err) {
|
||||
p.ShadowedPaths = paths[1:]
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
plugins = append(plugins, p)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}(paths)
|
||||
}
|
||||
if err := eg.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Slice(plugins, func(i, j int) bool {
|
||||
return sortorder.NaturalLess(plugins[i].Name, plugins[j].Name)
|
||||
})
|
||||
|
||||
return plugins, nil
|
||||
}
|
||||
|
||||
// PluginRunCommand returns an "os/exec".Cmd which when .Run() will execute the named plugin.
|
||||
// The rootcmd argument is referenced to determine the set of builtin commands in order to detect conficts.
|
||||
// The error returned satisfies the IsNotFound() predicate if no plugin was found or if the first candidate plugin was invalid somehow.
|
||||
func PluginRunCommand(dockerCli config.Provider, name string, rootcmd *cobra.Command) (*exec.Cmd, error) {
|
||||
// This uses the full original args, not the args which may
|
||||
// have been provided by cobra to our caller. This is because
|
||||
// they lack e.g. global options which we must propagate here.
|
||||
args := os.Args[1:]
|
||||
if !pluginNameRe.MatchString(name) {
|
||||
// We treat this as "not found" so that callers will
|
||||
// fallback to their "invalid" command path.
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
exename := addExeSuffix(metadata.NamePrefix + name)
|
||||
pluginDirs, err := getPluginDirs(dockerCli.ConfigFile())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, d := range pluginDirs {
|
||||
path := filepath.Join(d, exename)
|
||||
|
||||
// We stat here rather than letting the exec tell us
|
||||
// ENOENT because the latter does not distinguish a
|
||||
// file not existing from its dynamic loader or one of
|
||||
// its libraries not existing.
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
|
||||
c := &candidate{path: path}
|
||||
plugin, err := newPlugin(c, rootcmd.Commands())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if plugin.Err != nil {
|
||||
// TODO: why are we not returning plugin.Err?
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
cmd := exec.Command(plugin.Path, args...) // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
|
||||
|
||||
// Using dockerCli.{In,Out,Err}() here results in a hang until something is input.
|
||||
// See: - https://github.com/golang/go/issues/10338
|
||||
// - https://github.com/golang/go/commit/d000e8742a173aa0659584aa01b7ba2834ba28ab
|
||||
// os.Stdin is a *os.File which avoids this behaviour. We don't need the functionality
|
||||
// of the wrappers here anyway.
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
cmd.Env = append(cmd.Environ(), metadata.ReexecEnvvar+"="+os.Args[0])
|
||||
cmd.Env = appendPluginResourceAttributesEnvvar(cmd.Env, rootcmd, plugin)
|
||||
|
||||
return cmd, nil
|
||||
}
|
||||
return nil, errPluginNotFound(name)
|
||||
}
|
||||
|
||||
// IsPluginCommand checks if the given cmd is a plugin-stub.
|
||||
func IsPluginCommand(cmd *cobra.Command) bool {
|
||||
return cmd.Annotations[metadata.CommandAnnotationPlugin] == "true"
|
||||
}
|
20
vendor/github.com/docker/cli/cli-plugins/manager/manager_unix.go
generated
vendored
20
vendor/github.com/docker/cli/cli-plugins/manager/manager_unix.go
generated
vendored
@ -1,20 +0,0 @@
|
||||
//go:build !windows
|
||||
|
||||
package manager
|
||||
|
||||
// defaultSystemPluginDirs are the platform-specific locations to search
|
||||
// for plugins in order of preference.
|
||||
//
|
||||
// Plugin-discovery is performed in the following order of preference:
|
||||
//
|
||||
// 1. The "cli-plugins" directory inside the CLIs config-directory (usually "~/.docker/cli-plugins").
|
||||
// 2. Additional plugin directories as configured through [ConfigFile.CLIPluginsExtraDirs].
|
||||
// 3. Platform-specific defaultSystemPluginDirs (as defined below).
|
||||
//
|
||||
// [ConfigFile.CLIPluginsExtraDirs]: https://pkg.go.dev/github.com/docker/cli@v26.1.4+incompatible/cli/config/configfile#ConfigFile.CLIPluginsExtraDirs
|
||||
var defaultSystemPluginDirs = []string{
|
||||
"/usr/local/lib/docker/cli-plugins",
|
||||
"/usr/local/libexec/docker/cli-plugins",
|
||||
"/usr/lib/docker/cli-plugins",
|
||||
"/usr/libexec/docker/cli-plugins",
|
||||
}
|
21
vendor/github.com/docker/cli/cli-plugins/manager/manager_windows.go
generated
vendored
21
vendor/github.com/docker/cli/cli-plugins/manager/manager_windows.go
generated
vendored
@ -1,21 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// defaultSystemPluginDirs are the platform-specific locations to search
|
||||
// for plugins in order of preference.
|
||||
//
|
||||
// Plugin-discovery is performed in the following order of preference:
|
||||
//
|
||||
// 1. The "cli-plugins" directory inside the CLIs config-directory (usually "~/.docker/cli-plugins").
|
||||
// 2. Additional plugin directories as configured through [ConfigFile.CLIPluginsExtraDirs].
|
||||
// 3. Platform-specific defaultSystemPluginDirs (as defined below).
|
||||
//
|
||||
// [ConfigFile.CLIPluginsExtraDirs]: https://pkg.go.dev/github.com/docker/cli@v26.1.4+incompatible/cli/config/configfile#ConfigFile.CLIPluginsExtraDirs
|
||||
var defaultSystemPluginDirs = []string{
|
||||
filepath.Join(os.Getenv("ProgramData"), "Docker", "cli-plugins"),
|
||||
filepath.Join(os.Getenv("ProgramFiles"), "Docker", "cli-plugins"),
|
||||
}
|
23
vendor/github.com/docker/cli/cli-plugins/manager/metadata.go
generated
vendored
23
vendor/github.com/docker/cli/cli-plugins/manager/metadata.go
generated
vendored
@ -1,23 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
)
|
||||
|
||||
const (
|
||||
// NamePrefix is the prefix required on all plugin binary names
|
||||
NamePrefix = metadata.NamePrefix
|
||||
|
||||
// MetadataSubcommandName is the name of the plugin subcommand
|
||||
// which must be supported by every plugin and returns the
|
||||
// plugin metadata.
|
||||
MetadataSubcommandName = metadata.MetadataSubcommandName
|
||||
|
||||
// HookSubcommandName is the name of the plugin subcommand
|
||||
// which must be implemented by plugins declaring support
|
||||
// for hooks in their metadata.
|
||||
HookSubcommandName = metadata.HookSubcommandName
|
||||
)
|
||||
|
||||
// Metadata provided by the plugin.
|
||||
type Metadata = metadata.Metadata
|
126
vendor/github.com/docker/cli/cli-plugins/manager/plugin.go
generated
vendored
126
vendor/github.com/docker/cli/cli-plugins/manager/plugin.go
generated
vendored
@ -1,126 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var pluginNameRe = regexp.MustCompile("^[a-z][a-z0-9]*$")
|
||||
|
||||
// Plugin represents a potential plugin with all it's metadata.
|
||||
type Plugin struct {
|
||||
metadata.Metadata
|
||||
|
||||
Name string `json:",omitempty"`
|
||||
Path string `json:",omitempty"`
|
||||
|
||||
// Err is non-nil if the plugin failed one of the candidate tests.
|
||||
Err error `json:",omitempty"`
|
||||
|
||||
// ShadowedPaths contains the paths of any other plugins which this plugin takes precedence over.
|
||||
ShadowedPaths []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// newPlugin determines if the given candidate is valid and returns a
|
||||
// Plugin. If the candidate fails one of the tests then `Plugin.Err`
|
||||
// is set, and is always a `pluginError`, but the `Plugin` is still
|
||||
// returned with no error. An error is only returned due to a
|
||||
// non-recoverable error.
|
||||
func newPlugin(c Candidate, cmds []*cobra.Command) (Plugin, error) {
|
||||
path := c.Path()
|
||||
if path == "" {
|
||||
return Plugin{}, errors.New("plugin candidate path cannot be empty")
|
||||
}
|
||||
|
||||
// The candidate listing process should have skipped anything
|
||||
// which would fail here, so there are all real errors.
|
||||
fullname := filepath.Base(path)
|
||||
if fullname == "." {
|
||||
return Plugin{}, fmt.Errorf("unable to determine basename of plugin candidate %q", path)
|
||||
}
|
||||
var err error
|
||||
if fullname, err = trimExeSuffix(fullname); err != nil {
|
||||
return Plugin{}, fmt.Errorf("plugin candidate %q: %w", path, err)
|
||||
}
|
||||
if !strings.HasPrefix(fullname, metadata.NamePrefix) {
|
||||
return Plugin{}, fmt.Errorf("plugin candidate %q: does not have %q prefix", path, metadata.NamePrefix)
|
||||
}
|
||||
|
||||
p := Plugin{
|
||||
Name: strings.TrimPrefix(fullname, metadata.NamePrefix),
|
||||
Path: path,
|
||||
}
|
||||
|
||||
// Now apply the candidate tests, so these update p.Err.
|
||||
if !pluginNameRe.MatchString(p.Name) {
|
||||
p.Err = NewPluginError("plugin candidate %q did not match %q", p.Name, pluginNameRe.String())
|
||||
return p, nil
|
||||
}
|
||||
|
||||
for _, cmd := range cmds {
|
||||
// Ignore conflicts with commands which are
|
||||
// just plugin stubs (i.e. from a previous
|
||||
// call to AddPluginCommandStubs).
|
||||
if IsPluginCommand(cmd) {
|
||||
continue
|
||||
}
|
||||
if cmd.Name() == p.Name {
|
||||
p.Err = NewPluginError("plugin %q duplicates builtin command", p.Name)
|
||||
return p, nil
|
||||
}
|
||||
if cmd.HasAlias(p.Name) {
|
||||
p.Err = NewPluginError("plugin %q duplicates an alias of builtin command %q", p.Name, cmd.Name())
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
// We are supposed to check for relevant execute permissions here. Instead we rely on an attempt to execute.
|
||||
meta, err := c.Metadata()
|
||||
if err != nil {
|
||||
p.Err = wrapAsPluginError(err, "failed to fetch metadata")
|
||||
return p, nil
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(meta, &p.Metadata); err != nil {
|
||||
p.Err = wrapAsPluginError(err, "invalid metadata")
|
||||
return p, nil
|
||||
}
|
||||
if p.Metadata.SchemaVersion != "0.1.0" {
|
||||
p.Err = NewPluginError("plugin SchemaVersion %q is not valid, must be 0.1.0", p.Metadata.SchemaVersion)
|
||||
return p, nil
|
||||
}
|
||||
if p.Metadata.Vendor == "" {
|
||||
p.Err = NewPluginError("plugin metadata does not define a vendor")
|
||||
return p, nil
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// RunHook executes the plugin's hooks command
|
||||
// and returns its unprocessed output.
|
||||
func (p *Plugin) RunHook(ctx context.Context, hookData HookPluginData) ([]byte, error) {
|
||||
hDataBytes, err := json.Marshal(hookData)
|
||||
if err != nil {
|
||||
return nil, wrapAsPluginError(err, "failed to marshall hook data")
|
||||
}
|
||||
|
||||
pCmd := exec.CommandContext(ctx, p.Path, p.Name, metadata.HookSubcommandName, string(hDataBytes)) // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
|
||||
pCmd.Env = os.Environ()
|
||||
pCmd.Env = append(pCmd.Env, metadata.ReexecEnvvar+"="+os.Args[0])
|
||||
hookCmdOutput, err := pCmd.Output()
|
||||
if err != nil {
|
||||
return nil, wrapAsPluginError(err, "failed to execute plugin hook subcommand")
|
||||
}
|
||||
|
||||
return hookCmdOutput, nil
|
||||
}
|
11
vendor/github.com/docker/cli/cli-plugins/manager/suffix_unix.go
generated
vendored
11
vendor/github.com/docker/cli/cli-plugins/manager/suffix_unix.go
generated
vendored
@ -1,11 +0,0 @@
|
||||
//go:build !windows
|
||||
|
||||
package manager
|
||||
|
||||
func trimExeSuffix(s string) (string, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func addExeSuffix(s string) string {
|
||||
return s
|
||||
}
|
20
vendor/github.com/docker/cli/cli-plugins/manager/suffix_windows.go
generated
vendored
20
vendor/github.com/docker/cli/cli-plugins/manager/suffix_windows.go
generated
vendored
@ -1,20 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// This is made slightly more complex due to needing to be case-insensitive.
|
||||
func trimExeSuffix(s string) (string, error) {
|
||||
ext := filepath.Ext(s)
|
||||
if ext == "" || !strings.EqualFold(ext, ".exe") {
|
||||
return "", fmt.Errorf("path %q lacks required file extension (.exe)", s)
|
||||
}
|
||||
return strings.TrimSuffix(s, ext), nil
|
||||
}
|
||||
|
||||
func addExeSuffix(s string) string {
|
||||
return s + ".exe"
|
||||
}
|
85
vendor/github.com/docker/cli/cli-plugins/manager/telemetry.go
generated
vendored
85
vendor/github.com/docker/cli/cli-plugins/manager/telemetry.go
generated
vendored
@ -1,85 +0,0 @@
|
||||
package manager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/spf13/cobra"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/baggage"
|
||||
)
|
||||
|
||||
const (
|
||||
// resourceAttributesEnvVar is the name of the envvar that includes additional
|
||||
// resource attributes for OTEL as defined in the [OpenTelemetry specification].
|
||||
//
|
||||
// [OpenTelemetry specification]: https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/#general-sdk-configuration
|
||||
resourceAttributesEnvVar = "OTEL_RESOURCE_ATTRIBUTES"
|
||||
|
||||
// dockerCLIAttributePrefix is the prefix for any docker cli OTEL attributes.
|
||||
//
|
||||
// It is a copy of the const defined in [command.dockerCLIAttributePrefix].
|
||||
dockerCLIAttributePrefix = "docker.cli."
|
||||
cobraCommandPath = attribute.Key("cobra.command_path")
|
||||
)
|
||||
|
||||
func getPluginResourceAttributes(cmd *cobra.Command, plugin Plugin) attribute.Set {
|
||||
commandPath := cmd.Annotations[metadata.CommandAnnotationPluginCommandPath]
|
||||
if commandPath == "" {
|
||||
commandPath = fmt.Sprintf("%s %s", cmd.CommandPath(), plugin.Name)
|
||||
}
|
||||
|
||||
attrSet := attribute.NewSet(
|
||||
cobraCommandPath.String(commandPath),
|
||||
)
|
||||
|
||||
kvs := make([]attribute.KeyValue, 0, attrSet.Len())
|
||||
for iter := attrSet.Iter(); iter.Next(); {
|
||||
attr := iter.Attribute()
|
||||
kvs = append(kvs, attribute.KeyValue{
|
||||
Key: dockerCLIAttributePrefix + attr.Key,
|
||||
Value: attr.Value,
|
||||
})
|
||||
}
|
||||
return attribute.NewSet(kvs...)
|
||||
}
|
||||
|
||||
func appendPluginResourceAttributesEnvvar(env []string, cmd *cobra.Command, plugin Plugin) []string {
|
||||
if attrs := getPluginResourceAttributes(cmd, plugin); attrs.Len() > 0 {
|
||||
// Construct baggage members for each of the attributes.
|
||||
// Ignore any failures as these aren't significant and
|
||||
// represent an internal issue.
|
||||
members := make([]baggage.Member, 0, attrs.Len())
|
||||
for iter := attrs.Iter(); iter.Next(); {
|
||||
attr := iter.Attribute()
|
||||
m, err := baggage.NewMemberRaw(string(attr.Key), attr.Value.AsString())
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
continue
|
||||
}
|
||||
members = append(members, m)
|
||||
}
|
||||
|
||||
// Combine plugin added resource attributes with ones found in the environment
|
||||
// variable. Our own attributes should be namespaced so there shouldn't be a
|
||||
// conflict. We do not parse the environment variable because we do not want
|
||||
// to handle errors in user configuration.
|
||||
attrsSlice := make([]string, 0, 2)
|
||||
if v := strings.TrimSpace(os.Getenv(resourceAttributesEnvVar)); v != "" {
|
||||
attrsSlice = append(attrsSlice, v)
|
||||
}
|
||||
if b, err := baggage.New(members...); err != nil {
|
||||
otel.Handle(err)
|
||||
} else if b.Len() > 0 {
|
||||
attrsSlice = append(attrsSlice, b.String())
|
||||
}
|
||||
|
||||
if len(attrsSlice) > 0 {
|
||||
env = append(env, resourceAttributesEnvVar+"="+strings.Join(attrsSlice, ","))
|
||||
}
|
||||
}
|
||||
return env
|
||||
}
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@ -232,8 +232,6 @@ github.com/distribution/reference
|
||||
# github.com/docker/cli v28.0.2+incompatible
|
||||
## explicit
|
||||
github.com/docker/cli/cli
|
||||
github.com/docker/cli/cli-plugins/hooks
|
||||
github.com/docker/cli/cli-plugins/manager
|
||||
github.com/docker/cli/cli-plugins/metadata
|
||||
github.com/docker/cli/cli-plugins/plugin
|
||||
github.com/docker/cli/cli-plugins/socket
|
||||
|
Loading…
x
Reference in New Issue
Block a user