github.com/cookieai-jar/moby@v17.12.1-ce-rc2+incompatible/api/server/router/plugin/plugin_routes.go (about)

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