github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/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) pullPlugin(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 119 ref, tag, err := parseRemoteRef(r.FormValue("remote")) 120 if err != nil { 121 return err 122 } 123 124 name := r.FormValue("name") 125 if name == "" { 126 if _, ok := ref.(reference.Canonical); ok { 127 trimmed := reference.TrimNamed(ref) 128 if tag != "" { 129 nt, err := reference.WithTag(trimmed, tag) 130 if err != nil { 131 return err 132 } 133 name = nt.String() 134 } else { 135 name = reference.WithDefaultTag(trimmed).String() 136 } 137 } else { 138 name = ref.String() 139 } 140 } else { 141 localRef, err := reference.ParseNamed(name) 142 if err != nil { 143 return err 144 } 145 if _, ok := localRef.(reference.Canonical); ok { 146 return errors.New("cannot use digest in plugin tag") 147 } 148 if distreference.IsNameOnly(localRef) { 149 // TODO: log change in name to out stream 150 name = reference.WithDefaultTag(localRef).String() 151 } 152 } 153 w.Header().Set("Docker-Plugin-Name", name) 154 155 w.Header().Set("Content-Type", "application/json") 156 output := ioutils.NewWriteFlusher(w) 157 158 if err := pr.backend.Pull(ctx, ref, name, metaHeaders, authConfig, privileges, output); err != nil { 159 if !output.Flushed() { 160 return err 161 } 162 output.Write(streamformatter.NewJSONStreamFormatter().FormatError(err)) 163 } 164 165 return nil 166 } 167 168 func (pr *pluginRouter) createPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 169 if err := httputils.ParseForm(r); err != nil { 170 return err 171 } 172 173 options := &types.PluginCreateOptions{ 174 RepoName: r.FormValue("name")} 175 176 if err := pr.backend.CreateFromContext(ctx, r.Body, options); err != nil { 177 return err 178 } 179 //TODO: send progress bar 180 w.WriteHeader(http.StatusNoContent) 181 return nil 182 } 183 184 func (pr *pluginRouter) enablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 185 if err := httputils.ParseForm(r); err != nil { 186 return err 187 } 188 189 name := vars["name"] 190 timeout, err := strconv.Atoi(r.Form.Get("timeout")) 191 if err != nil { 192 return err 193 } 194 config := &types.PluginEnableConfig{Timeout: timeout} 195 196 return pr.backend.Enable(name, config) 197 } 198 199 func (pr *pluginRouter) disablePlugin(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 config := &types.PluginDisableConfig{ 206 ForceDisable: httputils.BoolValue(r, "force"), 207 } 208 209 return pr.backend.Disable(name, config) 210 } 211 212 func (pr *pluginRouter) removePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 213 if err := httputils.ParseForm(r); err != nil { 214 return err 215 } 216 217 name := vars["name"] 218 config := &types.PluginRmConfig{ 219 ForceRemove: httputils.BoolValue(r, "force"), 220 } 221 return pr.backend.Remove(name, config) 222 } 223 224 func (pr *pluginRouter) pushPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 225 if err := httputils.ParseForm(r); err != nil { 226 return errors.Wrap(err, "failed to parse form") 227 } 228 229 metaHeaders, authConfig := parseHeaders(r.Header) 230 231 w.Header().Set("Content-Type", "application/json") 232 output := ioutils.NewWriteFlusher(w) 233 234 if err := pr.backend.Push(ctx, vars["name"], metaHeaders, authConfig, output); err != nil { 235 if !output.Flushed() { 236 return err 237 } 238 output.Write(streamformatter.NewJSONStreamFormatter().FormatError(err)) 239 } 240 return nil 241 } 242 243 func (pr *pluginRouter) setPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 244 var args []string 245 if err := json.NewDecoder(r.Body).Decode(&args); err != nil { 246 return err 247 } 248 if err := pr.backend.Set(vars["name"], args); err != nil { 249 return err 250 } 251 w.WriteHeader(http.StatusNoContent) 252 return nil 253 } 254 255 func (pr *pluginRouter) listPlugins(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 256 l, err := pr.backend.List() 257 if err != nil { 258 return err 259 } 260 return httputils.WriteJSON(w, http.StatusOK, l) 261 } 262 263 func (pr *pluginRouter) inspectPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 264 result, err := pr.backend.Inspect(vars["name"]) 265 if err != nil { 266 return err 267 } 268 return httputils.WriteJSON(w, http.StatusOK, result) 269 }