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