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  }