mirror of https://github.com/docker/cli.git
128 lines
3.2 KiB
Go
128 lines
3.2 KiB
Go
|
package manager
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/docker/cli/cli-plugins/hooks"
|
||
|
"github.com/docker/cli/cli/command"
|
||
|
"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 string
|
||
|
Flags map[string]string
|
||
|
}
|
||
|
|
||
|
// RunPluginHooks calls the hook subcommand for all present
|
||
|
// CLI plugins that declare support for hooks in their metadata
|
||
|
// and parses/prints their responses.
|
||
|
func RunPluginHooks(dockerCli command.Cli, rootCmd, subCommand *cobra.Command, plugin string, args []string) error {
|
||
|
subCmdName := subCommand.Name()
|
||
|
if plugin != "" {
|
||
|
subCmdName = plugin
|
||
|
}
|
||
|
var flags map[string]string
|
||
|
if plugin == "" {
|
||
|
flags = getCommandFlags(subCommand)
|
||
|
} else {
|
||
|
flags = getNaiveFlags(args)
|
||
|
}
|
||
|
nextSteps := invokeAndCollectHooks(dockerCli, rootCmd, subCommand, subCmdName, flags)
|
||
|
|
||
|
hooks.PrintNextSteps(dockerCli.Err(), nextSteps)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func invokeAndCollectHooks(dockerCli command.Cli, rootCmd, subCmd *cobra.Command, hookCmdName string, flags map[string]string) []string {
|
||
|
pluginsCfg := dockerCli.ConfigFile().Plugins
|
||
|
if pluginsCfg == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
nextSteps := make([]string, 0, len(pluginsCfg))
|
||
|
for pluginName, cfg := range pluginsCfg {
|
||
|
if !registersHook(cfg, hookCmdName) {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
p, err := GetPlugin(pluginName, dockerCli, rootCmd)
|
||
|
if err != nil {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
hookReturn, err := p.RunHook(hookCmdName, flags)
|
||
|
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
|
||
|
}
|
||
|
nextSteps = append(nextSteps, processedHook)
|
||
|
}
|
||
|
return nextSteps
|
||
|
}
|
||
|
|
||
|
func registersHook(pluginCfg map[string]string, subCmdName string) bool {
|
||
|
hookCmdStr, ok := pluginCfg["hooks"]
|
||
|
if !ok {
|
||
|
return false
|
||
|
}
|
||
|
commands := strings.Split(hookCmdStr, ",")
|
||
|
for _, hookCmd := range commands {
|
||
|
if hookCmd == subCmdName {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|