diff --git a/cmd/buildx/main.go b/cmd/buildx/main.go index 61f3f630..8333ecc4 100644 --- a/cmd/buildx/main.go +++ b/cmd/buildx/main.go @@ -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, diff --git a/vendor/github.com/docker/cli/cli-plugins/hooks/printer.go b/vendor/github.com/docker/cli/cli-plugins/hooks/printer.go deleted file mode 100644 index f6d4b28e..00000000 --- a/vendor/github.com/docker/cli/cli-plugins/hooks/printer.go +++ /dev/null @@ -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) - } -} diff --git a/vendor/github.com/docker/cli/cli-plugins/hooks/template.go b/vendor/github.com/docker/cli/cli-plugins/hooks/template.go deleted file mode 100644 index e6bd69f3..00000000 --- a/vendor/github.com/docker/cli/cli-plugins/hooks/template.go +++ /dev/null @@ -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 -} diff --git a/vendor/github.com/docker/cli/cli-plugins/manager/annotations.go b/vendor/github.com/docker/cli/cli-plugins/manager/annotations.go deleted file mode 100644 index 8fc76b73..00000000 --- a/vendor/github.com/docker/cli/cli-plugins/manager/annotations.go +++ /dev/null @@ -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 -) diff --git a/vendor/github.com/docker/cli/cli-plugins/manager/candidate.go b/vendor/github.com/docker/cli/cli-plugins/manager/candidate.go deleted file mode 100644 index d809926c..00000000 --- a/vendor/github.com/docker/cli/cli-plugins/manager/candidate.go +++ /dev/null @@ -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" -} diff --git a/vendor/github.com/docker/cli/cli-plugins/manager/cobra.go b/vendor/github.com/docker/cli/cli-plugins/manager/cobra.go deleted file mode 100644 index ddf067be..00000000 --- a/vendor/github.com/docker/cli/cli-plugins/manager/cobra.go +++ /dev/null @@ -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 -} diff --git a/vendor/github.com/docker/cli/cli-plugins/manager/error.go b/vendor/github.com/docker/cli/cli-plugins/manager/error.go deleted file mode 100644 index 144c11fa..00000000 --- a/vendor/github.com/docker/cli/cli-plugins/manager/error.go +++ /dev/null @@ -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...)} -} diff --git a/vendor/github.com/docker/cli/cli-plugins/manager/hooks.go b/vendor/github.com/docker/cli/cli-plugins/manager/hooks.go deleted file mode 100644 index b92dab43..00000000 --- a/vendor/github.com/docker/cli/cli-plugins/manager/hooks.go +++ /dev/null @@ -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 -} diff --git a/vendor/github.com/docker/cli/cli-plugins/manager/manager.go b/vendor/github.com/docker/cli/cli-plugins/manager/manager.go deleted file mode 100644 index 4b553eae..00000000 --- a/vendor/github.com/docker/cli/cli-plugins/manager/manager.go +++ /dev/null @@ -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" -} diff --git a/vendor/github.com/docker/cli/cli-plugins/manager/manager_unix.go b/vendor/github.com/docker/cli/cli-plugins/manager/manager_unix.go deleted file mode 100644 index f546dc38..00000000 --- a/vendor/github.com/docker/cli/cli-plugins/manager/manager_unix.go +++ /dev/null @@ -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", -} diff --git a/vendor/github.com/docker/cli/cli-plugins/manager/manager_windows.go b/vendor/github.com/docker/cli/cli-plugins/manager/manager_windows.go deleted file mode 100644 index e8b5598e..00000000 --- a/vendor/github.com/docker/cli/cli-plugins/manager/manager_windows.go +++ /dev/null @@ -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"), -} diff --git a/vendor/github.com/docker/cli/cli-plugins/manager/metadata.go b/vendor/github.com/docker/cli/cli-plugins/manager/metadata.go deleted file mode 100644 index 9bddb121..00000000 --- a/vendor/github.com/docker/cli/cli-plugins/manager/metadata.go +++ /dev/null @@ -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 diff --git a/vendor/github.com/docker/cli/cli-plugins/manager/plugin.go b/vendor/github.com/docker/cli/cli-plugins/manager/plugin.go deleted file mode 100644 index ec196df1..00000000 --- a/vendor/github.com/docker/cli/cli-plugins/manager/plugin.go +++ /dev/null @@ -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 -} diff --git a/vendor/github.com/docker/cli/cli-plugins/manager/suffix_unix.go b/vendor/github.com/docker/cli/cli-plugins/manager/suffix_unix.go deleted file mode 100644 index 050e5020..00000000 --- a/vendor/github.com/docker/cli/cli-plugins/manager/suffix_unix.go +++ /dev/null @@ -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 -} diff --git a/vendor/github.com/docker/cli/cli-plugins/manager/suffix_windows.go b/vendor/github.com/docker/cli/cli-plugins/manager/suffix_windows.go deleted file mode 100644 index a00be881..00000000 --- a/vendor/github.com/docker/cli/cli-plugins/manager/suffix_windows.go +++ /dev/null @@ -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" -} diff --git a/vendor/github.com/docker/cli/cli-plugins/manager/telemetry.go b/vendor/github.com/docker/cli/cli-plugins/manager/telemetry.go deleted file mode 100644 index ab3dace4..00000000 --- a/vendor/github.com/docker/cli/cli-plugins/manager/telemetry.go +++ /dev/null @@ -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 -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 82b76616..d0c1f428 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -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