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