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