summaryrefslogtreecommitdiff
path: root/pkg/api/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/api/handlers')
-rw-r--r--pkg/api/handlers/containers.go8
-rw-r--r--pkg/api/handlers/libpod/images.go209
-rw-r--r--pkg/api/handlers/libpod/pods.go14
-rw-r--r--pkg/api/handlers/types.go27
-rw-r--r--pkg/api/handlers/utils/handler.go8
5 files changed, 234 insertions, 32 deletions
diff --git a/pkg/api/handlers/containers.go b/pkg/api/handlers/containers.go
index f180fbc2b..d7d040ce2 100644
--- a/pkg/api/handlers/containers.go
+++ b/pkg/api/handlers/containers.go
@@ -41,10 +41,9 @@ func StopContainer(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, errors.Wrapf(err, "unable to get state for Container %s", name))
return
}
- // If the Container is stopped already, send a 302
+ // If the Container is stopped already, send a 304
if state == define.ContainerStateStopped || state == define.ContainerStateExited {
- utils.Error(w, http.StatusText(http.StatusNotModified), http.StatusNotModified,
- errors.Errorf("Container %s is already stopped ", name))
+ utils.WriteResponse(w, http.StatusNotModified, "")
return
}
@@ -134,8 +133,7 @@ func StartContainer(w http.ResponseWriter, r *http.Request) {
return
}
if state == define.ContainerStateRunning {
- msg := fmt.Sprintf("Container %s is already running", name)
- utils.Error(w, msg, http.StatusNotModified, errors.New(msg))
+ utils.WriteResponse(w, http.StatusNotModified, "")
return
}
if err := con.Start(r.Context(), false); err != nil {
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 6c926c45b..f6459f1eb 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -1,15 +1,23 @@
package libpod
import (
+ "context"
"fmt"
+ "io"
"io/ioutil"
"net/http"
"os"
"strconv"
+ "github.com/containers/image/v5/docker"
+ "github.com/containers/image/v5/docker/reference"
+ "github.com/containers/image/v5/transports/alltransports"
+ "github.com/containers/image/v5/types"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/util"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
@@ -176,16 +184,205 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
}
func ImagesLoad(w http.ResponseWriter, r *http.Request) {
- //TODO ...
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.New("/libpod/images/load is not yet implemented"))
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Reference string `schema:"reference"`
+ }{
+ // Add defaults here once needed.
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ tmpfile, err := ioutil.TempFile("", "libpod-images-load.tar")
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
+ return
+ }
+ defer os.Remove(tmpfile.Name())
+ defer tmpfile.Close()
+
+ if _, err := io.Copy(tmpfile, r.Body); err != nil && err != io.EOF {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to write archive to temporary file"))
+ return
+ }
+
+ tmpfile.Close()
+ loadedImage, err := runtime.LoadImage(context.Background(), query.Reference, tmpfile.Name(), os.Stderr, "")
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to load image"))
+ return
+ }
+
+ utils.WriteResponse(w, http.StatusOK, []handlers.LibpodImagesLoadReport{{ID: loadedImage}})
}
func ImagesImport(w http.ResponseWriter, r *http.Request) {
- //TODO ...
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.New("/libpod/images/import is not yet implemented"))
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Changes []string `schema:"changes"`
+ Message string `schema:"message"`
+ Reference string `schema:"reference"`
+ URL string `schema:"URL"`
+ }{
+ // Add defaults here once needed.
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ // Check if we need to load the image from a URL or from the request's body.
+ source := query.URL
+ if len(query.URL) == 0 {
+ tmpfile, err := ioutil.TempFile("", "libpod-images-import.tar")
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
+ return
+ }
+ defer os.Remove(tmpfile.Name())
+ defer tmpfile.Close()
+
+ if _, err := io.Copy(tmpfile, r.Body); err != nil && err != io.EOF {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to write archive to temporary file"))
+ return
+ }
+
+ tmpfile.Close()
+ source = tmpfile.Name()
+ }
+
+ importedImage, err := runtime.Import(context.Background(), source, query.Reference, query.Changes, query.Message, true)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to import image"))
+ return
+ }
+
+ utils.WriteResponse(w, http.StatusOK, handlers.LibpodImagesImportReport{ID: importedImage})
}
func ImagesPull(w http.ResponseWriter, r *http.Request) {
- //TODO ...
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.New("/libpod/images/pull is not yet implemented"))
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Reference string `schema:"reference"`
+ Credentials string `schema:"credentials"`
+ OverrideOS string `schema:"overrideOS"`
+ OverrideArch string `schema:"overrideArch"`
+ TLSVerify bool `schema:"tlsVerify"`
+ AllTags bool `schema:"allTags"`
+ }{
+ TLSVerify: true,
+ }
+
+ if err := decoder.Decode(&query, r.URL.Query()); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
+ return
+ }
+
+ if len(query.Reference) == 0 {
+ utils.InternalServerError(w, errors.New("reference parameter cannot be empty"))
+ return
+ }
+ // Enforce the docker transport. This is just a precaution as some callers
+ // might accustomed to using the "transport:reference" notation. Using
+ // another than the "docker://" transport does not really make sense for a
+ // remote case. For loading tarballs, the load and import endpoints should
+ // be used.
+ imageRef, err := alltransports.ParseImageName(query.Reference)
+ if err == nil && imageRef.Transport().Name() != docker.Transport.Name() {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Errorf("reference %q must be a docker reference", query.Reference))
+ return
+ } else if err != nil {
+ origErr := err
+ imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s:%s", docker.Transport.Name(), query.Reference))
+ if err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(origErr, "reference %q must be a docker reference", query.Reference))
+ return
+ }
+ }
+
+ // all-tags doesn't work with a tagged reference, so let's check early
+ namedRef, err := reference.Parse(query.Reference)
+ if err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "error parsing reference %q", query.Reference))
+ return
+ }
+ if _, isTagged := namedRef.(reference.Tagged); isTagged && query.AllTags {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Errorf("reference %q must not have a tag for all-tags", query.Reference))
+ return
+ }
+
+ var registryCreds *types.DockerAuthConfig
+ if len(query.Credentials) != 0 {
+ creds, err := util.ParseRegistryCreds(query.Credentials)
+ if err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Wrapf(err, "error parsing credentials %q", query.Credentials))
+ return
+ }
+ registryCreds = creds
+ }
+
+ // Setup the registry options
+ dockerRegistryOptions := image.DockerRegistryOptions{
+ DockerRegistryCreds: registryCreds,
+ OSChoice: query.OverrideOS,
+ ArchitectureChoice: query.OverrideArch,
+ }
+ if query.TLSVerify {
+ dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
+ }
+
+ // Prepare the images we want to pull
+ imagesToPull := []string{}
+ res := []handlers.LibpodImagesPullReport{}
+ imageName := namedRef.String()
+
+ if !query.AllTags {
+ imagesToPull = append(imagesToPull, imageName)
+ } else {
+ systemContext := image.GetSystemContext("", "", false)
+ tags, err := docker.GetRepositoryTags(context.Background(), systemContext, imageRef)
+ if err != nil {
+ utils.InternalServerError(w, errors.Wrap(err, "error getting repository tags"))
+ return
+ }
+ for _, tag := range tags {
+ imagesToPull = append(imagesToPull, fmt.Sprintf("%s:%s", imageName, tag))
+ }
+ }
+
+ // Finally pull the images
+ for _, img := range imagesToPull {
+ newImage, err := runtime.ImageRuntime().New(
+ context.Background(),
+ img,
+ "",
+ "",
+ os.Stderr,
+ &dockerRegistryOptions,
+ image.SigningOptions{},
+ nil,
+ util.PullImageAlways)
+ if err != nil {
+ utils.InternalServerError(w, errors.Wrapf(err, "error pulling image %q", query.Reference))
+ return
+ }
+ res = append(res, handlers.LibpodImagesPullReport{ID: newImage.ID()})
+ }
+
+ utils.WriteResponse(w, http.StatusOK, res)
}
diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go
index 091368985..e9297d91b 100644
--- a/pkg/api/handlers/libpod/pods.go
+++ b/pkg/api/handlers/libpod/pods.go
@@ -91,7 +91,11 @@ func PodCreate(w http.ResponseWriter, r *http.Request) {
pod, err := runtime.NewPod(r.Context(), options...)
if err != nil {
- utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
+ http_code := http.StatusInternalServerError
+ if errors.Cause(err) == define.ErrPodExists {
+ http_code = http.StatusConflict
+ }
+ utils.Error(w, "Something went wrong.", http_code, err)
return
}
utils.WriteResponse(w, http.StatusCreated, handlers.IDResponse{ID: pod.CgroupParent()})
@@ -198,8 +202,7 @@ func PodStop(w http.ResponseWriter, r *http.Request) {
}
}
if allContainersStopped {
- alreadyStopped := errors.Errorf("pod %s is already stopped", pod.ID())
- utils.Error(w, "Something went wrong", http.StatusNotModified, alreadyStopped)
+ utils.WriteResponse(w, http.StatusNotModified, "")
return
}
@@ -245,8 +248,7 @@ func PodStart(w http.ResponseWriter, r *http.Request) {
}
}
if allContainersRunning {
- alreadyRunning := errors.Errorf("pod %s is already running", pod.ID())
- utils.Error(w, "Something went wrong", http.StatusNotModified, alreadyRunning)
+ utils.WriteResponse(w, http.StatusNotModified, "")
return
}
if _, err := pod.Start(r.Context()); err != nil {
@@ -409,5 +411,5 @@ func PodExists(w http.ResponseWriter, r *http.Request) {
utils.PodNotFound(w, name, err)
return
}
- utils.WriteResponse(w, http.StatusOK, "")
+ utils.WriteResponse(w, http.StatusNoContent, "")
}
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index 6169adb18..f5d9f9ad5 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -33,6 +33,18 @@ type ContainerConfig struct {
dockerContainer.Config
}
+type LibpodImagesLoadReport struct {
+ ID string `json:"id"`
+}
+
+type LibpodImagesImportReport struct {
+ ID string `json:"id"`
+}
+
+type LibpodImagesPullReport struct {
+ ID string `json:"id"`
+}
+
type ImageSummary struct {
docker.ImageSummary
CreatedTime time.Time `json:"CreatedTime,omitempty"`
@@ -49,21 +61,6 @@ type LibpodContainersPruneReport struct {
PruneError string `json:"error"`
}
-type LibpodImagesLoadReport struct {
- ID string `json:"id"`
- RepoTags []string `json:"repoTags"`
-}
-
-type LibpodImagesImportReport struct {
- ID string `json:"id"`
- RepoTags []string `json:"repoTags"`
-}
-
-type LibpodImagesPullReport struct {
- ID string `json:"id"`
- RepoTags []string `json:"repoTags"`
-}
-
type Info struct {
docker.Info
BuildahVersion string
diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go
index 970f93791..44bcc794c 100644
--- a/pkg/api/handlers/utils/handler.go
+++ b/pkg/api/handlers/utils/handler.go
@@ -23,6 +23,14 @@ func IsLibpodRequest(r *http.Request) bool {
// WriteResponse encodes the given value as JSON or string and renders it for http client
func WriteResponse(w http.ResponseWriter, code int, value interface{}) {
+ // RFC2616 explicitly states that the following status codes "MUST NOT
+ // include a message-body":
+ switch code {
+ case http.StatusNoContent, http.StatusNotModified: // 204, 304
+ w.WriteHeader(code)
+ return
+ }
+
switch v := value.(type) {
case string:
w.Header().Set("Content-Type", "text/plain; charset=us-ascii")