github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/cli/command/plugin/install.go (about)

     1  package plugin
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/docker/distribution/reference"
     9  	"github.com/docker/docker/api/types"
    10  	"github.com/docker/docker/cli"
    11  	"github.com/docker/docker/cli/command"
    12  	"github.com/docker/docker/cli/command/image"
    13  	"github.com/docker/docker/pkg/jsonmessage"
    14  	"github.com/docker/docker/registry"
    15  	"github.com/spf13/cobra"
    16  	"github.com/spf13/pflag"
    17  	"golang.org/x/net/context"
    18  )
    19  
    20  type pluginOptions struct {
    21  	remote          string
    22  	localName       string
    23  	grantPerms      bool
    24  	disable         bool
    25  	args            []string
    26  	skipRemoteCheck bool
    27  }
    28  
    29  func loadPullFlags(opts *pluginOptions, flags *pflag.FlagSet) {
    30  	flags.BoolVar(&opts.grantPerms, "grant-all-permissions", false, "Grant all permissions necessary to run the plugin")
    31  	command.AddTrustVerificationFlags(flags)
    32  }
    33  
    34  func newInstallCommand(dockerCli *command.DockerCli) *cobra.Command {
    35  	var options pluginOptions
    36  	cmd := &cobra.Command{
    37  		Use:   "install [OPTIONS] PLUGIN [KEY=VALUE...]",
    38  		Short: "Install a plugin",
    39  		Args:  cli.RequiresMinArgs(1),
    40  		RunE: func(cmd *cobra.Command, args []string) error {
    41  			options.remote = args[0]
    42  			if len(args) > 1 {
    43  				options.args = args[1:]
    44  			}
    45  			return runInstall(dockerCli, options)
    46  		},
    47  	}
    48  
    49  	flags := cmd.Flags()
    50  	loadPullFlags(&options, flags)
    51  	flags.BoolVar(&options.disable, "disable", false, "Do not enable the plugin on install")
    52  	flags.StringVar(&options.localName, "alias", "", "Local name for plugin")
    53  	return cmd
    54  }
    55  
    56  type pluginRegistryService struct {
    57  	registry.Service
    58  }
    59  
    60  func (s pluginRegistryService) ResolveRepository(name reference.Named) (repoInfo *registry.RepositoryInfo, err error) {
    61  	repoInfo, err = s.Service.ResolveRepository(name)
    62  	if repoInfo != nil {
    63  		repoInfo.Class = "plugin"
    64  	}
    65  	return
    66  }
    67  
    68  func newRegistryService() registry.Service {
    69  	return pluginRegistryService{
    70  		Service: registry.NewService(registry.ServiceOptions{V2Only: true}),
    71  	}
    72  }
    73  
    74  func buildPullConfig(ctx context.Context, dockerCli *command.DockerCli, opts pluginOptions, cmdName string) (types.PluginInstallOptions, error) {
    75  	// Names with both tag and digest will be treated by the daemon
    76  	// as a pull by digest with a local name for the tag
    77  	// (if no local name is provided).
    78  	ref, err := reference.ParseNormalizedNamed(opts.remote)
    79  	if err != nil {
    80  		return types.PluginInstallOptions{}, err
    81  	}
    82  
    83  	repoInfo, err := registry.ParseRepositoryInfo(ref)
    84  	if err != nil {
    85  		return types.PluginInstallOptions{}, err
    86  	}
    87  
    88  	remote := ref.String()
    89  
    90  	_, isCanonical := ref.(reference.Canonical)
    91  	if command.IsTrusted() && !isCanonical {
    92  		ref = reference.TagNameOnly(ref)
    93  		nt, ok := ref.(reference.NamedTagged)
    94  		if !ok {
    95  			return types.PluginInstallOptions{}, fmt.Errorf("invalid name: %s", ref.String())
    96  		}
    97  
    98  		ctx := context.Background()
    99  		trusted, err := image.TrustedReference(ctx, dockerCli, nt, newRegistryService())
   100  		if err != nil {
   101  			return types.PluginInstallOptions{}, err
   102  		}
   103  		remote = reference.FamiliarString(trusted)
   104  	}
   105  
   106  	authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index)
   107  
   108  	encodedAuth, err := command.EncodeAuthToBase64(authConfig)
   109  	if err != nil {
   110  		return types.PluginInstallOptions{}, err
   111  	}
   112  	registryAuthFunc := command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, cmdName)
   113  
   114  	options := types.PluginInstallOptions{
   115  		RegistryAuth:          encodedAuth,
   116  		RemoteRef:             remote,
   117  		Disabled:              opts.disable,
   118  		AcceptAllPermissions:  opts.grantPerms,
   119  		AcceptPermissionsFunc: acceptPrivileges(dockerCli, opts.remote),
   120  		// TODO: Rename PrivilegeFunc, it has nothing to do with privileges
   121  		PrivilegeFunc: registryAuthFunc,
   122  		Args:          opts.args,
   123  	}
   124  	return options, nil
   125  }
   126  
   127  func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error {
   128  	var localName string
   129  	if opts.localName != "" {
   130  		aref, err := reference.ParseNormalizedNamed(opts.localName)
   131  		if err != nil {
   132  			return err
   133  		}
   134  		if _, ok := aref.(reference.Canonical); ok {
   135  			return fmt.Errorf("invalid name: %s", opts.localName)
   136  		}
   137  		localName = reference.FamiliarString(reference.TagNameOnly(aref))
   138  	}
   139  
   140  	ctx := context.Background()
   141  	options, err := buildPullConfig(ctx, dockerCli, opts, "plugin install")
   142  	if err != nil {
   143  		return err
   144  	}
   145  	responseBody, err := dockerCli.Client().PluginInstall(ctx, localName, options)
   146  	if err != nil {
   147  		if strings.Contains(err.Error(), "target is image") {
   148  			return errors.New(err.Error() + " - Use `docker image pull`")
   149  		}
   150  		return err
   151  	}
   152  	defer responseBody.Close()
   153  	if err := jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), nil); err != nil {
   154  		return err
   155  	}
   156  	fmt.Fprintf(dockerCli.Out(), "Installed plugin %s\n", opts.remote) // todo: return proper values from the API for this result
   157  	return nil
   158  }
   159  
   160  func acceptPrivileges(dockerCli *command.DockerCli, name string) func(privileges types.PluginPrivileges) (bool, error) {
   161  	return func(privileges types.PluginPrivileges) (bool, error) {
   162  		fmt.Fprintf(dockerCli.Out(), "Plugin %q is requesting the following privileges:\n", name)
   163  		for _, privilege := range privileges {
   164  			fmt.Fprintf(dockerCli.Out(), " - %s: %v\n", privilege.Name, privilege.Value)
   165  		}
   166  		return command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), "Do you grant the above permissions?"), nil
   167  	}
   168  }