Merge pull request #1840 from tiborvass/cli-plugin-aliases

cli-plugins: alias an existing allowed command (only builder for now)
This commit is contained in:
Tõnis Tiigi 2019-04-23 19:13:51 -07:00 committed by GitHub
commit 70f48f2231
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 111 additions and 1 deletions

View File

@ -164,6 +164,7 @@ func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command
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...)

View File

@ -5,6 +5,7 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/image"
)
// NewBuilderCommand returns a cobra command for `builder` subcommands
@ -18,6 +19,7 @@ func NewBuilderCommand(dockerCli command.Cli) *cobra.Command {
}
cmd.AddCommand(
NewPruneCommand(dockerCli),
image.NewBuildCommand(dockerCli),
)
return cmd
}

View File

@ -161,3 +161,34 @@ func ValidateOutputPathFileMode(fileMode os.FileMode) error {
}
return nil
}
func stringSliceIndex(s, subs []string) int {
j := 0
if len(subs) > 0 {
for i, x := range s {
if j < len(subs) && subs[j] == x {
j++
} else {
j = 0
}
if len(subs) == j {
return i + 1 - j
}
}
}
return -1
}
// StringSliceReplaceAt replaces the sub-slice old, with the sub-slice new, in the string
// slice s, returning a new slice and a boolean indicating if the replacement happened.
// requireIdx is the index at which old needs to be found at (or -1 to disregard that).
func StringSliceReplaceAt(s, old, new []string, requireIndex int) ([]string, bool) {
idx := stringSliceIndex(s, old)
if (requireIndex != -1 && requireIndex != idx) || idx == -1 {
return s, false
}
out := append([]string{}, s[:idx]...)
out = append(out, new...)
out = append(out, s[idx+len(old):]...)
return out, true
}

33
cli/command/utils_test.go Normal file
View File

@ -0,0 +1,33 @@
package command
import (
"testing"
"gotest.tools/assert"
)
func TestStringSliceReplaceAt(t *testing.T) {
out, ok := StringSliceReplaceAt([]string{"abc", "foo", "bar", "bax"}, []string{"foo", "bar"}, []string{"baz"}, -1)
assert.Assert(t, ok)
assert.DeepEqual(t, []string{"abc", "baz", "bax"}, out)
out, ok = StringSliceReplaceAt([]string{"foo"}, []string{"foo", "bar"}, []string{"baz"}, -1)
assert.Assert(t, !ok)
assert.DeepEqual(t, []string{"foo"}, out)
out, ok = StringSliceReplaceAt([]string{"abc", "foo", "bar", "bax"}, []string{"foo", "bar"}, []string{"baz"}, 0)
assert.Assert(t, !ok)
assert.DeepEqual(t, []string{"abc", "foo", "bar", "bax"}, out)
out, ok = StringSliceReplaceAt([]string{"foo", "bar", "bax"}, []string{"foo", "bar"}, []string{"baz"}, 0)
assert.Assert(t, ok)
assert.DeepEqual(t, []string{"baz", "bax"}, out)
out, ok = StringSliceReplaceAt([]string{"abc", "foo", "bar", "baz"}, []string{"foo", "bar"}, nil, -1)
assert.Assert(t, ok)
assert.DeepEqual(t, []string{"abc", "baz"}, out)
out, ok = StringSliceReplaceAt([]string{"foo"}, nil, []string{"baz"}, -1)
assert.Assert(t, !ok)
assert.DeepEqual(t, []string{"foo"}, out)
}

View File

@ -50,6 +50,7 @@ type ConfigFile struct {
CurrentContext string `json:"currentContext,omitempty"`
CLIPluginsExtraDirs []string `json:"cliPluginsExtraDirs,omitempty"`
Plugins map[string]map[string]string `json:"plugins,omitempty"`
Aliases map[string]string `json:"aliases,omitempty"`
}
// ProxyConfig contains proxy configuration settings
@ -72,6 +73,7 @@ func New(fn string) *ConfigFile {
HTTPHeaders: make(map[string]string),
Filename: fn,
Plugins: make(map[string]map[string]string),
Aliases: make(map[string]string),
}
}

View File

@ -1,7 +1,6 @@
package main
import (
"errors"
"fmt"
"os"
"os/exec"
@ -16,11 +15,16 @@ import (
"github.com/docker/cli/cli/version"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/client"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
var allowedAliases = map[string]struct{}{
"builder": {},
}
func newDockerCommand(dockerCli *command.DockerCli) *cli.TopLevelCommand {
var (
opts *cliflags.ClientOptions
@ -204,6 +208,38 @@ func tryPluginRun(dockerCli command.Cli, cmd *cobra.Command, subcommand string)
return nil
}
func processAliases(dockerCli command.Cli, cmd *cobra.Command, args, osArgs []string) ([]string, []string, error) {
aliasMap := dockerCli.ConfigFile().Aliases
aliases := make([][2][]string, 0, len(aliasMap))
for k, v := range aliasMap {
if _, ok := allowedAliases[k]; !ok {
return args, osArgs, errors.Errorf("Not allowed to alias %q. Allowed aliases: %#v", k, allowedAliases)
}
if _, _, err := cmd.Find(strings.Split(v, " ")); err == nil {
return args, osArgs, errors.Errorf("Not allowed to alias with builtin %q as target", v)
}
aliases = append(aliases, [2][]string{{k}, {v}})
}
if v, ok := aliasMap["builder"]; ok {
aliases = append(aliases,
[2][]string{{"build"}, {v, "build"}},
[2][]string{{"image", "build"}, {v, "build"}},
)
}
for _, al := range aliases {
var didChange bool
args, didChange = command.StringSliceReplaceAt(args, al[0], al[1], 0)
if didChange {
osArgs, _ = command.StringSliceReplaceAt(osArgs, al[0], al[1], -1)
break
}
}
return args, osArgs, nil
}
func runDocker(dockerCli *command.DockerCli) error {
tcmd := newDockerCommand(dockerCli)
@ -216,6 +252,11 @@ func runDocker(dockerCli *command.DockerCli) error {
return err
}
args, os.Args, err = processAliases(dockerCli, cmd, args, os.Args)
if err != nil {
return err
}
if len(args) > 0 {
if _, _, err := cmd.Find(args); err != nil {
err := tryPluginRun(dockerCli, cmd, args[0])