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 }