github.com/olljanat/moby@v1.13.1/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  	distreference "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/pkg/ioutils"
    14  	"github.com/docker/docker/pkg/streamformatter"
    15  	"github.com/docker/docker/reference"
    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  	// NOTE: Using distribution reference to handle references
    50  	// containing both a name and digest
    51  	remoteRef, err := distreference.ParseNamed(remote)
    52  	if err != nil {
    53  		return nil, "", err
    54  	}
    55  
    56  	var tag string
    57  	if t, ok := remoteRef.(distreference.Tagged); ok {
    58  		tag = t.Tag()
    59  	}
    60  
    61  	// Convert distribution reference to docker reference
    62  	// TODO: remove when docker reference changes reconciled upstream
    63  	ref, err := reference.WithName(remoteRef.Name())
    64  	if err != nil {
    65  		return nil, "", err
    66  	}
    67  	if d, ok := remoteRef.(distreference.Digested); ok {
    68  		ref, err = reference.WithDigest(ref, d.Digest())
    69  		if err != nil {
    70  			return nil, "", err
    71  		}
    72  	} else if tag != "" {
    73  		ref, err = reference.WithTag(ref, tag)
    74  		if err != nil {
    75  			return nil, "", err
    76  		}
    77  	} else {
    78  		ref = reference.WithDefaultTag(ref)
    79  	}
    80  
    81  	return ref, tag, nil
    82  }
    83  
    84  func (pr *pluginRouter) getPrivileges(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    85  	if err := httputils.ParseForm(r); err != nil {
    86  		return err
    87  	}
    88  
    89  	metaHeaders, authConfig := parseHeaders(r.Header)
    90  
    91  	ref, _, err := parseRemoteRef(r.FormValue("remote"))
    92  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	privileges, err := pr.backend.Privileges(ctx, ref, metaHeaders, authConfig)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	return httputils.WriteJSON(w, http.StatusOK, privileges)
   101  }
   102  
   103  func (pr *pluginRouter) upgradePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   104  	if err := httputils.ParseForm(r); err != nil {
   105  		return errors.Wrap(err, "failed to parse form")
   106  	}
   107  
   108  	var privileges types.PluginPrivileges
   109  	dec := json.NewDecoder(r.Body)
   110  	if err := dec.Decode(&privileges); err != nil {
   111  		return errors.Wrap(err, "failed to parse privileges")
   112  	}
   113  	if dec.More() {
   114  		return errors.New("invalid privileges")
   115  	}
   116  
   117  	metaHeaders, authConfig := parseHeaders(r.Header)
   118  	ref, tag, err := parseRemoteRef(r.FormValue("remote"))
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	name, err := getName(ref, tag, vars["name"])
   124  	if err != nil {
   125  		return err
   126  	}
   127  	w.Header().Set("Docker-Plugin-Name", name)
   128  
   129  	w.Header().Set("Content-Type", "application/json")
   130  	output := ioutils.NewWriteFlusher(w)
   131  
   132  	if err := pr.backend.Upgrade(ctx, ref, name, metaHeaders, authConfig, privileges, output); err != nil {
   133  		if !output.Flushed() {
   134  			return err
   135  		}
   136  		output.Write(streamformatter.NewJSONStreamFormatter().FormatError(err))
   137  	}
   138  
   139  	return nil
   140  }
   141  
   142  func (pr *pluginRouter) pullPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   143  	if err := httputils.ParseForm(r); err != nil {
   144  		return errors.Wrap(err, "failed to parse form")
   145  	}
   146  
   147  	var privileges types.PluginPrivileges
   148  	dec := json.NewDecoder(r.Body)
   149  	if err := dec.Decode(&privileges); err != nil {
   150  		return errors.Wrap(err, "failed to parse privileges")
   151  	}
   152  	if dec.More() {
   153  		return errors.New("invalid privileges")
   154  	}
   155  
   156  	metaHeaders, authConfig := parseHeaders(r.Header)
   157  	ref, tag, err := parseRemoteRef(r.FormValue("remote"))
   158  	if err != nil {
   159  		return err
   160  	}
   161  
   162  	name, err := getName(ref, tag, r.FormValue("name"))
   163  	if err != nil {
   164  		return err
   165  	}
   166  	w.Header().Set("Docker-Plugin-Name", name)
   167  
   168  	w.Header().Set("Content-Type", "application/json")
   169  	output := ioutils.NewWriteFlusher(w)
   170  
   171  	if err := pr.backend.Pull(ctx, ref, name, metaHeaders, authConfig, privileges, output); err != nil {
   172  		if !output.Flushed() {
   173  			return err
   174  		}
   175  		output.Write(streamformatter.NewJSONStreamFormatter().FormatError(err))
   176  	}
   177  
   178  	return nil
   179  }
   180  
   181  func getName(ref reference.Named, tag, name string) (string, error) {
   182  	if name == "" {
   183  		if _, ok := ref.(reference.Canonical); ok {
   184  			trimmed := reference.TrimNamed(ref)
   185  			if tag != "" {
   186  				nt, err := reference.WithTag(trimmed, tag)
   187  				if err != nil {
   188  					return "", err
   189  				}
   190  				name = nt.String()
   191  			} else {
   192  				name = reference.WithDefaultTag(trimmed).String()
   193  			}
   194  		} else {
   195  			name = ref.String()
   196  		}
   197  	} else {
   198  		localRef, err := reference.ParseNamed(name)
   199  		if err != nil {
   200  			return "", err
   201  		}
   202  		if _, ok := localRef.(reference.Canonical); ok {
   203  			return "", errors.New("cannot use digest in plugin tag")
   204  		}
   205  		if distreference.IsNameOnly(localRef) {
   206  			// TODO: log change in name to out stream
   207  			name = reference.WithDefaultTag(localRef).String()
   208  		}
   209  	}
   210  	return name, nil
   211  }
   212  
   213  func (pr *pluginRouter) createPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   214  	if err := httputils.ParseForm(r); err != nil {
   215  		return err
   216  	}
   217  
   218  	options := &types.PluginCreateOptions{
   219  		RepoName: r.FormValue("name")}
   220  
   221  	if err := pr.backend.CreateFromContext(ctx, r.Body, options); err != nil {
   222  		return err
   223  	}
   224  	//TODO: send progress bar
   225  	w.WriteHeader(http.StatusNoContent)
   226  	return nil
   227  }
   228  
   229  func (pr *pluginRouter) enablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   230  	if err := httputils.ParseForm(r); err != nil {
   231  		return err
   232  	}
   233  
   234  	name := vars["name"]
   235  	timeout, err := strconv.Atoi(r.Form.Get("timeout"))
   236  	if err != nil {
   237  		return err
   238  	}
   239  	config := &types.PluginEnableConfig{Timeout: timeout}
   240  
   241  	return pr.backend.Enable(name, config)
   242  }
   243  
   244  func (pr *pluginRouter) disablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   245  	if err := httputils.ParseForm(r); err != nil {
   246  		return err
   247  	}
   248  
   249  	name := vars["name"]
   250  	config := &types.PluginDisableConfig{
   251  		ForceDisable: httputils.BoolValue(r, "force"),
   252  	}
   253  
   254  	return pr.backend.Disable(name, config)
   255  }
   256  
   257  func (pr *pluginRouter) removePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   258  	if err := httputils.ParseForm(r); err != nil {
   259  		return err
   260  	}
   261  
   262  	name := vars["name"]
   263  	config := &types.PluginRmConfig{
   264  		ForceRemove: httputils.BoolValue(r, "force"),
   265  	}
   266  	return pr.backend.Remove(name, config)
   267  }
   268  
   269  func (pr *pluginRouter) pushPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   270  	if err := httputils.ParseForm(r); err != nil {
   271  		return errors.Wrap(err, "failed to parse form")
   272  	}
   273  
   274  	metaHeaders, authConfig := parseHeaders(r.Header)
   275  
   276  	w.Header().Set("Content-Type", "application/json")
   277  	output := ioutils.NewWriteFlusher(w)
   278  
   279  	if err := pr.backend.Push(ctx, vars["name"], metaHeaders, authConfig, output); err != nil {
   280  		if !output.Flushed() {
   281  			return err
   282  		}
   283  		output.Write(streamformatter.NewJSONStreamFormatter().FormatError(err))
   284  	}
   285  	return nil
   286  }
   287  
   288  func (pr *pluginRouter) setPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   289  	var args []string
   290  	if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
   291  		return err
   292  	}
   293  	if err := pr.backend.Set(vars["name"], args); err != nil {
   294  		return err
   295  	}
   296  	w.WriteHeader(http.StatusNoContent)
   297  	return nil
   298  }
   299  
   300  func (pr *pluginRouter) listPlugins(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   301  	l, err := pr.backend.List()
   302  	if err != nil {
   303  		return err
   304  	}
   305  	return httputils.WriteJSON(w, http.StatusOK, l)
   306  }
   307  
   308  func (pr *pluginRouter) inspectPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   309  	result, err := pr.backend.Inspect(vars["name"])
   310  	if err != nil {
   311  		return err
   312  	}
   313  	return httputils.WriteJSON(w, http.StatusOK, result)
   314  }