github.com/rumpl/bof@v23.0.0-rc.2+incompatible/api/server/router/plugin/plugin_routes.go (about) 1 package plugin // import "github.com/docker/docker/api/server/router/plugin" 2 3 import ( 4 "context" 5 "encoding/base64" 6 "encoding/json" 7 "net/http" 8 "strconv" 9 "strings" 10 11 "github.com/docker/distribution/reference" 12 "github.com/docker/docker/api/server/httputils" 13 "github.com/docker/docker/api/types" 14 "github.com/docker/docker/api/types/filters" 15 "github.com/docker/docker/pkg/ioutils" 16 "github.com/docker/docker/pkg/streamformatter" 17 "github.com/pkg/errors" 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 if err := httputils.ReadJSON(r, &privileges); err != nil { 98 return err 99 } 100 101 metaHeaders, authConfig := parseHeaders(r.Header) 102 ref, tag, err := parseRemoteRef(r.FormValue("remote")) 103 if err != nil { 104 return err 105 } 106 107 name, err := getName(ref, tag, vars["name"]) 108 if err != nil { 109 return err 110 } 111 w.Header().Set("Docker-Plugin-Name", name) 112 113 w.Header().Set("Content-Type", "application/json") 114 output := ioutils.NewWriteFlusher(w) 115 116 if err := pr.backend.Upgrade(ctx, ref, name, metaHeaders, authConfig, privileges, output); err != nil { 117 if !output.Flushed() { 118 return err 119 } 120 _, _ = output.Write(streamformatter.FormatError(err)) 121 } 122 123 return nil 124 } 125 126 func (pr *pluginRouter) pullPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 127 if err := httputils.ParseForm(r); err != nil { 128 return errors.Wrap(err, "failed to parse form") 129 } 130 131 var privileges types.PluginPrivileges 132 if err := httputils.ReadJSON(r, &privileges); err != nil { 133 return err 134 } 135 136 metaHeaders, authConfig := parseHeaders(r.Header) 137 ref, tag, err := parseRemoteRef(r.FormValue("remote")) 138 if err != nil { 139 return err 140 } 141 142 name, err := getName(ref, tag, r.FormValue("name")) 143 if err != nil { 144 return err 145 } 146 w.Header().Set("Docker-Plugin-Name", name) 147 148 w.Header().Set("Content-Type", "application/json") 149 output := ioutils.NewWriteFlusher(w) 150 151 if err := pr.backend.Pull(ctx, ref, name, metaHeaders, authConfig, privileges, output); err != nil { 152 if !output.Flushed() { 153 return err 154 } 155 _, _ = output.Write(streamformatter.FormatError(err)) 156 } 157 158 return nil 159 } 160 161 func getName(ref reference.Named, tag, name string) (string, error) { 162 if name == "" { 163 if _, ok := ref.(reference.Canonical); ok { 164 trimmed := reference.TrimNamed(ref) 165 if tag != "" { 166 nt, err := reference.WithTag(trimmed, tag) 167 if err != nil { 168 return "", err 169 } 170 name = reference.FamiliarString(nt) 171 } else { 172 name = reference.FamiliarString(reference.TagNameOnly(trimmed)) 173 } 174 } else { 175 name = reference.FamiliarString(ref) 176 } 177 } else { 178 localRef, err := reference.ParseNormalizedNamed(name) 179 if err != nil { 180 return "", err 181 } 182 if _, ok := localRef.(reference.Canonical); ok { 183 return "", errors.New("cannot use digest in plugin tag") 184 } 185 if reference.IsNameOnly(localRef) { 186 // TODO: log change in name to out stream 187 name = reference.FamiliarString(reference.TagNameOnly(localRef)) 188 } 189 } 190 return name, nil 191 } 192 193 func (pr *pluginRouter) createPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 194 if err := httputils.ParseForm(r); err != nil { 195 return err 196 } 197 198 options := &types.PluginCreateOptions{ 199 RepoName: r.FormValue("name")} 200 201 if err := pr.backend.CreateFromContext(ctx, r.Body, options); err != nil { 202 return err 203 } 204 // TODO: send progress bar 205 w.WriteHeader(http.StatusNoContent) 206 return nil 207 } 208 209 func (pr *pluginRouter) enablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 210 if err := httputils.ParseForm(r); err != nil { 211 return err 212 } 213 214 name := vars["name"] 215 timeout, err := strconv.Atoi(r.Form.Get("timeout")) 216 if err != nil { 217 return err 218 } 219 config := &types.PluginEnableConfig{Timeout: timeout} 220 221 return pr.backend.Enable(name, config) 222 } 223 224 func (pr *pluginRouter) disablePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 225 if err := httputils.ParseForm(r); err != nil { 226 return err 227 } 228 229 name := vars["name"] 230 config := &types.PluginDisableConfig{ 231 ForceDisable: httputils.BoolValue(r, "force"), 232 } 233 234 return pr.backend.Disable(name, config) 235 } 236 237 func (pr *pluginRouter) removePlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 238 if err := httputils.ParseForm(r); err != nil { 239 return err 240 } 241 242 name := vars["name"] 243 config := &types.PluginRmConfig{ 244 ForceRemove: httputils.BoolValue(r, "force"), 245 } 246 return pr.backend.Remove(name, config) 247 } 248 249 func (pr *pluginRouter) pushPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 250 if err := httputils.ParseForm(r); err != nil { 251 return errors.Wrap(err, "failed to parse form") 252 } 253 254 metaHeaders, authConfig := parseHeaders(r.Header) 255 256 w.Header().Set("Content-Type", "application/json") 257 output := ioutils.NewWriteFlusher(w) 258 259 if err := pr.backend.Push(ctx, vars["name"], metaHeaders, authConfig, output); err != nil { 260 if !output.Flushed() { 261 return err 262 } 263 _, _ = output.Write(streamformatter.FormatError(err)) 264 } 265 return nil 266 } 267 268 func (pr *pluginRouter) setPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 269 var args []string 270 if err := httputils.ReadJSON(r, &args); err != nil { 271 return err 272 } 273 if err := pr.backend.Set(vars["name"], args); err != nil { 274 return err 275 } 276 w.WriteHeader(http.StatusNoContent) 277 return nil 278 } 279 280 func (pr *pluginRouter) listPlugins(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 281 if err := httputils.ParseForm(r); err != nil { 282 return err 283 } 284 285 pluginFilters, err := filters.FromJSON(r.Form.Get("filters")) 286 if err != nil { 287 return err 288 } 289 l, err := pr.backend.List(pluginFilters) 290 if err != nil { 291 return err 292 } 293 return httputils.WriteJSON(w, http.StatusOK, l) 294 } 295 296 func (pr *pluginRouter) inspectPlugin(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 297 result, err := pr.backend.Inspect(vars["name"]) 298 if err != nil { 299 return err 300 } 301 return httputils.WriteJSON(w, http.StatusOK, result) 302 }