github.com/rish1988/moby@v25.0.2+incompatible/client/plugin_install.go (about) 1 package client // import "github.com/docker/docker/client" 2 3 import ( 4 "context" 5 "encoding/json" 6 "io" 7 "net/http" 8 "net/url" 9 10 "github.com/distribution/reference" 11 "github.com/docker/docker/api/types" 12 "github.com/docker/docker/api/types/registry" 13 "github.com/docker/docker/errdefs" 14 "github.com/pkg/errors" 15 ) 16 17 // PluginInstall installs a plugin 18 func (cli *Client) PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) (rc io.ReadCloser, err error) { 19 query := url.Values{} 20 if _, err := reference.ParseNormalizedNamed(options.RemoteRef); err != nil { 21 return nil, errors.Wrap(err, "invalid remote reference") 22 } 23 query.Set("remote", options.RemoteRef) 24 25 privileges, err := cli.checkPluginPermissions(ctx, query, options) 26 if err != nil { 27 return nil, err 28 } 29 30 // set name for plugin pull, if empty should default to remote reference 31 query.Set("name", name) 32 33 resp, err := cli.tryPluginPull(ctx, query, privileges, options.RegistryAuth) 34 if err != nil { 35 return nil, err 36 } 37 38 name = resp.header.Get("Docker-Plugin-Name") 39 40 pr, pw := io.Pipe() 41 go func() { // todo: the client should probably be designed more around the actual api 42 _, err := io.Copy(pw, resp.body) 43 if err != nil { 44 pw.CloseWithError(err) 45 return 46 } 47 defer func() { 48 if err != nil { 49 delResp, _ := cli.delete(ctx, "/plugins/"+name, nil, nil) 50 ensureReaderClosed(delResp) 51 } 52 }() 53 if len(options.Args) > 0 { 54 if err := cli.PluginSet(ctx, name, options.Args); err != nil { 55 pw.CloseWithError(err) 56 return 57 } 58 } 59 60 if options.Disabled { 61 pw.Close() 62 return 63 } 64 65 enableErr := cli.PluginEnable(ctx, name, types.PluginEnableOptions{Timeout: 0}) 66 pw.CloseWithError(enableErr) 67 }() 68 return pr, nil 69 } 70 71 func (cli *Client) tryPluginPrivileges(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) { 72 return cli.get(ctx, "/plugins/privileges", query, http.Header{ 73 registry.AuthHeader: {registryAuth}, 74 }) 75 } 76 77 func (cli *Client) tryPluginPull(ctx context.Context, query url.Values, privileges types.PluginPrivileges, registryAuth string) (serverResponse, error) { 78 return cli.post(ctx, "/plugins/pull", query, privileges, http.Header{ 79 registry.AuthHeader: {registryAuth}, 80 }) 81 } 82 83 func (cli *Client) checkPluginPermissions(ctx context.Context, query url.Values, options types.PluginInstallOptions) (types.PluginPrivileges, error) { 84 resp, err := cli.tryPluginPrivileges(ctx, query, options.RegistryAuth) 85 if errdefs.IsUnauthorized(err) && options.PrivilegeFunc != nil { 86 // todo: do inspect before to check existing name before checking privileges 87 newAuthHeader, privilegeErr := options.PrivilegeFunc() 88 if privilegeErr != nil { 89 ensureReaderClosed(resp) 90 return nil, privilegeErr 91 } 92 options.RegistryAuth = newAuthHeader 93 resp, err = cli.tryPluginPrivileges(ctx, query, options.RegistryAuth) 94 } 95 if err != nil { 96 ensureReaderClosed(resp) 97 return nil, err 98 } 99 100 var privileges types.PluginPrivileges 101 if err := json.NewDecoder(resp.body).Decode(&privileges); err != nil { 102 ensureReaderClosed(resp) 103 return nil, err 104 } 105 ensureReaderClosed(resp) 106 107 if !options.AcceptAllPermissions && options.AcceptPermissionsFunc != nil && len(privileges) > 0 { 108 accept, err := options.AcceptPermissionsFunc(privileges) 109 if err != nil { 110 return nil, err 111 } 112 if !accept { 113 return nil, errors.Errorf("permission denied while installing plugin %s", options.RemoteRef) 114 } 115 } 116 return privileges, nil 117 }