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