github.com/rawahars/moby@v24.0.4+incompatible/api/server/router/plugin/plugin_routes.go (about)

     1  package plugin // import "github.com/docker/docker/api/server/router/plugin"
     2  
     3  import (
     4  	"context"
     5  	"net/http"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/docker/distribution/reference"
    10  	"github.com/docker/docker/api/server/httputils"
    11  	"github.com/docker/docker/api/types"
    12  	"github.com/docker/docker/api/types/filters"
    13  	"github.com/docker/docker/api/types/registry"
    14  	"github.com/docker/docker/pkg/ioutils"
    15  	"github.com/docker/docker/pkg/streamformatter"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  func parseHeaders(headers http.Header) (map[string][]string, *registry.AuthConfig) {
    20  	metaHeaders := map[string][]string{}
    21  	for k, v := range headers {
    22  		if strings.HasPrefix(k, "X-Meta-") {
    23  			metaHeaders[k] = v
    24  		}
    25  	}
    26  
    27  	// Ignore invalid AuthConfig to increase compatibility with the existing API.
    28  	authConfig, _ := registry.DecodeAuthConfig(headers.Get(registry.AuthHeader))
    29  	return metaHeaders, authConfig
    30  }
    31  
    32  // parseRemoteRef parses the remote reference into a reference.Named
    33  // returning the tag associated with the reference. In the case the
    34  // given reference string includes both digest and tag, the returned
    35  // reference will have the digest without the tag, but the tag will
    36  // be returned.
    37  func parseRemoteRef(remote string) (reference.Named, string, error) {
    38  	// Parse remote reference, supporting remotes with name and tag
    39  	remoteRef, err := reference.ParseNormalizedNamed(remote)
    40  	if err != nil {
    41  		return nil, "", err
    42  	}
    43  
    44  	type canonicalWithTag interface {
    45  		reference.Canonical
    46  		Tag() string
    47  	}
    48  
    49  	if canonical, ok := remoteRef.(canonicalWithTag); ok {
    50  		remoteRef, err = reference.WithDigest(reference.TrimNamed(remoteRef), canonical.Digest())
    51  		if err != nil {
    52  			return nil, "", err
    53  		}
    54  		return remoteRef, canonical.Tag(), nil
    55  	}
    56  
    57  	remoteRef = reference.TagNameOnly(remoteRef)
    58  
    59  	return remoteRef, "", nil
    60  }
    61  
    62  func (pr *pluginRouter) getPrivileges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    63  	if err := httputils.ParseForm(r); err != nil {
    64  		return err
    65  	}
    66  
    67  	metaHeaders, authConfig := parseHeaders(r.Header)
    68  
    69  	ref, _, err := parseRemoteRef(r.FormValue("remote"))
    70  	if err != nil {
    71  		return err
    72  	}
    73  
    74  	privileges, err := pr.backend.Privileges(ctx, ref, metaHeaders, authConfig)
    75  	if err != nil {
    76  		return err
    77  	}
    78  	return httputils.WriteJSON(w, http.StatusOK, privileges)
    79  }
    80  
    81  func (pr *pluginRouter) upgradePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    82  	if err := httputils.ParseForm(r); err != nil {
    83  		return errors.Wrap(err, "failed to parse form")
    84  	}
    85  
    86  	var privileges types.PluginPrivileges
    87  	if err := httputils.ReadJSON(r, &privileges); err != nil {
    88  		return err
    89  	}
    90  
    91  	metaHeaders, authConfig := parseHeaders(r.Header)
    92  	ref, tag, err := parseRemoteRef(r.FormValue("remote"))
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	name, err := getName(ref, tag, vars["name"])
    98  	if err != nil {
    99  		return err
   100  	}
   101  	w.Header().Set("Docker-Plugin-Name", name)
   102  
   103  	w.Header().Set("Content-Type", "application/json")
   104  	output := ioutils.NewWriteFlusher(w)
   105  
   106  	if err := pr.backend.Upgrade(ctx, ref, name, metaHeaders, authConfig, privileges, output); err != nil {
   107  		if !output.Flushed() {
   108  			return err
   109  		}
   110  		_, _ = output.Write(streamformatter.FormatError(err))
   111  	}
   112  
   113  	return nil
   114  }
   115  
   116  func (pr *pluginRouter) pullPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   117  	if err := httputils.ParseForm(r); err != nil {
   118  		return errors.Wrap(err, "failed to parse form")
   119  	}
   120  
   121  	var privileges types.PluginPrivileges
   122  	if err := httputils.ReadJSON(r, &privileges); err != nil {
   123  		return err
   124  	}
   125  
   126  	metaHeaders, authConfig := parseHeaders(r.Header)
   127  	ref, tag, err := parseRemoteRef(r.FormValue("remote"))
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	name, err := getName(ref, tag, r.FormValue("name"))
   133  	if err != nil {
   134  		return err
   135  	}
   136  	w.Header().Set("Docker-Plugin-Name", name)
   137  
   138  	w.Header().Set("Content-Type", "application/json")
   139  	output := ioutils.NewWriteFlusher(w)
   140  
   141  	if err := pr.backend.Pull(ctx, ref, name, metaHeaders, authConfig, privileges, output); err != nil {
   142  		if !output.Flushed() {
   143  			return err
   144  		}
   145  		_, _ = output.Write(streamformatter.FormatError(err))
   146  	}
   147  
   148  	return nil
   149  }
   150  
   151  func getName(ref reference.Named, tag, name string) (string, error) {
   152  	if name == "" {
   153  		if _, ok := ref.(reference.Canonical); ok {
   154  			trimmed := reference.TrimNamed(ref)
   155  			if tag != "" {
   156  				nt, err := reference.WithTag(trimmed, tag)
   157  				if err != nil {
   158  					return "", err
   159  				}
   160  				name = reference.FamiliarString(nt)
   161  			} else {
   162  				name = reference.FamiliarString(reference.TagNameOnly(trimmed))
   163  			}
   164  		} else {
   165  			name = reference.FamiliarString(ref)
   166  		}
   167  	} else {
   168  		localRef, err := reference.ParseNormalizedNamed(name)
   169  		if err != nil {
   170  			return "", err
   171  		}
   172  		if _, ok := localRef.(reference.Canonical); ok {
   173  			return "", errors.New("cannot use digest in plugin tag")
   174  		}
   175  		if reference.IsNameOnly(localRef) {
   176  			// TODO: log change in name to out stream
   177  			name = reference.FamiliarString(reference.TagNameOnly(localRef))
   178  		}
   179  	}
   180  	return name, nil
   181  }
   182  
   183  func (pr *pluginRouter) createPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   184  	if err := httputils.ParseForm(r); err != nil {
   185  		return err
   186  	}
   187  
   188  	options := &types.PluginCreateOptions{
   189  		RepoName: r.FormValue("name")}
   190  
   191  	if err := pr.backend.CreateFromContext(ctx, r.Body, options); err != nil {
   192  		return err
   193  	}
   194  	// TODO: send progress bar
   195  	w.WriteHeader(http.StatusNoContent)
   196  	return nil
   197  }
   198  
   199  func (pr *pluginRouter) enablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   200  	if err := httputils.ParseForm(r); err != nil {
   201  		return err
   202  	}
   203  
   204  	name := vars["name"]
   205  	timeout, err := strconv.Atoi(r.Form.Get("timeout"))
   206  	if err != nil {
   207  		return err
   208  	}
   209  	config := &types.PluginEnableConfig{Timeout: timeout}
   210  
   211  	return pr.backend.Enable(name, config)
   212  }
   213  
   214  func (pr *pluginRouter) disablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   215  	if err := httputils.ParseForm(r); err != nil {
   216  		return err
   217  	}
   218  
   219  	name := vars["name"]
   220  	config := &types.PluginDisableConfig{
   221  		ForceDisable: httputils.BoolValue(r, "force"),
   222  	}
   223  
   224  	return pr.backend.Disable(name, config)
   225  }
   226  
   227  func (pr *pluginRouter) removePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   228  	if err := httputils.ParseForm(r); err != nil {
   229  		return err
   230  	}
   231  
   232  	name := vars["name"]
   233  	config := &types.PluginRmConfig{
   234  		ForceRemove: httputils.BoolValue(r, "force"),
   235  	}
   236  	return pr.backend.Remove(name, config)
   237  }
   238  
   239  func (pr *pluginRouter) pushPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   240  	if err := httputils.ParseForm(r); err != nil {
   241  		return errors.Wrap(err, "failed to parse form")
   242  	}
   243  
   244  	metaHeaders, authConfig := parseHeaders(r.Header)
   245  
   246  	w.Header().Set("Content-Type", "application/json")
   247  	output := ioutils.NewWriteFlusher(w)
   248  
   249  	if err := pr.backend.Push(ctx, vars["name"], metaHeaders, authConfig, output); err != nil {
   250  		if !output.Flushed() {
   251  			return err
   252  		}
   253  		_, _ = output.Write(streamformatter.FormatError(err))
   254  	}
   255  	return nil
   256  }
   257  
   258  func (pr *pluginRouter) setPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   259  	var args []string
   260  	if err := httputils.ReadJSON(r, &args); err != nil {
   261  		return err
   262  	}
   263  	if err := pr.backend.Set(vars["name"], args); err != nil {
   264  		return err
   265  	}
   266  	w.WriteHeader(http.StatusNoContent)
   267  	return nil
   268  }
   269  
   270  func (pr *pluginRouter) listPlugins(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   271  	if err := httputils.ParseForm(r); err != nil {
   272  		return err
   273  	}
   274  
   275  	pluginFilters, err := filters.FromJSON(r.Form.Get("filters"))
   276  	if err != nil {
   277  		return err
   278  	}
   279  	l, err := pr.backend.List(pluginFilters)
   280  	if err != nil {
   281  		return err
   282  	}
   283  	return httputils.WriteJSON(w, http.StatusOK, l)
   284  }
   285  
   286  func (pr *pluginRouter) inspectPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   287  	result, err := pr.backend.Inspect(vars["name"])
   288  	if err != nil {
   289  		return err
   290  	}
   291  	return httputils.WriteJSON(w, http.StatusOK, result)
   292  }