github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/cli/command/plugin/install.go (about)

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