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