diff options
Diffstat (limited to 'pkg/api/handlers')
-rw-r--r-- | pkg/api/handlers/compat/containers_create.go | 5 | ||||
-rw-r--r-- | pkg/api/handlers/compat/containers_stats.go | 30 | ||||
-rw-r--r-- | pkg/api/handlers/compat/images_push.go | 112 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/images.go | 2 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/images_pull.go | 2 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/manifests.go | 2 | ||||
-rw-r--r-- | pkg/api/handlers/utils/images.go | 25 |
7 files changed, 85 insertions, 93 deletions
diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index 8e9e1fb39..0b5cbd343 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -71,13 +71,12 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { imgNameOrID := newImage.ID() // if the img had multi names with the same sha256 ID, should use the InputName, not the ID if len(newImage.Names()) > 1 { - imageRef, err := utils.ParseDockerReference(resolvedName) - if err != nil { + if err := utils.IsRegistryReference(resolvedName); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } // maybe the InputName has no tag, so use full name to display - imgNameOrID = imageRef.DockerReference().String() + imgNameOrID = resolvedName } sg := specgen.NewSpecGenerator(imgNameOrID, cliOpts.RootFS) diff --git a/pkg/api/handlers/compat/containers_stats.go b/pkg/api/handlers/compat/containers_stats.go index 694b57bb1..851955207 100644 --- a/pkg/api/handlers/compat/containers_stats.go +++ b/pkg/api/handlers/compat/containers_stats.go @@ -22,7 +22,8 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { - Stream bool `schema:"stream"` + Stream bool `schema:"stream"` + OneShot bool `schema:"one-shot"` //added schema for one shot }{ Stream: true, } @@ -30,6 +31,10 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } + if query.Stream && query.OneShot { // mismatch. one-shot can only be passed with stream=false + utils.Error(w, "invalid combination of stream and one-shot", http.StatusBadRequest, define.ErrInvalidArg) + return + } name := utils.GetName(r) ctnr, err := runtime.LookupContainer(name) @@ -56,6 +61,16 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { return } + coder := json.NewEncoder(w) + // Write header and content type. + w.WriteHeader(http.StatusOK) + w.Header().Add("Content-Type", "application/json") + if flusher, ok := w.(http.Flusher); ok { + flusher.Flush() + } + + // Setup JSON encoder for streaming. + coder.SetEscapeHTML(true) var preRead time.Time var preCPUStats CPUStats if query.Stream { @@ -75,17 +90,6 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { } } - // Write header and content type. - w.WriteHeader(http.StatusOK) - w.Header().Add("Content-Type", "application/json") - if flusher, ok := w.(http.Flusher); ok { - flusher.Flush() - } - - // Setup JSON encoder for streaming. - coder := json.NewEncoder(w) - coder.SetEscapeHTML(true) - streamLabel: // A label to flatten the scope select { case <-r.Context().Done(): @@ -199,7 +203,7 @@ streamLabel: // A label to flatten the scope flusher.Flush() } - if !query.Stream { + if !query.Stream || query.OneShot { return } diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go index db02af445..62f8cdc77 100644 --- a/pkg/api/handlers/compat/images_push.go +++ b/pkg/api/handlers/compat/images_push.go @@ -1,7 +1,6 @@ package compat import ( - "context" "encoding/json" "fmt" "io/ioutil" @@ -12,7 +11,6 @@ import ( "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" "github.com/containers/podman/v3/pkg/auth" - "github.com/containers/podman/v3/pkg/channel" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/containers/storage" @@ -101,46 +99,33 @@ func PushImage(w http.ResponseWriter, r *http.Request) { destination = imageName } - errorWriter := channel.NewWriter(make(chan []byte)) - defer errorWriter.Close() - - statusWriter := channel.NewWriter(make(chan []byte)) - defer statusWriter.Close() - - runCtx, cancel := context.WithCancel(context.Background()) - var failed bool - - go func() { - defer cancel() - - statusWriter.Write([]byte(fmt.Sprintf("The push refers to repository [%s]", imageName))) - - err := imageEngine.Push(runCtx, imageName, destination, options) - if err != nil { - if errors.Cause(err) != storage.ErrImageUnknown { - errorWriter.Write([]byte("An image does not exist locally with the tag: " + imageName)) - } else { - errorWriter.Write([]byte(err.Error())) - } - } - }() - - flush := func() { - if flusher, ok := w.(http.Flusher); ok { - flusher.Flush() - } + flush := func() {} + if flusher, ok := w.(http.Flusher); ok { + flush = flusher.Flush } w.WriteHeader(http.StatusOK) w.Header().Add("Content-Type", "application/json") flush() + var report jsonmessage.JSONMessage enc := json.NewEncoder(w) enc.SetEscapeHTML(true) + report.Status = fmt.Sprintf("The push refers to repository [%s]", imageName) + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to json encode error %q", err.Error()) + } + flush() + + pushErrChan := make(chan error) + go func() { + pushErrChan <- imageEngine.Push(r.Context(), imageName, destination, options) + }() + loop: // break out of for/select infinite loop for { - var report jsonmessage.JSONMessage + report = jsonmessage.JSONMessage{} select { case e := <-options.Progress: @@ -160,43 +145,50 @@ loop: // break out of for/select infinite loop } report.ID = e.Artifact.Digest.Encoded()[0:12] if err := enc.Encode(report); err != nil { - errorWriter.Write([]byte(err.Error())) + logrus.Warnf("Failed to json encode error %q", err.Error()) } flush() - case e := <-statusWriter.Chan(): - report.Status = string(e) - if err := enc.Encode(report); err != nil { - errorWriter.Write([]byte(err.Error())) + case err := <-pushErrChan: + if err != nil { + var msg string + if errors.Cause(err) != storage.ErrImageUnknown { + msg = "An image does not exist locally with the tag: " + imageName + } else { + msg = err.Error() + } + report.Error = &jsonmessage.JSONError{ + Message: msg, + } + report.ErrorMessage = msg + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to json encode error %q", err.Error()) + } + flush() + break loop } - flush() - case e := <-errorWriter.Chan(): - failed = true - report.Error = &jsonmessage.JSONError{ - Message: string(e), + + digestBytes, err := ioutil.ReadAll(digestFile) + if err != nil { + report.Error = &jsonmessage.JSONError{ + Message: err.Error(), + } + report.ErrorMessage = err.Error() + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to json encode error %q", err.Error()) + } + flush() + break loop } - report.ErrorMessage = string(e) + tag := query.Tag + if tag == "" { + tag = "latest" + } + report.Status = fmt.Sprintf("%s: digest: %s", tag, string(digestBytes)) if err := enc.Encode(report); err != nil { logrus.Warnf("Failed to json encode error %q", err.Error()) } + flush() - case <-runCtx.Done(): - if !failed { - digestBytes, err := ioutil.ReadAll(digestFile) - if err == nil { - tag := query.Tag - if tag == "" { - tag = "latest" - } - report.Status = fmt.Sprintf("%s: digest: %s", tag, string(digestBytes)) - if err := enc.Encode(report); err != nil { - logrus.Warnf("Failed to json encode error %q", err.Error()) - } - flush() - } - } - break loop // break out of for/select infinite loop - case <-r.Context().Done(): - // Client has closed connection break loop // break out of for/select infinite loop } } diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index a90408bfd..fc6ab4b4c 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -482,7 +482,7 @@ func PushImage(w http.ResponseWriter, r *http.Request) { destination = source } - if _, err := utils.ParseDockerReference(destination); err != nil { + if err := utils.IsRegistryReference(destination); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/libpod/images_pull.go b/pkg/api/handlers/libpod/images_pull.go index 7545ba235..fe56aa31d 100644 --- a/pkg/api/handlers/libpod/images_pull.go +++ b/pkg/api/handlers/libpod/images_pull.go @@ -48,7 +48,7 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { } // Make sure that the reference has no transport or the docker one. - if _, err := utils.ParseDockerReference(query.Reference); err != nil { + if err := utils.IsRegistryReference(query.Reference); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index f21eb2e80..2f36db583 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -169,7 +169,7 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) { errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } - if _, err := utils.ParseDockerReference(query.Destination); err != nil { + if err := utils.IsRegistryReference(query.Destination); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } diff --git a/pkg/api/handlers/utils/images.go b/pkg/api/handlers/utils/images.go index 2662cd368..2a1908d63 100644 --- a/pkg/api/handlers/utils/images.go +++ b/pkg/api/handlers/utils/images.go @@ -15,22 +15,19 @@ import ( "github.com/pkg/errors" ) -// ParseDockerReference parses the specified image name to a -// `types.ImageReference` and enforces it to refer to a docker-transport -// reference. -func ParseDockerReference(name string) (types.ImageReference, error) { - dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name()) +// IsRegistryReference checks if the specified name points to the "docker://" +// transport. If it points to no supported transport, we'll assume a +// non-transport reference pointing to an image (e.g., "fedora:latest"). +func IsRegistryReference(name string) error { imageRef, err := alltransports.ParseImageName(name) - if err == nil && imageRef.Transport().Name() != docker.Transport.Name() { - return nil, errors.Errorf("reference %q must be a docker reference", name) - } else if err != nil { - origErr := err - imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, name)) - if err != nil { - return nil, errors.Wrapf(origErr, "reference %q must be a docker reference", name) - } + if err != nil { + // No supported transport -> assume a docker-stype reference. + return nil } - return imageRef, nil + if imageRef.Transport().Name() == docker.Transport.Name() { + return nil + } + return errors.Errorf("unsupport transport %s in %q: only docker transport is supported", imageRef.Transport().Name(), name) } // ParseStorageReference parses the specified image name to a |