github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/api/server/router/plugin/plugin_routes.go (about)

     1  package plugin // import "github.com/Prakhar-Agarwal-byte/moby/api/server/router/plugin"
     2  
     3  import (
     4  	"context"
     5  	"net/http"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/Prakhar-Agarwal-byte/moby/api/server/httputils"
    10  	"github.com/Prakhar-Agarwal-byte/moby/api/types"
    11  	"github.com/Prakhar-Agarwal-byte/moby/api/types/filters"
    12  	"github.com/Prakhar-Agarwal-byte/moby/api/types/registry"
    13  	"github.com/Prakhar-Agarwal-byte/moby/pkg/ioutils"
    14  	"github.com/Prakhar-Agarwal-byte/moby/pkg/streamformatter"
    15  	"github.com/distribution/reference"
    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  
   192  	if err := pr.backend.CreateFromContext(ctx, r.Body, options); err != nil {
   193  		return err
   194  	}
   195  	// TODO: send progress bar
   196  	w.WriteHeader(http.StatusNoContent)
   197  	return nil
   198  }
   199  
   200  func (pr *pluginRouter) enablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   201  	if err := httputils.ParseForm(r); err != nil {
   202  		return err
   203  	}
   204  
   205  	name := vars["name"]
   206  	timeout, err := strconv.Atoi(r.Form.Get("timeout"))
   207  	if err != nil {
   208  		return err
   209  	}
   210  	config := &types.PluginEnableConfig{Timeout: timeout}
   211  
   212  	return pr.backend.Enable(name, config)
   213  }
   214  
   215  func (pr *pluginRouter) disablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   216  	if err := httputils.ParseForm(r); err != nil {
   217  		return err
   218  	}
   219  
   220  	name := vars["name"]
   221  	config := &types.PluginDisableConfig{
   222  		ForceDisable: httputils.BoolValue(r, "force"),
   223  	}
   224  
   225  	return pr.backend.Disable(name, config)
   226  }
   227  
   228  func (pr *pluginRouter) removePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   229  	if err := httputils.ParseForm(r); err != nil {
   230  		return err
   231  	}
   232  
   233  	name := vars["name"]
   234  	config := &types.PluginRmConfig{
   235  		ForceRemove: httputils.BoolValue(r, "force"),
   236  	}
   237  	return pr.backend.Remove(name, config)
   238  }
   239  
   240  func (pr *pluginRouter) pushPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   241  	if err := httputils.ParseForm(r); err != nil {
   242  		return errors.Wrap(err, "failed to parse form")
   243  	}
   244  
   245  	metaHeaders, authConfig := parseHeaders(r.Header)
   246  
   247  	w.Header().Set("Content-Type", "application/json")
   248  	output := ioutils.NewWriteFlusher(w)
   249  
   250  	if err := pr.backend.Push(ctx, vars["name"], metaHeaders, authConfig, output); err != nil {
   251  		if !output.Flushed() {
   252  			return err
   253  		}
   254  		_, _ = output.Write(streamformatter.FormatError(err))
   255  	}
   256  	return nil
   257  }
   258  
   259  func (pr *pluginRouter) setPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   260  	var args []string
   261  	if err := httputils.ReadJSON(r, &args); err != nil {
   262  		return err
   263  	}
   264  	if err := pr.backend.Set(vars["name"], args); err != nil {
   265  		return err
   266  	}
   267  	w.WriteHeader(http.StatusNoContent)
   268  	return nil
   269  }
   270  
   271  func (pr *pluginRouter) listPlugins(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   272  	if err := httputils.ParseForm(r); err != nil {
   273  		return err
   274  	}
   275  
   276  	pluginFilters, err := filters.FromJSON(r.Form.Get("filters"))
   277  	if err != nil {
   278  		return err
   279  	}
   280  	l, err := pr.backend.List(pluginFilters)
   281  	if err != nil {
   282  		return err
   283  	}
   284  	return httputils.WriteJSON(w, http.StatusOK, l)
   285  }
   286  
   287  func (pr *pluginRouter) inspectPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   288  	result, err := pr.backend.Inspect(vars["name"])
   289  	if err != nil {
   290  		return err
   291  	}
   292  	return httputils.WriteJSON(w, http.StatusOK, result)
   293  }