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 }