Import docker/docker/cli
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
This commit is contained in:
32
cli/command/plugin/cmd.go
Normal file
32
cli/command/plugin/cmd.go
Normal file
@ -0,0 +1,32 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewPluginCommand returns a cobra command for `plugin` subcommands
|
||||
func NewPluginCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "plugin",
|
||||
Short: "Manage plugins",
|
||||
Args: cli.NoArgs,
|
||||
RunE: dockerCli.ShowHelp,
|
||||
Tags: map[string]string{"version": "1.25"},
|
||||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
newDisableCommand(dockerCli),
|
||||
newEnableCommand(dockerCli),
|
||||
newInspectCommand(dockerCli),
|
||||
newInstallCommand(dockerCli),
|
||||
newListCommand(dockerCli),
|
||||
newRemoveCommand(dockerCli),
|
||||
newSetCommand(dockerCli),
|
||||
newPushCommand(dockerCli),
|
||||
newCreateCommand(dockerCli),
|
||||
newUpgradeCommand(dockerCli),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
128
cli/command/plugin/create.go
Normal file
128
cli/command/plugin/create.go
Normal file
@ -0,0 +1,128 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// validateTag checks if the given repoName can be resolved.
|
||||
func validateTag(rawRepo string) error {
|
||||
_, err := reference.ParseNormalizedNamed(rawRepo)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// validateConfig ensures that a valid config.json is available in the given path
|
||||
func validateConfig(path string) error {
|
||||
dt, err := os.Open(filepath.Join(path, "config.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := types.PluginConfig{}
|
||||
err = json.NewDecoder(dt).Decode(&m)
|
||||
dt.Close()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// validateContextDir validates the given dir and returns abs path on success.
|
||||
func validateContextDir(contextDir string) (string, error) {
|
||||
absContextDir, err := filepath.Abs(contextDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
stat, err := os.Lstat(absContextDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !stat.IsDir() {
|
||||
return "", errors.Errorf("context must be a directory")
|
||||
}
|
||||
|
||||
return absContextDir, nil
|
||||
}
|
||||
|
||||
type pluginCreateOptions struct {
|
||||
repoName string
|
||||
context string
|
||||
compress bool
|
||||
}
|
||||
|
||||
func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
options := pluginCreateOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "create [OPTIONS] PLUGIN PLUGIN-DATA-DIR",
|
||||
Short: "Create a plugin from a rootfs and configuration. Plugin data directory must contain config.json and rootfs directory.",
|
||||
Args: cli.RequiresMinArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
options.repoName = args[0]
|
||||
options.context = args[1]
|
||||
return runCreate(dockerCli, options)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.BoolVar(&options.compress, "compress", false, "Compress the context using gzip")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCreate(dockerCli *command.DockerCli, options pluginCreateOptions) error {
|
||||
var (
|
||||
createCtx io.ReadCloser
|
||||
err error
|
||||
)
|
||||
|
||||
if err := validateTag(options.repoName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
absContextDir, err := validateContextDir(options.context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateConfig(options.context); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
compression := archive.Uncompressed
|
||||
if options.compress {
|
||||
logrus.Debugf("compression enabled")
|
||||
compression = archive.Gzip
|
||||
}
|
||||
|
||||
createCtx, err = archive.TarWithOptions(absContextDir, &archive.TarOptions{
|
||||
Compression: compression,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
createOptions := types.PluginCreateOptions{RepoName: options.repoName}
|
||||
if err = dockerCli.Client().PluginCreate(ctx, createCtx, createOptions); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(dockerCli.Out(), options.repoName)
|
||||
return nil
|
||||
}
|
||||
36
cli/command/plugin/disable.go
Normal file
36
cli/command/plugin/disable.go
Normal file
@ -0,0 +1,36 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func newDisableCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var force bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "disable [OPTIONS] PLUGIN",
|
||||
Short: "Disable a plugin",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runDisable(dockerCli, args[0], force)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&force, "force", "f", false, "Force the disable of an active plugin")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runDisable(dockerCli *command.DockerCli, name string, force bool) error {
|
||||
if err := dockerCli.Client().PluginDisable(context.Background(), name, types.PluginDisableOptions{Force: force}); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(dockerCli.Out(), name)
|
||||
return nil
|
||||
}
|
||||
48
cli/command/plugin/enable.go
Normal file
48
cli/command/plugin/enable.go
Normal file
@ -0,0 +1,48 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type enableOpts struct {
|
||||
timeout int
|
||||
name string
|
||||
}
|
||||
|
||||
func newEnableCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts enableOpts
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "enable [OPTIONS] PLUGIN",
|
||||
Short: "Enable a plugin",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.name = args[0]
|
||||
return runEnable(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.IntVar(&opts.timeout, "timeout", 0, "HTTP client timeout (in seconds)")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runEnable(dockerCli *command.DockerCli, opts *enableOpts) error {
|
||||
name := opts.name
|
||||
if opts.timeout < 0 {
|
||||
return errors.Errorf("negative timeout %d is invalid", opts.timeout)
|
||||
}
|
||||
|
||||
if err := dockerCli.Client().PluginEnable(context.Background(), name, types.PluginEnableOptions{Timeout: opts.timeout}); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(dockerCli.Out(), name)
|
||||
return nil
|
||||
}
|
||||
42
cli/command/plugin/inspect.go
Normal file
42
cli/command/plugin/inspect.go
Normal file
@ -0,0 +1,42 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/docker/docker/cli/command/inspect"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type inspectOptions struct {
|
||||
pluginNames []string
|
||||
format string
|
||||
}
|
||||
|
||||
func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts inspectOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "inspect [OPTIONS] PLUGIN [PLUGIN...]",
|
||||
Short: "Display detailed information on one or more plugins",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.pluginNames = args
|
||||
return runInspect(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVarP(&opts.format, "format", "f", "", "Format the output using the given Go template")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
|
||||
client := dockerCli.Client()
|
||||
ctx := context.Background()
|
||||
getRef := func(ref string) (interface{}, []byte, error) {
|
||||
return client.PluginInspectWithRaw(ctx, ref)
|
||||
}
|
||||
|
||||
return inspect.Inspect(dockerCli.Out(), opts.pluginNames, opts.format, getRef)
|
||||
}
|
||||
168
cli/command/plugin/install.go
Normal file
168
cli/command/plugin/install.go
Normal file
@ -0,0 +1,168 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/docker/docker/cli/command/image"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type pluginOptions struct {
|
||||
remote string
|
||||
localName string
|
||||
grantPerms bool
|
||||
disable bool
|
||||
args []string
|
||||
skipRemoteCheck bool
|
||||
}
|
||||
|
||||
func loadPullFlags(opts *pluginOptions, flags *pflag.FlagSet) {
|
||||
flags.BoolVar(&opts.grantPerms, "grant-all-permissions", false, "Grant all permissions necessary to run the plugin")
|
||||
command.AddTrustVerificationFlags(flags)
|
||||
}
|
||||
|
||||
func newInstallCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var options pluginOptions
|
||||
cmd := &cobra.Command{
|
||||
Use: "install [OPTIONS] PLUGIN [KEY=VALUE...]",
|
||||
Short: "Install a plugin",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
options.remote = args[0]
|
||||
if len(args) > 1 {
|
||||
options.args = args[1:]
|
||||
}
|
||||
return runInstall(dockerCli, options)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
loadPullFlags(&options, flags)
|
||||
flags.BoolVar(&options.disable, "disable", false, "Do not enable the plugin on install")
|
||||
flags.StringVar(&options.localName, "alias", "", "Local name for plugin")
|
||||
return cmd
|
||||
}
|
||||
|
||||
type pluginRegistryService struct {
|
||||
registry.Service
|
||||
}
|
||||
|
||||
func (s pluginRegistryService) ResolveRepository(name reference.Named) (repoInfo *registry.RepositoryInfo, err error) {
|
||||
repoInfo, err = s.Service.ResolveRepository(name)
|
||||
if repoInfo != nil {
|
||||
repoInfo.Class = "plugin"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func newRegistryService() registry.Service {
|
||||
return pluginRegistryService{
|
||||
Service: registry.NewService(registry.ServiceOptions{V2Only: true}),
|
||||
}
|
||||
}
|
||||
|
||||
func buildPullConfig(ctx context.Context, dockerCli *command.DockerCli, opts pluginOptions, cmdName string) (types.PluginInstallOptions, error) {
|
||||
// Names with both tag and digest will be treated by the daemon
|
||||
// as a pull by digest with a local name for the tag
|
||||
// (if no local name is provided).
|
||||
ref, err := reference.ParseNormalizedNamed(opts.remote)
|
||||
if err != nil {
|
||||
return types.PluginInstallOptions{}, err
|
||||
}
|
||||
|
||||
repoInfo, err := registry.ParseRepositoryInfo(ref)
|
||||
if err != nil {
|
||||
return types.PluginInstallOptions{}, err
|
||||
}
|
||||
|
||||
remote := ref.String()
|
||||
|
||||
_, isCanonical := ref.(reference.Canonical)
|
||||
if command.IsTrusted() && !isCanonical {
|
||||
ref = reference.TagNameOnly(ref)
|
||||
nt, ok := ref.(reference.NamedTagged)
|
||||
if !ok {
|
||||
return types.PluginInstallOptions{}, errors.Errorf("invalid name: %s", ref.String())
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
trusted, err := image.TrustedReference(ctx, dockerCli, nt, newRegistryService())
|
||||
if err != nil {
|
||||
return types.PluginInstallOptions{}, err
|
||||
}
|
||||
remote = reference.FamiliarString(trusted)
|
||||
}
|
||||
|
||||
authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
|
||||
|
||||
encodedAuth, err := command.EncodeAuthToBase64(authConfig)
|
||||
if err != nil {
|
||||
return types.PluginInstallOptions{}, err
|
||||
}
|
||||
registryAuthFunc := command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, cmdName)
|
||||
|
||||
options := types.PluginInstallOptions{
|
||||
RegistryAuth: encodedAuth,
|
||||
RemoteRef: remote,
|
||||
Disabled: opts.disable,
|
||||
AcceptAllPermissions: opts.grantPerms,
|
||||
AcceptPermissionsFunc: acceptPrivileges(dockerCli, opts.remote),
|
||||
// TODO: Rename PrivilegeFunc, it has nothing to do with privileges
|
||||
PrivilegeFunc: registryAuthFunc,
|
||||
Args: opts.args,
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error {
|
||||
var localName string
|
||||
if opts.localName != "" {
|
||||
aref, err := reference.ParseNormalizedNamed(opts.localName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := aref.(reference.Canonical); ok {
|
||||
return errors.Errorf("invalid name: %s", opts.localName)
|
||||
}
|
||||
localName = reference.FamiliarString(reference.TagNameOnly(aref))
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
options, err := buildPullConfig(ctx, dockerCli, opts, "plugin install")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
responseBody, err := dockerCli.Client().PluginInstall(ctx, localName, options)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "(image) when fetching") {
|
||||
return errors.New(err.Error() + " - Use `docker image pull`")
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer responseBody.Close()
|
||||
if err := jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(dockerCli.Out(), "Installed plugin %s\n", opts.remote) // todo: return proper values from the API for this result
|
||||
return nil
|
||||
}
|
||||
|
||||
func acceptPrivileges(dockerCli *command.DockerCli, name string) func(privileges types.PluginPrivileges) (bool, error) {
|
||||
return func(privileges types.PluginPrivileges) (bool, error) {
|
||||
fmt.Fprintf(dockerCli.Out(), "Plugin %q is requesting the following privileges:\n", name)
|
||||
for _, privilege := range privileges {
|
||||
fmt.Fprintf(dockerCli.Out(), " - %s: %v\n", privilege.Name, privilege.Value)
|
||||
}
|
||||
return command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), "Do you grant the above permissions?"), nil
|
||||
}
|
||||
}
|
||||
63
cli/command/plugin/list.go
Normal file
63
cli/command/plugin/list.go
Normal file
@ -0,0 +1,63 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/docker/docker/cli/command/formatter"
|
||||
"github.com/docker/docker/opts"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type listOptions struct {
|
||||
quiet bool
|
||||
noTrunc bool
|
||||
format string
|
||||
filter opts.FilterOpt
|
||||
}
|
||||
|
||||
func newListCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
opts := listOptions{filter: opts.NewFilterOpt()}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "ls [OPTIONS]",
|
||||
Short: "List plugins",
|
||||
Aliases: []string{"list"},
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runList(dockerCli, opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display plugin IDs")
|
||||
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Don't truncate output")
|
||||
flags.StringVar(&opts.format, "format", "", "Pretty-print plugins using a Go template")
|
||||
flags.VarP(&opts.filter, "filter", "f", "Provide filter values (e.g. 'enabled=true')")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runList(dockerCli *command.DockerCli, opts listOptions) error {
|
||||
plugins, err := dockerCli.Client().PluginList(context.Background(), opts.filter.Value())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
format := opts.format
|
||||
if len(format) == 0 {
|
||||
if len(dockerCli.ConfigFile().PluginsFormat) > 0 && !opts.quiet {
|
||||
format = dockerCli.ConfigFile().PluginsFormat
|
||||
} else {
|
||||
format = formatter.TableFormatKey
|
||||
}
|
||||
}
|
||||
|
||||
pluginsCtx := formatter.Context{
|
||||
Output: dockerCli.Out(),
|
||||
Format: formatter.NewPluginFormat(format, opts.quiet),
|
||||
Trunc: !opts.noTrunc,
|
||||
}
|
||||
return formatter.PluginWrite(pluginsCtx, plugins)
|
||||
}
|
||||
69
cli/command/plugin/push.go
Normal file
69
cli/command/plugin/push.go
Normal file
@ -0,0 +1,69 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/docker/docker/cli/command/image"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newPushCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "push [OPTIONS] PLUGIN[:TAG]",
|
||||
Short: "Push a plugin to a registry",
|
||||
Args: cli.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runPush(dockerCli, args[0])
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
command.AddTrustSigningFlags(flags)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runPush(dockerCli *command.DockerCli, name string) error {
|
||||
named, err := reference.ParseNormalizedNamed(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := named.(reference.Canonical); ok {
|
||||
return errors.Errorf("invalid name: %s", name)
|
||||
}
|
||||
|
||||
named = reference.TagNameOnly(named)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
repoInfo, err := registry.ParseRepositoryInfo(named)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
|
||||
|
||||
encodedAuth, err := command.EncodeAuthToBase64(authConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
responseBody, err := dockerCli.Client().PluginPush(ctx, reference.FamiliarString(named), encodedAuth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer responseBody.Close()
|
||||
|
||||
if command.IsTrusted() {
|
||||
repoInfo.Class = "plugin"
|
||||
return image.PushTrustedReference(dockerCli, repoInfo, named, authConfig, responseBody)
|
||||
}
|
||||
|
||||
return jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), nil)
|
||||
}
|
||||
55
cli/command/plugin/remove.go
Normal file
55
cli/command/plugin/remove.go
Normal file
@ -0,0 +1,55 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type rmOptions struct {
|
||||
force bool
|
||||
|
||||
plugins []string
|
||||
}
|
||||
|
||||
func newRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var opts rmOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "rm [OPTIONS] PLUGIN [PLUGIN...]",
|
||||
Short: "Remove one or more plugins",
|
||||
Aliases: []string{"remove"},
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.plugins = args
|
||||
return runRemove(dockerCli, &opts)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.force, "force", "f", false, "Force the removal of an active plugin")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRemove(dockerCli *command.DockerCli, opts *rmOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
var errs cli.Errors
|
||||
for _, name := range opts.plugins {
|
||||
// TODO: pass names to api instead of making multiple api calls
|
||||
if err := dockerCli.Client().PluginRemove(ctx, name, types.PluginRemoveOptions{Force: opts.force}); err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
fmt.Fprintln(dockerCli.Out(), name)
|
||||
}
|
||||
// Do not simplify to `return errs` because even if errs == nil, it is not a nil-error interface value.
|
||||
if errs != nil {
|
||||
return errs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
22
cli/command/plugin/set.go
Normal file
22
cli/command/plugin/set.go
Normal file
@ -0,0 +1,22 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newSetCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "set PLUGIN KEY=VALUE [KEY=VALUE...]",
|
||||
Short: "Change settings for a plugin",
|
||||
Args: cli.RequiresMinArgs(2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return dockerCli.Client().PluginSet(context.Background(), args[0], args[1:])
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
90
cli/command/plugin/upgrade.go
Normal file
90
cli/command/plugin/upgrade.go
Normal file
@ -0,0 +1,90 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/cli"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newUpgradeCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||
var options pluginOptions
|
||||
cmd := &cobra.Command{
|
||||
Use: "upgrade [OPTIONS] PLUGIN [REMOTE]",
|
||||
Short: "Upgrade an existing plugin",
|
||||
Args: cli.RequiresRangeArgs(1, 2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
options.localName = args[0]
|
||||
if len(args) == 2 {
|
||||
options.remote = args[1]
|
||||
}
|
||||
return runUpgrade(dockerCli, options)
|
||||
},
|
||||
Tags: map[string]string{"version": "1.26"},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
loadPullFlags(&options, flags)
|
||||
flags.BoolVar(&options.skipRemoteCheck, "skip-remote-check", false, "Do not check if specified remote plugin matches existing plugin image")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUpgrade(dockerCli *command.DockerCli, opts pluginOptions) error {
|
||||
ctx := context.Background()
|
||||
p, _, err := dockerCli.Client().PluginInspectWithRaw(ctx, opts.localName)
|
||||
if err != nil {
|
||||
return errors.Errorf("error reading plugin data: %v", err)
|
||||
}
|
||||
|
||||
if p.Enabled {
|
||||
return errors.Errorf("the plugin must be disabled before upgrading")
|
||||
}
|
||||
|
||||
opts.localName = p.Name
|
||||
if opts.remote == "" {
|
||||
opts.remote = p.PluginReference
|
||||
}
|
||||
remote, err := reference.ParseNormalizedNamed(opts.remote)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error parsing remote upgrade image reference")
|
||||
}
|
||||
remote = reference.TagNameOnly(remote)
|
||||
|
||||
old, err := reference.ParseNormalizedNamed(p.PluginReference)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error parsing current image reference")
|
||||
}
|
||||
old = reference.TagNameOnly(old)
|
||||
|
||||
fmt.Fprintf(dockerCli.Out(), "Upgrading plugin %s from %s to %s\n", p.Name, reference.FamiliarString(old), reference.FamiliarString(remote))
|
||||
if !opts.skipRemoteCheck && remote.String() != old.String() {
|
||||
if !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), "Plugin images do not match, are you sure?") {
|
||||
return errors.New("canceling upgrade request")
|
||||
}
|
||||
}
|
||||
|
||||
options, err := buildPullConfig(ctx, dockerCli, opts, "plugin upgrade")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
responseBody, err := dockerCli.Client().PluginUpgrade(ctx, opts.localName, options)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "target is image") {
|
||||
return errors.New(err.Error() + " - Use `docker image pull`")
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer responseBody.Close()
|
||||
if err := jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(dockerCli.Out(), "Upgraded plugin %s to %s\n", opts.localName, opts.remote) // todo: return proper values from the API for this result
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user