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  }