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"
20 func parseHeaders(headers http.Header) (map[string][]string, *types.AuthConfig) {
22 metaHeaders := map[string][]string{}
23 for k, v := range headers {
24 if strings.HasPrefix(k, "X-Meta-") {
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{}
39 return metaHeaders, authConfig
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
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)
54 type canonicalWithTag interface {
59 if canonical, ok := remoteRef.(canonicalWithTag); ok {
60 remoteRef, err = reference.WithDigest(reference.TrimNamed(remoteRef), canonical.Digest())
64 return remoteRef, canonical.Tag(), nil
67 remoteRef = reference.TagNameOnly(remoteRef)
69 return remoteRef, "", nil
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 {
77 metaHeaders, authConfig := parseHeaders(r.Header)
79 ref, _, err := parseRemoteRef(r.FormValue("remote"))
84 privileges, err := pr.backend.Privileges(ctx, ref, metaHeaders, authConfig)
88 return httputils.WriteJSON(w, http.StatusOK, privileges)
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")
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")
102 return errors.New("invalid privileges")
105 metaHeaders, authConfig := parseHeaders(r.Header)
106 ref, tag, err := parseRemoteRef(r.FormValue("remote"))
111 name, err := getName(ref, tag, vars["name"])
115 w.Header().Set("Docker-Plugin-Name", name)
117 w.Header().Set("Content-Type", "application/json")
118 output := ioutils.NewWriteFlusher(w)
120 if err := pr.backend.Upgrade(ctx, ref, name, metaHeaders, authConfig, privileges, output); err != nil {
121 if !output.Flushed() {
124 output.Write(streamformatter.FormatError(err))
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")
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")
141 return errors.New("invalid privileges")
144 metaHeaders, authConfig := parseHeaders(r.Header)
145 ref, tag, err := parseRemoteRef(r.FormValue("remote"))
150 name, err := getName(ref, tag, r.FormValue("name"))
154 w.Header().Set("Docker-Plugin-Name", name)
156 w.Header().Set("Content-Type", "application/json")
157 output := ioutils.NewWriteFlusher(w)
159 if err := pr.backend.Pull(ctx, ref, name, metaHeaders, authConfig, privileges, output); err != nil {
160 if !output.Flushed() {
163 output.Write(streamformatter.FormatError(err))
169 func getName(ref reference.Named, tag, name string) (string, error) {
171 if _, ok := ref.(reference.Canonical); ok {
172 trimmed := reference.TrimNamed(ref)
174 nt, err := reference.WithTag(trimmed, tag)
178 name = reference.FamiliarString(nt)
180 name = reference.FamiliarString(reference.TagNameOnly(trimmed))
183 name = reference.FamiliarString(ref)
186 localRef, err := reference.ParseNormalizedNamed(name)
190 if _, ok := localRef.(reference.Canonical); ok {
191 return "", errors.New("cannot use digest in plugin tag")
193 if reference.IsNameOnly(localRef) {
194 // TODO: log change in name to out stream
195 name = reference.FamiliarString(reference.TagNameOnly(localRef))
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 {
206 options := &types.PluginCreateOptions{
207 RepoName: r.FormValue("name")}
209 if err := pr.backend.CreateFromContext(ctx, r.Body, options); err != nil {
212 //TODO: send progress bar
213 w.WriteHeader(http.StatusNoContent)
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 {
223 timeout, err := strconv.Atoi(r.Form.Get("timeout"))
227 config := &types.PluginEnableConfig{Timeout: timeout}
229 return pr.backend.Enable(name, config)
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 {
238 config := &types.PluginDisableConfig{
239 ForceDisable: httputils.BoolValue(r, "force"),
242 return pr.backend.Disable(name, config)
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 {
251 config := &types.PluginRmConfig{
252 ForceRemove: httputils.BoolValue(r, "force"),
254 return pr.backend.Remove(name, config)
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")
262 metaHeaders, authConfig := parseHeaders(r.Header)
264 w.Header().Set("Content-Type", "application/json")
265 output := ioutils.NewWriteFlusher(w)
267 if err := pr.backend.Push(ctx, vars["name"], metaHeaders, authConfig, output); err != nil {
268 if !output.Flushed() {
271 output.Write(streamformatter.FormatError(err))
276 func (pr *pluginRouter) setPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
278 if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
281 if err := pr.backend.Set(vars["name"], args); err != nil {
284 w.WriteHeader(http.StatusNoContent)
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 {
293 pluginFilters, err := filters.FromParam(r.Form.Get("filters"))
297 l, err := pr.backend.List(pluginFilters)
301 return httputils.WriteJSON(w, http.StatusOK, l)
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"])
309 return httputils.WriteJSON(w, http.StatusOK, result)