dd93571c |
package image |
1a775800 |
import (
"encoding/base64"
"encoding/json"
"io"
"net/http" |
92f10fe2 |
"strconv" |
1a775800 |
"strings"
|
da982cf5 |
"github.com/docker/docker/api/server/httputils" |
7c36a1af |
"github.com/docker/docker/api/types" |
93e02efa |
"github.com/docker/docker/api/types/backend" |
7c36a1af |
"github.com/docker/docker/api/types/container" |
820b809e |
"github.com/docker/docker/api/types/filters" |
7c36a1af |
"github.com/docker/docker/api/types/versions" |
1a775800 |
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/streamformatter" |
92f10fe2 |
"github.com/docker/docker/registry" |
0380fbff |
specs "github.com/opencontainers/image-spec/specs-go/v1" |
ebcb7d6b |
"github.com/pkg/errors" |
94e3b0f4 |
"golang.org/x/net/context" |
1a775800 |
)
|
dd93571c |
func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
da982cf5 |
if err := httputils.ParseForm(r); err != nil { |
1a775800 |
return err
}
|
da982cf5 |
if err := httputils.CheckForJSON(r); err != nil { |
1a775800 |
return err
}
cname := r.Form.Get("container")
|
da982cf5 |
pause := httputils.BoolValue(r, "pause")
version := httputils.VersionFromContext(ctx) |
7534f172 |
if r.FormValue("pause") == "" && versions.GreaterThanOrEqualTo(version, "1.13") { |
1a775800 |
pause = true
}
|
f0d26e16 |
c, _, _, err := s.decoder.DecodeConfig(r.Body) |
1a775800 |
if err != nil && err != io.EOF { //Do not fail if body is empty.
return err
} |
2a2d1f57 |
if c == nil { |
7ac4232e |
c = &container.Config{} |
1a775800 |
}
|
93e02efa |
commitCfg := &backend.ContainerCommitConfig{
ContainerCommitConfig: types.ContainerCommitConfig{
Pause: pause,
Repo: r.Form.Get("repo"),
Tag: r.Form.Get("tag"),
Author: r.Form.Get("author"),
Comment: r.Form.Get("comment"),
Config: c,
MergeConfigs: true,
},
Changes: r.Form["changes"], |
2a2d1f57 |
}
|
90215065 |
imgID, err := s.backend.Commit(cname, commitCfg) |
1a775800 |
if err != nil {
return err
}
|
2f5f0af3 |
return httputils.WriteJSON(w, http.StatusCreated, &types.IDResponse{ID: imgID}) |
1a775800 |
}
// Creates an image from Pull or from Import |
dd93571c |
func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
da982cf5 |
if err := httputils.ParseForm(r); err != nil { |
1a775800 |
return err
}
var ( |
0380fbff |
image = r.Form.Get("fromImage")
repo = r.Form.Get("repo")
tag = r.Form.Get("tag")
message = r.Form.Get("message")
err error
output = ioutils.NewWriteFlusher(w)
platform = &specs.Platform{} |
1a775800 |
) |
ec2289b2 |
defer output.Close() |
1a775800 |
w.Header().Set("Content-Type", "application/json")
|
0380fbff |
platform, err = httputils.GetRequestedPlatform(ctx, r)
if err == nil {
if image != "" { //pull
metaHeaders := map[string][]string{}
for k, v := range r.Header {
if strings.HasPrefix(k, "X-Meta-") {
metaHeaders[k] = v
} |
4352da78 |
} |
47afe6bd |
|
0380fbff |
authEncoded := r.Header.Get("X-Registry-Auth")
authConfig := &types.AuthConfig{}
if authEncoded != "" {
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
// for a pull it is not an error if no auth was given
// to increase compatibility with the existing api it is defaulting to be empty
authConfig = &types.AuthConfig{}
} |
1a775800 |
} |
0380fbff |
err = s.backend.PullImage(ctx, image, tag, platform.OS, metaHeaders, authConfig, output)
} else { //import
src := r.Form.Get("fromSrc")
// 'err' MUST NOT be defined within this block, we need any error
// generated from the download to be available to the output
// stream processing below
err = s.backend.ImportImage(src, repo, platform.OS, tag, message, r.Body, output, r.Form["changes"]) |
1a775800 |
}
}
if err != nil {
if !output.Flushed() {
return err
} |
c87d67b0 |
output.Write(streamformatter.FormatError(err)) |
1a775800 |
}
return nil
}
|
ebcb7d6b |
type validationError struct {
cause error
}
func (e validationError) Error() string {
return e.cause.Error()
}
func (e validationError) Cause() error {
return e.cause
}
func (validationError) InvalidParameter() {}
|
dd93571c |
func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
1a775800 |
metaHeaders := map[string][]string{}
for k, v := range r.Header {
if strings.HasPrefix(k, "X-Meta-") {
metaHeaders[k] = v
}
} |
da982cf5 |
if err := httputils.ParseForm(r); err != nil { |
1a775800 |
return err
} |
5b321e32 |
authConfig := &types.AuthConfig{} |
1a775800 |
authEncoded := r.Header.Get("X-Registry-Auth")
if authEncoded != "" {
// the new format is to handle the authConfig as a header
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
// to increase compatibility to existing api it is defaulting to be empty |
5b321e32 |
authConfig = &types.AuthConfig{} |
1a775800 |
}
} else {
// the old format is supported for compatibility if there was no authConfig header
if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { |
ebcb7d6b |
return errors.Wrap(validationError{err}, "Bad parameters and missing X-Registry-Auth") |
1a775800 |
}
}
|
47afe6bd |
image := vars["name"] |
4352da78 |
tag := r.Form.Get("tag")
|
1a775800 |
output := ioutils.NewWriteFlusher(w) |
ec2289b2 |
defer output.Close() |
1a775800 |
w.Header().Set("Content-Type", "application/json")
|
47afe6bd |
if err := s.backend.PushImage(ctx, image, tag, metaHeaders, authConfig, output); err != nil { |
1a775800 |
if !output.Flushed() {
return err
} |
c87d67b0 |
output.Write(streamformatter.FormatError(err)) |
1a775800 |
}
return nil
}
|
dd93571c |
func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
da982cf5 |
if err := httputils.ParseForm(r); err != nil { |
1a775800 |
return err
}
w.Header().Set("Content-Type", "application/x-tar")
output := ioutils.NewWriteFlusher(w) |
ec2289b2 |
defer output.Close() |
1a775800 |
var names []string
if name, ok := vars["name"]; ok {
names = []string{name}
} else {
names = r.Form["names"]
}
|
90215065 |
if err := s.backend.ExportImage(names, output); err != nil { |
1a775800 |
if !output.Flushed() {
return err
} |
c87d67b0 |
output.Write(streamformatter.FormatError(err)) |
1a775800 |
}
return nil
}
|
06d8f504 |
func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
fae09e25 |
if err := httputils.ParseForm(r); err != nil {
return err
}
quiet := httputils.BoolValueOrDefault(r, "quiet", true) |
96d7db66 |
|
2f27632c |
w.Header().Set("Content-Type", "application/json") |
96d7db66 |
|
2f27632c |
output := ioutils.NewWriteFlusher(w)
defer output.Close()
if err := s.backend.LoadImage(r.Body, output, quiet); err != nil { |
c87d67b0 |
output.Write(streamformatter.FormatError(err)) |
96d7db66 |
} |
2f27632c |
return nil |
1a775800 |
}
|
ebcb7d6b |
type missingImageError struct{}
func (missingImageError) Error() string {
return "image name cannot be blank"
}
func (missingImageError) InvalidParameter() {}
|
dd93571c |
func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
da982cf5 |
if err := httputils.ParseForm(r); err != nil { |
1a775800 |
return err
}
name := vars["name"] |
111d2f34 |
|
d4a8d09d |
if strings.TrimSpace(name) == "" { |
ebcb7d6b |
return missingImageError{} |
111d2f34 |
}
|
da982cf5 |
force := httputils.BoolValue(r, "force")
prune := !httputils.BoolValue(r, "noprune") |
1a775800 |
|
90215065 |
list, err := s.backend.ImageDelete(name, force, prune) |
1a775800 |
if err != nil {
return err
}
|
da982cf5 |
return httputils.WriteJSON(w, http.StatusOK, list) |
1a775800 |
}
|
dd93571c |
func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
90215065 |
imageInspect, err := s.backend.LookupImage(vars["name"]) |
1a775800 |
if err != nil {
return err
}
|
da982cf5 |
return httputils.WriteJSON(w, http.StatusOK, imageInspect) |
1a775800 |
}
|
dd93571c |
func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
da982cf5 |
if err := httputils.ParseForm(r); err != nil { |
1a775800 |
return err
}
|
a4efe66c |
imageFilters, err := filters.FromJSON(r.Form.Get("filters")) |
820b809e |
if err != nil {
return err
}
filterParam := r.Form.Get("filter") |
0f9d22cd |
// FIXME(vdemeester) This has been deprecated in 1.13, and is target for removal for v17.12
if filterParam != "" { |
820b809e |
imageFilters.Add("reference", filterParam)
}
images, err := s.backend.Images(imageFilters, httputils.BoolValue(r, "all"), false) |
1a775800 |
if err != nil {
return err
}
|
da982cf5 |
return httputils.WriteJSON(w, http.StatusOK, images) |
1a775800 |
}
|
dd93571c |
func (s *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
1a775800 |
name := vars["name"] |
90215065 |
history, err := s.backend.ImageHistory(name) |
1a775800 |
if err != nil {
return err
}
|
da982cf5 |
return httputils.WriteJSON(w, http.StatusOK, history) |
1a775800 |
}
|
dd93571c |
func (s *imageRouter) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
da982cf5 |
if err := httputils.ParseForm(r); err != nil { |
1a775800 |
return err
} |
47afe6bd |
if err := s.backend.TagImage(vars["name"], r.Form.Get("repo"), r.Form.Get("tag")); err != nil { |
1a775800 |
return err
}
w.WriteHeader(http.StatusCreated)
return nil
}
|
dd93571c |
func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
da982cf5 |
if err := httputils.ParseForm(r); err != nil { |
1a775800 |
return err
}
var ( |
5b321e32 |
config *types.AuthConfig |
1a775800 |
authEncoded = r.Header.Get("X-Registry-Auth")
headers = map[string][]string{}
)
if authEncoded != "" {
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
// for a search it is not an error if no auth was given
// to increase compatibility with the existing api it is defaulting to be empty |
5b321e32 |
config = &types.AuthConfig{} |
1a775800 |
}
}
for k, v := range r.Header {
if strings.HasPrefix(k, "X-Meta-") {
headers[k] = v
}
} |
92f10fe2 |
limit := registry.DefaultSearchLimit
if r.Form.Get("limit") != "" {
limitValue, err := strconv.Atoi(r.Form.Get("limit"))
if err != nil {
return err
}
limit = limitValue
}
query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), limit, config, headers) |
1a775800 |
if err != nil {
return err
} |
da982cf5 |
return httputils.WriteJSON(w, http.StatusOK, query.Results) |
1a775800 |
} |
33f4d68f |
func (s *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := httputils.ParseForm(r); err != nil {
return err
}
|
a4efe66c |
pruneFilters, err := filters.FromJSON(r.Form.Get("filters")) |
a6be56b5 |
if err != nil { |
33f4d68f |
return err
}
|
0dee6979 |
pruneReport, err := s.backend.ImagesPrune(ctx, pruneFilters) |
33f4d68f |
if err != nil {
return err
}
return httputils.WriteJSON(w, http.StatusOK, pruneReport)
} |